#include <algorithm>
#include <array>
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <random>
#include <set>
#include <utility>
#include <vector>

using namespace std;

// Generate endpoint-collision stress tests.
//
// Each block starts with a short prefix that creates a merge through an
// interval whose endpoint summary can be lost if a solution stores only one
// representative per left/right endpoint and then updates that summary using a
// different interval with the same endpoint. The generator mixes two symmetric
// block families (same-right and same-left collisions), then adds extra local
// intervals, optional inter-block bridges, and random background noise so the
// final tests are not just a repetition of one fixed gadget.
//
// Tiny mode keeps every interval length at most 2 while preserving the same
// collision pattern.

namespace {

struct block_data {
    array<int, 5> pts;
    vector<pair<int, int>> prefix;
    vector<pair<int, int>> extras;
};

void try_add_candidate(vector<pair<int, int>>& out, set<pair<int, int>>& used, int l, int r) {
    if (l >= r) return;
    pair<int, int> interval = {l, r};
    if (!used.insert(interval).second) return;
    out.push_back(interval);
}

array<int, 5> make_points(int base, bool tiny_mode, mt19937& rng) {
    array<int, 5> pts;
    pts[0] = base;
    if (tiny_mode) {
        for (int i = 1; i < 5; i++) {
            pts[i] = base + i;
        }
        return pts;
    }

    uniform_int_distribution<int> gap_dist(1, 3);
    for (int i = 1; i < 5; i++) {
        pts[i] = pts[i - 1] + gap_dist(rng);
    }
    return pts;
}

vector<pair<int, int>> build_local_extras(const array<int, 5>& pts,
                                          const vector<pair<int, int>>& prefix,
                                          bool tiny_mode) {
    set<pair<int, int>> used(prefix.begin(), prefix.end());
    vector<pair<int, int>> extras;

    try_add_candidate(extras, used, pts[0], pts[1]);
    try_add_candidate(extras, used, pts[3], pts[4]);

    if (!tiny_mode) {
        try_add_candidate(extras, used, pts[0], pts[3]);
        try_add_candidate(extras, used, pts[1], pts[4]);
        try_add_candidate(extras, used, pts[0], pts[4]);

        for (int i = 0; i < 4; i++) {
            if (pts[i] + 1 < pts[i + 1]) {
                try_add_candidate(extras, used, pts[i] + 1, pts[i + 1]);
                try_add_candidate(extras, used, pts[i], pts[i + 1] - 1);
            }
        }
    }

    return extras;
}

block_data build_same_right_block(int base, bool tiny_mode, mt19937& rng) {
    block_data block;
    block.pts = make_points(base, tiny_mode, rng);
    const auto& p = block.pts;

    // Representative-collision motif on the right endpoint.
    block.prefix = {
        {p[2], p[3]},
        {p[1], p[2]},
        {p[2], p[4]},
        {p[1], p[3]},
        {p[0], p[2]},
    };
    block.extras = build_local_extras(block.pts, block.prefix, tiny_mode);
    return block;
}

block_data build_same_left_block(int base, bool tiny_mode, mt19937& rng) {
    block_data block;
    block.pts = make_points(base, tiny_mode, rng);
    const auto& p = block.pts;

    // Symmetric version that stresses solutions which summarize only by left endpoint.
    block.prefix = {
        {p[1], p[2]},
        {p[2], p[3]},
        {p[0], p[2]},
        {p[1], p[3]},
        {p[2], p[4]},
    };
    block.extras = build_local_extras(block.pts, block.prefix, tiny_mode);
    return block;
}

vector<pair<int, int>> build_bridge_candidates(const vector<block_data>& blocks) {
    vector<pair<int, int>> bridges;
    set<pair<int, int>> used;

    int limit_span = min((int)blocks.size(), 4);
    for (int span = 2; span <= limit_span; span++) {
        for (int start = 0; start + span <= (int)blocks.size(); start++) {
            const auto& left = blocks[start].pts;
            const auto& right = blocks[start + span - 1].pts;
            try_add_candidate(bridges, used, left[0], right[4]);
            try_add_candidate(bridges, used, left[1], right[4]);
            try_add_candidate(bridges, used, left[0], right[3]);
        }
    }

    return bridges;
}

void append_candidate_pool(vector<pair<int, int>>& intervals,
                           set<pair<int, int>>& seen,
                           vector<pair<int, int>> candidates,
                           int target_size,
                           mt19937& rng) {
    shuffle(candidates.begin(), candidates.end(), rng);
    for (auto interval : candidates) {
        if ((int)intervals.size() == target_size) break;
        if (!seen.insert(interval).second) continue;
        intervals.push_back(interval);
    }
}

void append_random_noise(vector<pair<int, int>>& intervals,
                         set<pair<int, int>>& seen,
                         int n,
                         bool tiny_mode,
                         int noise_base,
                         mt19937& rng) {
    int limit = 2 * n;
    if (tiny_mode) {
        if (noise_base >= limit) noise_base = max(0, limit - 2);
        uniform_int_distribution<int> len_dist(1, 2);
        int attempts = 0;
        while ((int)intervals.size() < n && attempts <= 8 * n) {
            int l = noise_base + (int)(rng() % (unsigned)max(1, limit - noise_base));
            int r = l + len_dist(rng);
            attempts++;
            if (r > limit) continue;
            if (!seen.insert({l, r}).second) continue;
            intervals.push_back({l, r});
            attempts = 0;
        }

        for (int l = noise_base; (int)intervals.size() < n && l < limit; l++) {
            for (int len = 1; len <= 2 && (int)intervals.size() < n; len++) {
                int r = l + len;
                if (r > limit) continue;
                if (!seen.insert({l, r}).second) continue;
                intervals.push_back({l, r});
            }
        }
        return;
    }

    if (noise_base >= limit) noise_base = max(0, limit - 1);
    int attempts = 0;
    while ((int)intervals.size() < n && attempts <= 8 * n) {
        int l = noise_base + (int)(rng() % (unsigned)max(1, limit - noise_base));
        int r = l + 1 + (int)(rng() % (unsigned)(limit - l));
        attempts++;
        if (r > limit) continue;
        if (!seen.insert({l, r}).second) continue;
        intervals.push_back({l, r});
        attempts = 0;
    }

    for (int l = noise_base; (int)intervals.size() < n && l < limit; l++) {
        for (int r = l + 1; (int)intervals.size() < n && r <= limit; r++) {
            if (!seen.insert({l, r}).second) continue;
            intervals.push_back({l, r});
        }
    }
}

}  // namespace

int main(int argc, char* argv[]) {
    assert(argc == 4 || argc == 5);
    int n = atoi(argv[1]);
    int requested_blocks = atoi(argv[2]);
    int mode = 0;
    int seed = 0;
    if (argc == 4) {
        seed = atoi(argv[3]);
    } else {
        mode = atoi(argv[3]);
        seed = atoi(argv[4]);
    }
    assert(n >= 1);
    assert(mode == 0 || mode == 1);

    if (n < 5) {
        cout << n << '\n';
        for (int i = 0; i < n; i++) {
            cout << 2 * i << ' ' << 2 * i + 1 << '\n';
        }
        return 0;
    }

    bool tiny_mode = (mode == 1);
    mt19937 rng(seed);

    int per_block_stride = tiny_mode ? 6 : 15;
    int max_blocks_by_size = max(1, n / 5);
    int max_blocks_by_coord = max(1, (2 * n + 2) / per_block_stride);
    int max_blocks = min(max_blocks_by_size, max_blocks_by_coord);
    int blocks = requested_blocks <= 0 ? max(1, min(max_blocks, n / 12)) : min(requested_blocks, max_blocks);

    vector<block_data> block_list;
    block_list.reserve((size_t)blocks);

    int base = 0;
    for (int i = 0; i < blocks; i++) {
        bool same_right = (i == 0) || ((rng() & 1U) == 0U);
        block_data block = same_right ? build_same_right_block(base, tiny_mode, rng)
                                      : build_same_left_block(base, tiny_mode, rng);
        block_list.push_back(block);
        base = block.pts[4] + (tiny_mode ? 2 : 3);
    }

    vector<pair<int, int>> intervals;
    intervals.reserve((size_t)n);
    set<pair<int, int>> seen;

    for (const auto& block : block_list) {
        for (auto interval : block.prefix) {
            assert(seen.insert(interval).second);
            intervals.push_back(interval);
        }
    }

    vector<pair<int, int>> local_candidates;
    for (const auto& block : block_list) {
        for (auto interval : block.extras) {
            local_candidates.push_back(interval);
        }
    }
    append_candidate_pool(intervals, seen, local_candidates, n, rng);

    if (!tiny_mode) {
        append_candidate_pool(intervals, seen, build_bridge_candidates(block_list), n, rng);
    }

    append_random_noise(intervals, seen, n, tiny_mode, base, rng);
    assert((int)intervals.size() == n);

    int max_r = 0;
    for (auto [l, r] : intervals) {
        assert(0 <= l && l < r);
        if (tiny_mode) {
            assert(r - l <= 2);
        }
        max_r = max(max_r, r);
    }
    assert(max_r <= 2 * n);

    cout << n << '\n';
    for (auto [l, r] : intervals) {
        cout << l << ' ' << r << '\n';
    }
    return 0;
}
