//#pragma GCC optimize("Ofast,unroll-loops")
//#pragma GCC target("avx2,tune=native")
#include <bits/stdc++.h>
using namespace std;
 
#define file "graph"
#define ff(i, a, b) for(auto i=(a); i<=(b); ++i)
#define ffr(i, b, a) for(auto i=(b); b>=(a); --i)
#define nl "\n"
#define ss " "
//#define pb push_back
#define pb emplace_back
#define fi first
#define se second
#define sz(s) (int)s.size()
#define all(s) (s).begin(), (s).end()
#define ms(a,x) memset(a, x, sizeof (a))
#define cn continue
#define re exit(0)
 
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
 
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll ran(ll l, ll r)
{
    return uniform_int_distribution<ll> (l, r)(rng);
}
 
inline void rf()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);
    if(fopen(file".inp","r"))
    {
        freopen(file".inp","r",stdin);
        freopen(file".out","w",stdout);
    }
}
 
const int mod=1e9+19972207;
const int maxn=1e5+15;
const ll inf=5e16;
 
template<typename T> inline void add(T &x, const T &y)
{
    x+=y;
    if(x>=mod) x-=mod;
    if(x<0) x+=mod;
}
 
template<typename T> inline bool maxi(T &a, T b)
{
    if(a>=b) return 0;
    a=b; return 1;
}
 
template<typename T> inline bool mini(T &a, T b)
{
    if(a<=b) return 0;
    a=b; return 1;
}
 
int diss, numPair;
int numNode;
vector<pair<int, int>> edges;
 
void init(void) {
	cin >> diss >> numPair;
}
void print(void) {
	cout << numNode << " " << edges.size() << "\n";
	for (const pair<int, int> &e : edges) cout << e.fi << " " << e.se << "\n";
}
 
namespace general {
	bool check(void) {
		return diss > 2;
	}
 
	void createComponent(int x, int y) {
		int left_center = ++numNode;
		int cur = left_center;
		ff(i, 1, diss) {
			int tmp = ++numNode;
			edges.push_back({cur, tmp});
			cur = tmp;
		}
		int right_center = cur;
 
		ff(i, 1, x) edges.push_back({left_center, ++numNode});
		ff(i, 1, y) edges.push_back({right_center, ++numNode});
	}
 
	void solve(void) {
		while (numPair > 0) {
 
            int N_remain = 1000 - numNode;
            int M_remain = 1000 - (int)edges.size();
 
            if (N_remain < diss + 3) break;
            if (M_remain < diss + 2) break;
 
			// 1. Tìm t max sao cho t^2 <= numPair (Logic Cauchy ban đầu)
			int t_optimal = 1;
			while ((ll)(t_optimal + 1) * (t_optimal + 1) <= numPair) t_optimal++;
 
            // 2. Giới hạn t bởi N và M
            int limit_sum_N = N_remain - (diss + 1);
            int limit_sum_M = M_remain - diss;
            int limit_sum = min(limit_sum_N, limit_sum_M);
 
            int t_limit = limit_sum / 2;
            if (t_limit < 1) t_limit = 1;
 
            int t = min(t_optimal, t_limit);
            if (t < 1) t = 1;
 
            // 3. Tạo TPLT với x=y=t
            int x = t;
            int y = t;
 
            ll pairs_to_create = (ll)x * y;
 
            if (pairs_to_create > numPair) {
                // Điều này chỉ xảy ra khi t_optimal được tính quá sát limit_sum/2
                // Trong trường hợp này, ta phải giảm x, y sao cho x*y = numPair và x+y nhỏ nhất
                int P = numPair;
 
                x = (int)sqrt(P);
                while(P % x != 0) x--;
                y = P / x;
 
                // Kiểm tra lại giới hạn sum (Nếu t_optimal quá lớn)
                if (x + y > limit_sum) {
                    x = 1;
                    y = limit_sum - 1;
                    if (y < 1) break;
                    pairs_to_create = min((ll)P, (ll)x * y);
                    y = (int)pairs_to_create; // x=1, y=P
                } else {
                    pairs_to_create = (ll)x * y;
                }
            }
 
            // Kiểm tra lại lần cuối
            if (x == 0 || y == 0 || x + y > limit_sum) break;
 
            if (x > 0 && y > 0 && pairs_to_create > 0) {
                createComponent(x, y);
                numPair -= pairs_to_create;
            } else {
                break;
            }
		}
	}
}
 
namespace special {
	bool check(void) {
		return diss == 2;
	}
 
	void createComponent(int t) {
		int u = ++numNode;
		ff(i, 1, t) edges.push_back({u, ++numNode});
	}
 
	void solve(void) { 
		while (numPair > 0) {
 
            int N_remain = 1000 - numNode;
            int M_remain = 1000 - (int)edges.size();
 
            if (N_remain < 3 || M_remain < 2) break;
 
            // 1. Tối ưu: t max sao cho t*(t-1)/2 <= numPair
            int t_optimal = 2;
            while ((ll)(t_optimal + 1) * t_optimal / 2 <= numPair) t_optimal++;
            t_optimal--;
 
            // 2. Giới hạn t bởi N và M
            int limit_t_N = N_remain - 1;
            int limit_t_M = M_remain;
 
            int limit_t = min({t_optimal, limit_t_N, limit_t_M});
            if (limit_t < 2) break;
 
            int t = limit_t;
 
            ll pairs_created = (ll)t * (t - 1) / 2;
 
            if (pairs_created > numPair) {
                // Điều này không nên xảy ra nếu t đã được giới hạn bởi t_optimal
                // Nhưng nếu xảy ra, ta phải giảm t xuống.
                t = 2;
                while ((ll)(t + 1) * t / 2 <= numPair) t++;
                t--;
 
                t = min(t, limit_t);
                pairs_created = (ll)t * (t - 1) / 2;
            }
 
            if (t < 2 || pairs_created <= 0) break;
 
            if (pairs_created > 0) {
                createComponent(t);
                numPair -= pairs_created;
            } else {
                break;
            }
		}
	}
}
 
void process(void) {
	if (general::check()) {
		general::solve();
	} else if (special::check()) {
		special::solve();
	}
}
 
signed main()
{
    rf();
    init();
    process();
    print();
    return 0;
}