#pragma once

#include <iostream>
#include <vector>
#include <algorithm>

using std::copy;
using std::vector;
using std::find;
using std::sort;

class Set {
 public:
  Set() {}
  explicit Set(vector<int64_t> v) {
    this->Copy(v);
  }

  Set& operator=(Set other) {
    this->Copy(other.Data());
    return *this;
  }

  vector<int64_t> Data() { return this->list; }
  void Add(int64_t value) {
    this->list.push_back(value);
  }
  void Remove(int64_t value) {
    vector<int64_t>::iterator idx = this->Index(value);
    if (idx == this->list.end()) return;
    this->list.erase(idx);
    this->list.resize(this->list.size() - 1);
  }
  bool Contains(int64_t value) {
    if (this->Index(value) == this->list.end())
      return false;
    return true;
  }

  Set Union(Set other) {
    vector<int64_t> res, second = other.Data();
    res.reserve(this->list.size() + second.size());
    res.insert(res.end(), this->list.begin(), this->list.end() );
    res.insert(res.end(), second.begin(), second.end());
    return Set(res);
  }
  Set Intersection(Set other) {
    vector<int64_t> res;
    for (auto k : other.Data())
      if (this->Contains(k))
        res.push_back(k);
    sort(res.begin(), res.end());
    return Set(res);
  }
  Set Difference(Set other) {
    vector<int64_t> res;
    for (auto k : this->list)
      if (!other.Contains(k))
        res.push_back(k);
    sort(res.begin(), res.end());
    return Set(res);
  }
  Set SymmetricDifference(Set other) {
    vector<int64_t> res;
    for (auto k : other.Data())
      if (!this->Contains(k))
        res.push_back(k);
    for (auto k : this->list)
      if (!other.Contains(k))
        res.push_back(k);
    sort(res.begin(), res.end());
    return Set(res);
  }

 private:
  vector<int64_t> list;

  void Copy(vector<int64_t> v) {
    this->list.resize(v.size());
    copy(v.begin(), v.end(), this->list.begin());
  }
  vector<int64_t>::iterator Index(int64_t value) {
    auto idx = find(this->list.begin(), this->list.end(), value);
    return idx;
  }
};