#ifndef LOCAL
#pragma GCC optimize("O3")
#endif
#include "bits/stdc++.h"
using namespace std;
#define sim template < class c
#define ris return * this
#define dor > debug & operator <<
#define eni(x) sim > typename \
  enable_if<sizeof dud<c>(0) x 1, debug&>::type operator<<(c i) {
sim > struct rge { c b, e; };
sim > rge<c> range(c i, c j) { return rge<c>{i, j}; }
sim > auto dud(c* x) -> decltype(cerr << *x, 0);
sim > char dud(...);
struct debug {
#ifdef LOCAL
~debug() { cerr << endl; }
eni(!=) cerr << boolalpha << i; ris; }
eni(==) ris << range(begin(i), end(i)); }
sim, class b dor(pair < b, c > d) {
  ris << "(" << d.first << ", " << d.second << ")";
}
sim dor(rge<c> d) {
  *this << "[";
  for (auto it = d.b; it != d.e; ++it)
    *this << ", " + 2 * (it == d.b) << *it;
  ris << "]";
}
#else
sim dor(const c&) { ris; }
#endif
};
#define imie(...) " [" << #__VA_ARGS__ ": " << (__VA_ARGS__) << "] "

typedef long long ll;

//~ #warning bigger nax
const int nax = 1e6 + 5;
bool is_prime[nax];

const int dx[4] = {-1, 1, 0, 0};
const int dy[4] = {0, 0, -1, 1};

int main() {
	for(int i = 2; i < nax; ++i) {
		is_prime[i] = true;
	}
	for(int i = 2; i < nax; ++i) {
		if(is_prime[i]) {
			for(int j = 2 * i; j < nax; j += i) {
				is_prime[j] = false;
			}
		}
	}
	vector<int> primes;
	for(int i = 2; i < nax; ++i) {
		if(is_prime[i]) {
			primes.push_back(i);
		}
	}
	
	int n;
	scanf("%d", &n);
	vector<vector<ll>> grid(n, vector<ll>(n));
	int nxt = 5;
	vector<int> odd;
	for(int i = 5; i <= 62751 + 20; i += 2) {
		odd.push_back(i);
	}
	//~ random_shuffle(odd.begin(), odd.end());
	int z = 0;
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			if((i + j) % 2) {
				continue;
			}
			const int M = 4;
			if(i % M == 0 || j == 0) {
				assert(!odd.empty());
				if(i % (2 * M) == 0) {
					grid[i][j] = odd.back();
					odd.pop_back();
				}
				else {
					grid[i][j] = odd[z++];
				}
				continue;
				grid[i][j] = nxt; //primes[nxt++];
				nxt += 2;
			}
			else {
				grid[i][j] = grid[i-1][j-1] * 2;
				//~ int dist = min(j, i - i / M * M);
				//~ grid[i][j] = grid[i-dist][j-dist] * (dist + 1);
			}
		}
	}
	auto inside = [&] (int a, int b) {
		return 0 <= a && 0 <= b && a < n && b < n;
	};
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			if(grid[i][j]) {
				continue;
			}
			grid[i][j] = 1;
			for(int dir = 0; dir < 4; ++dir) {
				int i2 = i + dx[dir];
				int j2 = j + dy[dir];
				if(inside(i2, j2)) {
					grid[i][j] = grid[i][j] / __gcd(grid[i][j], grid[i2][j2]) * grid[i2][j2];
				}
			}
			if(0)for(int rep = 0; rep < 4; ++rep) {
				for(int dir = 0; dir < 4; ++dir) {
					int i2 = i + dx[dir];
					int j2 = j + dy[dir];
					if(inside(i2, j2) && grid[i][j] == grid[i2][j2]) {
						grid[i][j] *= 64;
					}
				}
			}
		}
	}
	set<ll> secik;
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			if((i + j) % 2 == 0) {
				assert(!secik.count(grid[i][j]));
				secik.insert(grid[i][j]);
			}
		}
	}
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			while((i + j) % 2 == 1 && secik.count(grid[i][j] + 1)) {
				grid[i][j] *= 2;
			}
			printf("%lld ", grid[i][j] + (i + j) % 2);
			//~ fflush(stdout);
			if((i + j) % 2 == 1) {
				assert(!secik.count(grid[i][j] + 1));
				secik.insert(grid[i][j] + 1);
			}
		}
		puts("");
		//~ debug() << grid[i];
	}
	assert((int) secik.size() == n * n);
	ll big = 0;
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			big = max(big, grid[i][j]);
		}
	}
	cerr << "big = " << big << endl;
	debug() << imie(big) imie(big / (ll) 1e12) imie(nxt) imie(primes.size());
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			if(j != n - 1) {
				assert(max(grid[i][j], grid[i][j+1]) % min(grid[i][j], grid[i][j+1]) == 0);
			}
			if(i != n - 1) {
				assert(max(grid[i][j], grid[i+1][j]) % min(grid[i][j], grid[i+1][j]) == 0);
			}
		}
	}
}


Measured memory_bytes: 17656