#include<iostream>
#include<vector>
#include<stdlib.h>
#include<time.h>
#include<fstream>
#include<ctype.h>
#include<string>
using namespace std;
template <class DataType,class Conversion>
class CuckooHash
{
private:
int hashA1,hashA2,hashB1,hashB2;
int M1,M2,size,limit;
int prime;
DataType uninserted,*null;
DataType *Hash1,*Hash2;
DataType *aux;
void makeSizes(void);
int hashFunction1(DataType x);
int hashFunction2(DataType x);
void remakeFunctions(void);
int reinsert(DataType x);
int rehash(void);
int insertTry(DataType x);
public:
CuckooHash(void);
CuckooHash(int M1,int M2);
~CuckooHash(void);
DataType operator+=(const DataType&);
int operator-=(const DataType&);
int operator<(const DataType&);
int clean(void);
template<class DataType2,class Conversion2> friend istream& operator>>(istream&,CuckooHash<DataType2,Conversion2> &);
template<class DataType2,class Conversion2> friend ostream& operator<<(ostream&,CuckooHash<DataType2,Conversion2> &);
};
template<class DataType,class Conversion>
CuckooHash<DataType,Conversion>::CuckooHash(void)
{
srand(time(NULL));
this->M1 = 1000003;
this->M2 = 1000033;
prime = 1000000009;
Hash1 = new DataType[M1];
Hash2 = new DataType[M2];
aux = new DataType[M1+M2];
null = new DataType;
this->clean();
remakeFunctions();
}
template<class DataType,class Conversion>
CuckooHash<DataType,Conversion>::CuckooHash(int M1,int M2)
{
srand(time(NULL));
this->M1 = M1;
this->M2 = M2;
prime = 1000000009;
Hash1 = new DataType[M1];
Hash2 = new DataType[M2];
aux = new DataType[M1+M2];
null = new DataType;
this->clean();
remakeFunctions();
}
template<class DataType,class Conversion>
int CuckooHash<DataType,Conversion>::hashFunction1(DataType x)
{
Conversion conversion;
int y = conversion(x);
y = abs(y);
return ((1LL*hashA1*y+hashA2)%prime)%M1;
}
template<class DataType,class Conversion>
int CuckooHash<DataType,Conversion>::hashFunction2(DataType x)
{
Conversion conversion;
int y = conversion(x);
y = abs(y);
return ((1LL*hashB2*y+hashB1)%prime)%M2;
}
template<class DataType,class Conversion>
void CuckooHash<DataType,Conversion>::remakeFunctions(void)
{
hashA1 = rand()%prime;
hashA2 = rand()%prime;
hashB1 = rand()%prime;
hashB2 = rand()%prime;
}
template<class DataType,class Conversion>
void CuckooHash<DataType,Conversion>::makeSizes(void)
{
this->M1 = 1000011 + rand()%666013;
this->M2 = 1000011 + rand()%666013;
this->M1 += 1-(this->M1 & 1);
this->M2 += 1-(this->M2 & 1);
}
template<class DataType,class Conversion>
inline int CuckooHash<DataType,Conversion>::rehash(void)
{
/*Functia de rehash. Se iau toate elementele
din hash si se pastreaza separat. Se goleste
hashul si se incearca reinserarea fiecarui
element folosind functii de hash diferite.*/
int limit = -1;
aux[++limit] = uninserted;
for(int i=0;i<M1;i++)
if(Hash1[i] != *null)
aux[++limit] = Hash1[i];
for(int i=0;i<M2;i++)
if(Hash2[i] != *null)
aux[++limit] = Hash2[i];
int i = limit;
for(;;)
{
for(int j=0;j<=limit;j++)
*this -= aux[j];
remakeFunctions();
for(i=0;i<=limit;i++)
if(!insertTry(aux[i]))
break;
if(i == limit+1)
{
return 1;
}
}
return 0;
}
template<class DataType,class Conversion>
inline int CuckooHash<DataType,Conversion>::reinsert(DataType x)
{
/*Functia care incearca sa reinsereze
elementul x. Inserarea se face alternativ
intre cei doi vectori. Daca pozitia pe care
incearca sa insereze elementul x este o cupata
se elimina elementul respectiv, se atribuie
pozitiei valoarea lui x si se apeleaza
recursiv functia reinsert pentru valoarea
eliminata.*/
int hash_funct2 = hashFunction2(x),
hash_funct1 = hashFunction1(x);
if(size == 0)
{
uninserted = x;
return 0;
}
if(size & 1)
{
if(Hash1[hash_funct1] != *null)
{
DataType y = Hash1[hash_funct1];
Hash1[hash_funct1] = x;
-- size;
return reinsert(y);
}
else if(size & 1)
{
Hash1[hash_funct1] = x;
return 1;
}
}
else
{
if(Hash2[hash_funct2] != *null)
{
DataType y = Hash2[hash_funct2];
Hash2[hash_funct2] = x;
-- size;
return reinsert(y);
}
else
{
Hash2[hash_funct2] = x;
return 1;
}
}
return 0;
}
template<class DataType,class Conversion>
inline int CuckooHash<DataType,Conversion>::insertTry(DataType x)
{
/*Functia care verifica daca un element
poate fi inserat in Hash. Daca poate sa il
insereze, il insereaza, iar daca nu, pastreaza
ultimul element neinserat in variabila privata
"inserted"*/
int hash_funct1 = hashFunction1(x);
if(*this<x)
{
return 1;
}
if(Hash1[hash_funct1] != *null)
{
DataType y = Hash1[hash_funct1];
Hash1[hash_funct1] = x;
size = 20;
return reinsert(y);
}
else
{
Hash1[hash_funct1] = x;
return 1;
}
}
template<class DataType,class Conversion>
inline DataType CuckooHash<DataType,Conversion>::operator+=(const DataType& nou)
{
/*Functia publica de insert care
incearca sa insereze valoarea x
si daca nu reuseste apeleaza functia
rehash.*/
if(!insertTry(nou))
{
rehash();
}
return nou;
}
template<class DataType,class Conversion>
inline int CuckooHash<DataType,Conversion>::operator-=(const DataType& nou)
{
/*Daca exista valoarea x in hash
se elimina, atribuindu-se pozitiei
valoarea 0*/
int hash_funct1 = hashFunction1(nou);
int hash_funct2 = hashFunction2(nou);
if(Hash1[hash_funct1] == nou)
{
Hash1[hash_funct1] = *null;
return 1;
}
else if(Hash2[hash_funct2] == nou)
{
Hash2[hash_funct2] = *null;
return 1;
}
return -1;
}
template<class DataType,class Conversion>
inline int CuckooHash<DataType,Conversion>::operator<(const DataType& nou)
{
/*Se cauta valoarea x in hash.
Daca valoarea nu se gaseste in niciunul
dintre cei 2 vectori inseamna ca nu exista
in hash!*/
int hash_funct1 = hashFunction1(nou);
int hash_funct2 = hashFunction2(nou);
if(Hash1[hash_funct1] == nou)
{
return 1;
}
else if(Hash2[hash_funct2] == nou)
{
return 1;
}
return 0;
}
template<class DataType,class Conversion>
CuckooHash<DataType,Conversion>::~CuckooHash(void)
{
/*Elibereaza memoria folosita de
tabelele de dispersie si de vectorul
auxiliar*/
delete[] Hash1;
delete[] Hash2;
delete[] aux;
}
template<class DataType,class Conversion>
int CuckooHash<DataType,Conversion>::clean(void)
{
try
{
for(int i=0;i<M1;i++)
Hash1[i] = *null;
for(int i=0;i<M2;i++)
Hash2[i] = *null;
return 1;
}
catch(int e)
{
return 0;
}
}
template<class DataType2,class Conversion2>
istream& operator>>(istream& in,CuckooHash<DataType2,Conversion2>& Hash)
{
/*Citeste hash-ul primit ca parametru
din fisierul de intrare primit ca stream*/
char op;
DataType2 aux;
for(;(in>>op);)
{
if(op == '+' || op == '-')
{
in >> aux;
if(op == '+')
Hash += aux;
else
Hash -= aux;
}
}
return in;
}
template<class DataType2,class Conversion2>
ostream& operator<<(ostream& out,CuckooHash<DataType2,Conversion2>& Hash)
{
for(int i=0;i<Hash.M1;i++)
if(Hash.Hash1[i] != *(Hash.null))
out << Hash.Hash1[i] << " ";
for(int i=0;i<Hash.M2;i++)
if(Hash.Hash2[i] != *(Hash.null))
out << Hash.Hash2[i] << " ";
return out;
}
struct conversion {
int operator() (const int& x)
{ return x; }
} ;
struct conversion2 {
int operator() (const double& x)
{ return (int)(x*10000); }
} ;
struct conversion3 {
int operator() (const string& x)
{
int a = 1;
for(int i=0;i<x.size();i++)
a = a*26+x[i];
return a;
}
};
int main()
{
ifstream f("hashuri.in");
ofstream g("hashuri.out");
int N,op,a;
CuckooHash<int,conversion> Hash;
f >> N;
for(int i=1;i<=N;i++)
{
f >> op >> a;
switch(op)
{
case 1 : Hash += a;
break;
case 2 : Hash -= a;
break;
case 3 : g << (Hash<a) << "\n";
}
}
f.close();
g.close();
/* ifstream f("fisier.txt");
ofstream g("fisier2.txt");
CuckooHash<int,conversion> Hash;
CuckooHash<double,conversion2> Hash2;
CuckooHash<string,conversion3> Hash3;
f >> Hash3;
cout << Hash3;
f.close();*/
}