output_description: Print your solution in the following format:
  
      
      
      a_{1,1} ... a_{1,N}
      :
      a_{N,1} ... a_{N,N}
      
  
  * * *
input_description: Input is given from Standard Input in the following format:
  
      
      
      N
attempt: import sys
  import math
  import collections
  import itertools
  import array
  import inspect
  
  # Set max recursion limit
  sys.setrecursionlimit(1000000)
  
  
  # Debug output
  def chkprint(*args):
      names = {id(v): k for k, v in inspect.currentframe().f_back.f_locals.items()}
      print(", ".join(names.get(id(arg), "???") + " = " + repr(arg) for arg in args))
  
  
  # Binary converter
  def to_bin(x):
      return bin(x)[2:]
  
  
  def li_input():
      return [int(_) for _ in input().split()]
  
  
  def gcd(n, m):
      if n % m == 0:
          return m
      else:
          return gcd(m, n % m)
  
  
  def gcd_list(L):
      v = L[0]
  
      for i in range(1, len(L)):
          v = gcd(v, L[i])
  
      return v
  
  
  def lcm(n, m):
      return (n * m) // gcd(n, m)
  
  
  def lcm_list(L):
      v = L[0]
  
      for i in range(1, len(L)):
          v = lcm(v, L[i])
  
      return v
  
  
  # Width First Search (+ Distance)
  def wfs_d(D, N, K):
      """
      D: 隣接行列(距離付き)
      N: ノード数
      K: 始点ノード
      """
  
      dfk = [-1] * (N + 1)
      dfk[K] = 0
  
      cps = [(K, 0)]
      r = [False] * (N + 1)
      r[K] = True
      while len(cps) != 0:
          n_cps = []
          for cp, cd in cps:
              for i, dfcp in enumerate(D[cp]):
                  if dfcp != -1 and not r[i]:
                      dfk[i] = cd + dfcp
                      n_cps.append((i, cd + dfcp))
                      r[i] = True
  
          cps = n_cps[:]
  
      return dfk
  
  
  # Depth First Search (+Distance)
  def dfs_d(v, pre, dist):
      """
      v:  現在のノード
      pre: １つ前のノード
      dist: 現在の距離
  
      以下は別途用意する
      D: 隣接リスト(行列ではない)
      D_dfs_d: dfs_d関数で用いる，始点ノードから見た距離リスト
      """
  
      global D
      global D_dfs_d
  
      D_dfs_d[v] = dist
  
      for next_v, d in D[v]:
          if next_v != pre:
              dfs_d(next_v, v, dist + d)
  
      return
  
  
  def sigma(N):
      ans = 0
      for i in range(1, N + 1):
          ans += i
      return ans
  
  
  def comb(n, r):
      if n - r < r:
          r = n - r
      if r == 0:
          return 1
      if r == 1:
          return n
  
      numerator = [n - r + k + 1 for k in range(r)]
      denominator = [k + 1 for k in range(r)]
  
      for p in range(2, r + 1):
          pivot = denominator[p - 1]
          if pivot > 1:
              offset = (n - r) % p
              for k in range(p - 1, r, p):
                  numerator[k - offset] /= pivot
                  denominator[k] /= pivot
  
      result = 1
      for k in range(r):
          if numerator[k] > 1:
              result *= int(numerator[k])
  
      return result
  
  
  # --------------------------------------------
  
  dp = None
  
  
  def bisearch(L, target):
      low = 0
      high = len(L) - 1
  
      if len(L) == 0:
          return False
  
      while low <= high:
          mid = (low + high) // 2
          guess = L[mid]
          if guess == target:
              return True
          elif guess < target:
              low = mid + 1
          elif guess > target:
              high = mid - 1
      if guess != target:
          return False
  
  
  def main():
      MAX_V = 10**150
      N = int(input())
  
      L = [2, 3, 4, 5]
      A = [[0] * N for _ in range(N + 1)]
  
      A[0][0] = 2
      A[1][0] = 5
      A[0][1] = 3
      A[1][1] = 4
  
      for t in range(2, N):
          for i in range(t):
              if i == 0:
                  f = True
                  for x in range(A[i][t - 1] + 1, 0, -A[i][t - 1]):
                      if not bisearch(L, x):
                          L.append(x)
                          L.sort()
                          A[i][t] = x
                          f = False
                          break
                  if f:
                      for x in range(A[i][t - 1] + 1, MAX_V, A[i][t - 1]):
                          if not bisearch(L, x):
                              L.append(x)
                              L.sort()
                              A[i][t] = x
                              break
              else:
                  _lcm = lcm(A[i - 1][t], A[i][t - 1])
                  f = True
                  for x in range(_lcm + 1, 0, -_lcm):
                      if not bisearch(L, x):
                          L.append(x)
                          L.sort()
                          A[i][t] = x
                          f = False
                          break
                  if f:
                      for x in range(_lcm + 1, MAX_V, _lcm):
                          if not bisearch(L, x):
                              L.append(x)
                              L.sort()
                              A[i][t] = x
                              break
  
          for j in range(t):
              if j == 0:
                  f = True
                  for x in range(A[t - 1][j] + 1, 0, -A[t - 1][j]):
                      if not bisearch(L, x):
                          L.append(x)
                          L.sort()
                          A[t][j] = x
                          f = False
                          break
                  if f:
                      for x in range(A[t - 1][j] + 1, MAX_V, A[t - 1][j]):
                          if not bisearch(L, x):
                              L.append(x)
                              L.sort()
                              A[t][j] = x
                              break
              else:
                  _lcm = lcm(A[t - 1][j], A[t][j - 1])
                  f = True
                  for x in range(_lcm + 1, 0, -_lcm):
  
                      if not bisearch(L, x):
                          L.append(x)
                          L.sort()
                          A[t][j] = x
                          f = False
                          break
                  if f:
                      for x in range(_lcm + 1, MAX_V, _lcm):
                          if not bisearch(L, x):
                              L.append(x)
                              L.sort()
                              A[t][j] = x
                              break
  
          print(L)
  
          _lcm = lcm(A[t - 1][t], A[t][t - 1])
          f = True
          for x in range(_lcm + 1, 0, -_lcm):
              if not bisearch(L, x):
                  L.append(x)
                  L.sort()
                  A[t][t] = x
                  break
          if f:
              for x in range(_lcm + 1, MAX_V, _lcm):
                  if not bisearch(L, x):
                      L.append(x)
                      L.sort()
                      A[t][t] = x
                      break
  
      for i in range(N):
          print(" ".join(list(map(str, A[i]))))
  
  
  main()
  
problem_description: Statement
  
  You are given an integer N.
  
  Construct any one N-by-N matrix a that satisfies the conditions below. It can
  be proved that a solution always exists under the constraints of this problem.
  
    * 1 \leq a_{i,j} \leq 10^{15}
    * a_{i,j} are pairwise distinct integers.
    * There exists a positive integer m such that the following holds: Let x and y be two elements of the matrix that are vertically or horizontally adjacent. Then, {\rm max}(x,y) {\rm mod} {\rm min}(x,y) is always m.
samples: [{"input": "2", "output": "4 7\n    23 10\n    \n\n  * For any two elements x and y that are vertically or horizontally adjacent, {\\rm max}(x,y) {\\rm mod} {\\rm min}(x,y) is always 3."}]