#include<bits/stdc++.h>

#ifndef KRAKOZYABRA
#include "testlib.h"
#else
#include "testliblochal.h"
#endif // KRAKOZYABRA

using namespace std;

template <typename T>
class ordered_set {
private:
    struct Node {
        T data;
        int weight; // Number of nodes in the subtree rooted at this node
        std::unique_ptr<Node> left;
        std::unique_ptr<Node> right;

        Node(const T& data) : data(data), weight(1), left(nullptr), right(nullptr) {}
    };

    std::unique_ptr<Node> root;

    // Helper function to update the weight of a node
    void update_weight(Node* node) {
        if (node) {
            node->weight = 1;
            if (node->left) node->weight += node->left->weight;
            if (node->right) node->weight += node->right->weight;
        }
    }

    // Helper function for insertion
    Node* insert_recursive(Node* node, const T& data) {
        if (!node) {
            return new Node(data);
        }

        if (data < node->data) {
            node->left.reset(insert_recursive(node->left.release(), data));  //Release ownership before recursive call
        }
        else {
            node->right.reset(insert_recursive(node->right.release(), data)); //Release ownership before recursive call
        }
        update_weight(node);
        return node;
    }

    // Helper function for deletion (find minimum in right subtree)
    Node* find_min(Node* node) {
        while (node->left) {
            node = node->left.get(); // Access the raw pointer
        }
        return node;
    }

    // Helper function for deletion
    Node* delete_recursive(Node* node, const T& data) {
        if (!node) {
            return nullptr; // Value not found
        }

        if (data < node->data) {
            node->left.reset(delete_recursive(node->left.release(), data)); //Release ownership before recursive call
        }
        else if (data > node->data) {
            node->right.reset(delete_recursive(node->right.release(), data)); //Release ownership before recursive call
        }
        else {
            // Node to be deleted found

            // Case 1: Node with no child or only one child
            if (!node->left) {
                Node* temp = node->right.release();
                delete node;
                return temp;
            }
            else if (!node->right) {
                Node* temp = node->left.release();
                delete node;
                return temp;
            }

            // Case 2: Node with two children
            Node* temp = find_min(node->right.get());
            node->data = temp->data;
            node->right.reset(delete_recursive(node->right.release(), temp->data));
        }

        update_weight(node);
        return node;
    }

    //Helper for get_element_at_index (find the k-th smallest element).
    T get_element_at_index_recursive(Node* node, int index) {
        if (!node) {
            throw std::out_of_range("Index out of range");
        }

        int left_subtree_size = (node->left) ? node->left->weight : 0;

        if (index == left_subtree_size) {
            return node->data;
        }
        else if (index < left_subtree_size) {
            return get_element_at_index_recursive(node->left.get(), index);
        }
        else {
            return get_element_at_index_recursive(node->right.get(), index - left_subtree_size - 1);
        }
    }

    //Helper to perform inorder traversal
    void inorder_traversal_recursive(Node* node, std::vector<T>& result) const {
        if (node) {
            inorder_traversal_recursive(node->left.get(), result);
            result.push_back(node->data);
            inorder_traversal_recursive(node->right.get(), result);
        }
    }

    //Helper to find index of an element.
    int get_index_of_element_recursive(Node* node, const T& target, int current_rank) const {
        if (!node) {
            return -1; // Element not found
        }

        int left_subtree_size = (node->left) ? node->left->weight : 0;

        if (target == node->data) {
            return current_rank + left_subtree_size;
        }
        else if (target < node->data) {
            return get_index_of_element_recursive(node->left.get(), target, current_rank);
        }
        else {
            return get_index_of_element_recursive(node->right.get(), target, current_rank + left_subtree_size + 1);
        }
    }


public:
    ordered_set() : root(nullptr) {}

    void insert(const T& data) {
        if (!root) {
            root = std::make_unique<Node>(data);
        }
        else {
            root.reset(insert_recursive(root.release(), data)); //Release ownership before recursive call
        }
    }

    void erase(const T& data) {
        root.reset(delete_recursive(root.release(), data)); //Release ownership before recursive call
    }

    T get_element(int index) {
        if (index < 0 || index >= size()) {
            throw std::out_of_range("Index out of range");
        }
        return get_element_at_index_recursive(root.get(), index);
    }

    int size() const {
        return (root) ? root->weight : 0;
    }

    bool empty() const {
        return root == nullptr;
    }

    void clear() {
        root.reset(); // Effectively deletes the entire tree
    }

    // Returns a vector of the elements in sorted order (inorder traversal)
    std::vector<T> inorder_traversal() const {
        std::vector<T> result;
        inorder_traversal_recursive(root.get(), result);
        return result;
    }

    // Added function to get the index of an element
    int get_index(const T target) const {
        return get_index_of_element_recursive(root.get(), target, 0);
    }
};

int main(int argc, char* argv[]) {
#ifndef KRAKOZYABRA
    registerGen(argc, argv, 1);
#endif // KRAKOZYABRA


    int minN = opt<int>(2), maxN = opt<int>(3);
    int minM = opt<int>(4), maxM = opt<int>(5);
    int minA = opt<int>(6), maxA = opt<int>(7);
    int n = rnd.next(minN, maxN);
    int m = rnd.next(minM, maxM);
    cout << n << ' ' << m << '\n';
    ordered_set<int> st;
    vector<int> a;
    while (a.size() < n) {
        int aa = rnd.next(minA, maxA);
        if (st.get_index(aa) == -1) {
            a.push_back(aa);
            st.insert(aa);
        }
    }

    auto getrndin = [&]() {
        if (st.size() == 0)
            return -1;
        int sz = st.size();
        int i = rnd.next(0, sz - 1);
        return st.get_element(i);
        };
    auto getrndnew = [&]() {
        int aa = rnd.next(minA, maxA);
        while (st.get_index(aa) != -1)
            aa = rnd.next(minA, maxA);
        return aa;
        };

    for (int i = 0; i < n; i++)
        cout << a[i] << " \n"[i == n - 1];
    for (int i = 0; i < m; i++) {
        int t = 0;
        if (st.size() == 0)
            t = 2;
        else
            t = rnd.next(1, 3);
        if (t == 1) {
            int x = getrndnew();
            int y = getrndin();
            st.insert(x);
            cout << t << ' ' << x << ' ' << y << '\n';
        }
        if (t == 2) {
            int x = getrndnew();
            st.insert(x);
            cout << t << ' ' << x << '\n';
        }
        if (t == 3) {
            int x = getrndin();
            st.erase(x);
            cout << t << ' ' << x << '\n';
        }
    }
}

//#pragma once
//#include <bits/stdc++.h>
//#include <random>
//#include <optional>
//
//using namespace std;
//
//random_device rd;
//mt19937 gen(rd());
//
//template <typename T>
//T opt(T value) {
//    FILE* stream;
//    freopen_s(&stream, "params.txt", "r", stdin);
//    T res;
//    for (int i = 0; i < value; i++)
//        cin >> res;
//    return res;
//}
//
//class Random {
//public:
//    long long next(long long a, long long b) {
//        return a + gen() % (b - a + 1);
//    };
//};
//
//Random rnd;