//const int N = 5e5+5;
//bool prime[N];
//ll gcd(ll x, ll y){
//    if(y == 0)
//        return x;
//    return gcd(y,x%y);
//}
//void alternate(vi &arr){
//    vi ans;
//    for(int i = 0; i < 250; ++i){
//        ans.pb(arr[i]);
//        ans.pb(arr[499-i]);
//    }
//    arr = ans;
//}
//void _(){
//    fill(prime+2,prime+N,true);
//    vector<int> primes;
//    for(int i = 2; i < N; ++i)
//        if(prime[i]){
//            for(int j = 2*i; j < N; j += i)
//                prime[j] = false;
//            primes.push_back(i);
//        }
//    primes.resize(1000);
//    vi p1;
//    vi p2;
//    for(int i = 0; i < 1000; ++i)
//        if(i%2) 
//            p1.pb(primes[i]);
//        else
//            p2.pb(primes[i]);
//    alternate(p1);
//    alternate(p2);
//    //print(p1);
//    //print(p2);
//    int n;
//    cin >> n;
//    if(n == 2){
//        cout << "4 7 \n23 10\n";
//        return;
//    }
//    vector<ll> arr;
//    vvl grid(n,vl(n));
//    for(int i = 0; i < n; ++i)
//        for(int j = 0; j < n; ++j){
//            if(i % 2 == j % 2){
//                int d1 = (i+j)/2;
//                int d2 = (i-j+n-1)/2;
//                ll a = p1[d1];
//                ll b = p2[d2];
//                grid[i][j] = a*b;
//            }
//        }
//    for(int i = 0; i < n; ++i)
//        for(int j = 0; j < n; ++j){
//            if(i % 2 != j % 2){
//                vl neigh;
//                if(i > 0) neigh.pb(grid[i-1][j]);
//                if(i+1 < n) neigh.pb(grid[i+1][j]);
//                if(j > 0) neigh.pb(grid[i][j-1]);
//                if(j+1 < n) neigh.pb(grid[i][j+1]);
//                ll ans = 1;
//                for(ll x : neigh){
//                    ans = ans*(x/gcd(ans,x));
//                }
//                grid[i][j] = ans+1;
//                assert(grid[i][j] < 1e15);
//                for(auto cur : neigh)
//                    assert(grid[i][j]%cur == 1);
//            }
//        }
//    for(int i = 0; i < n; ++i)
//        for(int j = 0; j < n; ++j)
//            arr.push_back(grid[i][j]);
//    for(int i = 0; i < n; ++i)
//        for(int j = 0; j < n; ++j)
//            cout << grid[i][j]  << " \n"[j==n-1];
//    sort(all(arr));
//    arr.erase(unique(all(arr)),arr.end());
//    assert(arr.size() == n*n);
//    //cout << n*n << ' ' << arr.size() << '\n';
//}
//
// ================================================ 
// ^^^	ABOVE is what I wrote in my editor	^^^
// ------------------------------------------------ 
// vvv	BELOW is automatically generated 	vvv
// ================================================ 
#include <numeric>
#include <set>
#include <iomanip>
#include <algorithm> 
#include <cassert>
#include <vector>
#include <iostream>
#define pb push_back 
#define all(v) (v).begin(),(v).end()
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<int> vi;
template<typename IT>
ostream &print(IT b, IT e, string sep = " ", ostream &stream = cout){
	for(IT it = b; it != e; ++it){
		stream << *it;
		if(it+1 != e) stream << sep;
	} 
	return stream;
}
template<typename IT> istream &read(IT b, IT e, istream &stream = cin){
	for(IT it = b; it != e; ++it) stream >> *it; return stream;
}
template<typename T> ostream &operator<<(ostream &stream, vector<T> &t){
	return print(all(t)," ",stream);
}
template<typename T> ostream &operator<<(ostream &stream, vector<T> &&t){
	return print(all(t)," ",stream);
}
template<typename T> istream &operator>>(istream &stream, vector<T> &t){
	return read(all(t),stream);
}
template <typename T> 
void print(T x){
	cout << x << '\n';
}
template <typename t> 
void print(vector<t> arr,string sep=" "){
    if(arr.empty())
        return;
	print(arr.begin(),arr.end(),sep);
	cout << '\n';
}
template <typename T> 
void print(vector<vector<T> > arr){
	for(int i = 0; i < arr.size(); ++i){
		cout << "[" << arr[i] << "]";
		if(i+1 < arr.size())
			cout << ", ";
	}
	cout << '\n';
}
template<typename T>
T _();
const ll INF = ll(2e18)+666;
const ll MOD = 1e9+7;
template<typename T>
vector<T> operator+(const vector<T> &lhs, const vector<T> &rhs){
	vector<T> ans(lhs);
	assert(ans.size() == rhs.size());
	for(int i = 0; i < rhs.size(); ++i)
		ans[i] += rhs[i];
	return ans;
}
template<typename T>
vector<T> operator+=(vector<T> &lhs, const vector<T> &rhs){
	return lhs = lhs+rhs;
	//vector<T> ans(lhs);
	//assert(ans.size() == rhs.size());
	//for(int i = 0; i < rhs.size(); ++i)
		//ans[i] += rhs[i];
	//return ans;
}
template<typename T>
vector<T> operator-(const vector<T> &lhs, const vector<T> &rhs){
	vector<T> ans(all(lhs));
	assert(ans.size() == rhs.size());
	for(int i = 0; i < rhs.size(); ++i)
		ans[i] -= rhs[i];
	return ans;
}
template<typename T>
vector<T> operator-=(vector<T> &lhs, const vector<T> &rhs){
	return lhs = lhs-rhs;
	//vector<T> ans(lhs);
	//assert(ans.size() == rhs.size());
	//for(int i = 0; i < rhs.size(); ++i)
		//ans[i] += rhs[i];
	//return ans;
}
template<typename T>
T max(const vector<T> arr){
	assert(!arr.empty());
	T ans = arr[0];
	for(auto &cur : arr)
		ans = max(ans,cur);
	return ans;
}
template<typename T>
T max(const set<T> s){
	assert(!s.empty());
	return *--s.end();
}
template<typename T, typename G>
vector<T> set_max(vector<T> &base, const vector<G> &comp){
	assert(base.size() == comp.size());
	for(int i = 0; i < (int)comp.size(); ++i)
		base[i] = max(base[i],comp[i]);
	return base;
}
template <typename T>
vector<T> unique(vector<T> &arr){
    sort(all(arr));
	arr.erase(unique(all(arr)),arr.end());
	return arr;
}
const int N = 5e5+5;
bool prime[N];
ll gcd(ll x, ll y){
    if(y == 0)
        return x;
    return gcd(y,x%y);
}
void alternate(vi &arr){
    vi ans;
    for(int i = 0; i < 250; ++i){
        ans.pb(arr[i]);
        ans.pb(arr[499-i]);
    }
    arr = ans;
}
void _(){
    fill(prime+2,prime+N,true);
    vector<int> primes;
    for(int i = 2; i < N; ++i)
        if(prime[i]){
            for(int j = 2*i; j < N; j += i)
                prime[j] = false;
            primes.push_back(i);
        }
    primes.resize(1000);
    vi p1;
    vi p2;
    for(int i = 0; i < 1000; ++i)
        if(i%2) 
            p1.pb(primes[i]);
        else
            p2.pb(primes[i]);
    alternate(p1);
    alternate(p2);
    //print(p1);
    //print(p2);
    int n;
    cin >> n;
    if(n == 2){
        cout << "4 7 \n23 10\n";
        return;
    }
    vector<ll> arr;
    vvl grid(n,vl(n));
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < n; ++j){
            if(i % 2 == j % 2){
                int d1 = (i+j)/2;
                int d2 = (i-j+n-1)/2;
                ll a = p1[d1];
                ll b = p2[d2];
                grid[i][j] = a*b;
            }
        }
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < n; ++j){
            if(i % 2 != j % 2){
                vl neigh;
                if(i > 0) neigh.pb(grid[i-1][j]);
                if(i+1 < n) neigh.pb(grid[i+1][j]);
                if(j > 0) neigh.pb(grid[i][j-1]);
                if(j+1 < n) neigh.pb(grid[i][j+1]);
                ll ans = 1;
                for(ll x : neigh){
                    ans = ans*(x/gcd(ans,x));
                }
                grid[i][j] = ans+1;
                assert(grid[i][j] < 1e15);
                for(auto cur : neigh)
                    assert(grid[i][j]%cur == 1);
            }
        }
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < n; ++j)
            arr.push_back(grid[i][j]);
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < n; ++j)
            cout << grid[i][j]  << " \n"[j==n-1];
    sort(all(arr));
    arr.erase(unique(all(arr)),arr.end());
    assert(arr.size() == n*n);
    //cout << n*n << ' ' << arr.size() << '\n';
}
int main(){
	ios_base::sync_with_stdio(false); cin.tie(0);
	cout << fixed << setprecision(12);
	_();
}


Measured memory_bytes: 7924