#include <bits/stdc++.h>
using namespace std;
const int N = 505;
int n, tail;
long long ans[N][N];
set<long long> used;
int pri[N * N];
bool isNot[N * N];
void Sieve() {
  for (int i = 2; i <= n * n; ++i) {
    if (!isNot[i]) pri[++tail] = i;
    for (int j = 1; j <= tail && pri[j] * i <= n * n; ++j) {
      isNot[pri[j] * i] = 1;
      if (i % pri[j] == 0) continue;
    }
  }
}
long long gcd(long long x, long long y) { return (!x) ? (y) : gcd(y % x, x); }
long long lcm(long long x, long long y) {
  return (!x || !y) ? (x | y) : (x * y / gcd(x, y));
}
void Fill(int i, int j) {
  long long tmp = min(min(n - i + 1, n - j + 1), min(i, j)) + 1;
  ans[i][j] = tmp;
  while (used.count(ans[i][j])) ans[i][j] += tmp;
  used.insert(ans[i][j]);
}
struct Node {
  int r, c;
  int Cal() const { return min(min(n - r + 1, n - c + 1), min(r, c)); }
  bool operator<(const Node &d) const { return Cal() > d.Cal(); }
} todo[N * N];
int main() {
  scanf("%d", &n);
  Sieve();
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= n; ++j)
      if ((i & 1) == (j & 1)) {
        todo[++tail] = (Node){i, j};
      }
  sort(todo + 1, todo + tail + 1);
  for (int i = 1; i <= tail; ++i) Fill(todo[i].r, todo[i].c);
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= n; ++j) {
      if ((i & 1) != (j & 1)) {
        long long tmp = lcm(lcm(ans[i - 1][j], ans[i + 1][j]),
                            lcm(ans[i][j - 1], ans[i][j + 1]));
        ans[i][j] = tmp + 1;
        int cnt = 0;
        while (used.count(ans[i][j])) {
          ans[i][j] += tmp;
          ++cnt;
        }
        used.insert(ans[i][j]);
      }
    }
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
      printf("%lld ", ans[i][j]);
      if (ans[i][j] > 1e15) {
        printf("________________________\n");
        return 0;
      }
    }
    printf("\n");
  }
  return 0;
}
