Problem: For a set $S$ of integers, perform a sequence of the following operations. Note that each value in $S$ must be unique.

* insert($x$): Insert $x$ to $S$ and report the number of elements in $S$ after the operation.
* find($x$): Report the number of $x$ in $S$ (0 or 1).
* delete($x$): Delete $x$ from $S$.
* dump($L$, $R$): Print elements $x$ in $S$ such that $L \leq x \leq R$.

Constraints

* $1 \leq q \leq 200,000$
* $0 \leq x \leq 1,000,000,000$
* The total number of elements printed by dump operations does not exceed $1,000,000$

Input

The input is given in the following format.


$q$
$query_1$
$query_2$
:
$query_q$


Each query $query_i$ is given by


0 $x$


or


1 $x$


or


2 $x$


or


3 $L$ $R$


where the first digits 0, 1, 2 and 3 represent insert, find, delete and dump operations respectively.

Output

For each insert operation, print the number of elements in $S$.
For each find operation, print the number of specified elements in $S$.
For each dump operation, print the corresponding elements in ascending order. Print an element in a line.

Example

Input

9
0 1
0 2
0 3
2 2
1 1
1 2
1 3
0 4
3 2 4


Output

1
2
3
1
0
1
3
3
4

Solution (in CPP):

Solution:
#include <cstdio>
#include <cstdint>
#include <cassert>
#include <algorithm>
#include <map>
#include <vector>

class EmdeBoasTree;

template <size_t Height>
class EmdeBoasTreeNode {
  friend EmdeBoasTree;
  friend EmdeBoasTreeNode<Height+1>;
  using IndexType = unsigned long long;
  static constexpr int HALF_WIDTH=1<<(Height-1);

  IndexType min_=1, max_=0;
  EmdeBoasTreeNode<Height-1> *summary=nullptr;
  std::map<IndexType, EmdeBoasTreeNode<Height-1>> cluster={};

  EmdeBoasTreeNode() = default;

  constexpr IndexType upper(IndexType u) const {
    return u >> HALF_WIDTH;
  }

  constexpr IndexType lower(IndexType u) const {
    return u & ((IndexType(1) << HALF_WIDTH) - 1);
  }

  bool contains(IndexType u) const {
    if (u == min_ || u == max_) return true;
    if (u < min_ || max_ < u) return false;
    if (summary == nullptr || !summary->contains(upper(u))) return false;
    assert(cluster.find(upper(u)) != cluster.end());
    return cluster.at(upper(u)).contains(lower(u));
  }

  IndexType successor(IndexType u) const {
    if (empty() || u >= max_) throw std::out_of_range("no successor");
    if (u < min_) return min_;
    if (summary == nullptr) return max_;

    IndexType uu=upper(u), lu=lower(u);
    if (summary->contains(uu) && lu < cluster.at(uu).max())
      return uu << HALF_WIDTH | cluster.at(uu).successor(lu);

    if (uu >= summary->max()) return max_;
    IndexType bucket=summary->successor(uu);
    return bucket << HALF_WIDTH | cluster.at(bucket).min();
  }

  IndexType predecessor(IndexType u) const {
    if (empty() || u <= min_) throw std::out_of_range("no predecessor");
    if (u > max_) return max_;
    if (summary == nullptr) return min_;

    IndexType uu=upper(u), lu=lower(u);
    if (summary->contains(uu) && lu > cluster.at(uu).min())
      return uu << HALF_WIDTH | cluster.at(uu).predecessor(lu);

    if (uu <= summary->min()) return min_;
    IndexType bucket=summary->predecessor(uu);
    return bucket << HALF_WIDTH | cluster.at(bucket).max();
  }

  bool insert(IndexType u) {
    if (empty()) {
      min_ = max_ = u;
      return true;
    }
    if (u == min_ || u == max_) return false;

    if (min_ == max_) {
      if (max_ < u) {
        max_ = u;
        return true;
      }
      min_ = u;
      return true;
    }

    if (min_ > u) {
      std::swap(min_, u);
    } else if (max_ < u) {
      std::swap(max_, u);
    }

    if (summary == nullptr) summary = new EmdeBoasTreeNode<Height-1>;
    summary->insert(upper(u));

    auto it=cluster.lower_bound(upper(u));
    if (it == cluster.end() || it->first != upper(u))
      it = cluster.emplace_hint(it, upper(u), EmdeBoasTreeNode<Height-1>());

    return it->second.insert(lower(u));
  }

  bool erase(IndexType u) {
    if (min_ == max_) {
      if (min_ == u) {
        min_ = 1;
        max_ = 0;
        return true;
      } else {
        return false;
      }
    }

    if (u < min_ || max_ < u) return false;

    if (summary == nullptr) {
      if (u == min_) {
        min_ = max_;
        return true;
      } else if (u == max_) {
        max_ = min_;
        return true;
      } else {
        return false;
      }
    }

    IndexType bucket=upper(u), v=lower(u);
    if (u == min_) {
      bucket = summary->min();
      v = cluster.at(bucket).min();
      min_ = bucket << HALF_WIDTH | v;
    } else if (u == max_) {
      bucket = summary->max();
      v = cluster.at(bucket).max();
      max_ = bucket << HALF_WIDTH | v;
    } else {
      if (!summary->contains(bucket)) return false;
    }
    bool res=cluster.at(bucket).erase(v);
    if (res && cluster.at(bucket).empty()) {
      cluster.erase(bucket);
      summary->erase(bucket);
      if (summary->empty()) {
        delete summary;
        summary = nullptr;
      }
    }
    return res;
  }

  IndexType min() const {
    if (empty()) throw std::out_of_range("min() on {}");
    return min_;
  }

  IndexType max() const {
    if (empty()) throw std::out_of_range("max() on {}");
    return max_;
  }

  bool empty() const {
    return max_ < min_;
  }
};

template <>
class EmdeBoasTreeNode<3> {
  friend EmdeBoasTree;
  friend EmdeBoasTreeNode<4>;
  using IndexType = unsigned long long;

  IndexType vec[4]={};

  EmdeBoasTreeNode() = default;

  bool contains(IndexType u) const {
    return vec[u/64] >> (u%64) & 1;
  }

  IndexType successor(IndexType u) const {
    IndexType tmp=vec[u/64]&~((1uLL<<((u+1)%64))-1);
    if (u % 64 < 63 && tmp > 0) return (u/64*64) | __builtin_ctzll(tmp);
    for (size_t i=u/64+1; i<4; ++i)
      if (vec[i]) return i<<6 | __builtin_ctzll(vec[i]);

    throw std::out_of_range("u <= minimum");
  }

  IndexType predecessor(IndexType u) const {
    IndexType tmp=vec[u/64]&((1uLL<<(u%64))-1);
    if (tmp > 0) return (u/64*64) | (63-__builtin_clzll(tmp));
    for (size_t i=u/64; i--;)
      if (vec[i]) return i<<6 | (63-__builtin_clzll(vec[i]));

    throw std::out_of_range("u >= maximum");
  }

  bool insert(IndexType u) {
    if (vec[u/64] >> (u%64) & 1) return false;
    vec[u/64] |= 1uLL << (u%64);
    return true;
  }

  bool erase(IndexType u) {
    if (!(vec[u/64] >> (u%64) & 1)) return false;
    vec[u/64] ^= 1uLL << (u%64);
    return true;
  }

  IndexType min() const {
    for (int i=0; i<4; ++i)
      if (vec[i]) return i<<6 | __builtin_ctzll(vec[i]);

    throw std::out_of_range("min() on {}");
  }

  IndexType max() const {
    for (size_t i=4; i--;)
      if (vec[i]) return i<<6 | (63-__builtin_clzll(vec[i]));

    throw std::out_of_range("max() on {}");
  }

  bool empty() const {
    return !(vec[0] || vec[1] || vec[2] || vec[3]);
  }
};

class EmdeBoasTree {
  using IndexType = unsigned long long;
  static constexpr size_t TopHeight=5;

  EmdeBoasTreeNode<TopHeight> *root;
  size_t size_=0;

public:
  EmdeBoasTree(): root(new EmdeBoasTreeNode<TopHeight>) {}

  IndexType min() const { return root->min(); }
  IndexType max() const { return root->max(); }
  bool empty() const { return root->empty(); }
  bool contains(IndexType u) const { return !empty() && root->contains(u); }
  IndexType successor(IndexType u) const { return root->successor(u); }
  IndexType predecessor(IndexType u) const { return root->predecessor(u); }
  bool insert(IndexType u) { return root->insert(u) && ++size_; }
  bool erase(IndexType u) { return !empty() && root->erase(u) && size_--; }
  size_t size() const { return size_; }

  void debug() const {
    if (empty()) {
      fprintf(stderr, "{}\n");
      return ;
    }
    fprintf(stderr, "{");
    IndexType i_ub=max();
    for (IndexType i=min(); i<i_ub; i=successor(i)) {
      fprintf(stderr, "%llu%s", i, i<i_ub? ", ":"");
      assert(contains(successor(i)));
      assert(i < successor(i));
    }

    fprintf(stderr, "%llu}\n", i_ub);
    fflush(stderr);
  }
};

int main() {
  int q;
  scanf("%d", &q);

  EmdeBoasTree veb;

  for (int i=0; i<q; ++i) {
    int t;
    scanf("%d", &t);

    if (t == 0) {
      /* insert */
      int x;
      scanf("%d", &x);
      veb.insert(x);
      printf("%zu\n", veb.size());
    } else if (t == 1) {
      /* find */
      int x;
      scanf("%d", &x);
      printf("%d\n", veb.contains(x));
    } else if (t == 2) {
      /* delete */
      int x;
      scanf("%d", &x);
      veb.erase(x);
    } else if (t == 3) {
      /* dump */
      unsigned long long l, r;
      scanf("%llu %llu", &l, &r);
      if (veb.empty()) continue;
      // if (veb.max() < l) continue;

      // if (veb.contains(l))
      //   printf("%llu\n", l);

      // r = std::min(r, veb.max());
      // while (l < r) {
      //   l = veb.successor(l);
      //   if (l > r) break;
      //   printf("%llu\n", l);
      // }
      std::vector<int> res;
      if (veb.min() > r) continue;
      if (veb.contains(r))
        res.push_back(r);

      l = std::max(l, veb.min());
      while (l < r) {
        r = veb.predecessor(r);
        if (l > r) break;
        res.push_back(r);
      }
      std::reverse(res.begin(), res.end());
      for (int x: res)
        printf("%d\n", x);
    }
  }
}