//+------------------------------------------------------------------+ //| HashSet.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include #include #include #include //+------------------------------------------------------------------+ //| Struct Slot. | //| Usage: Internal structure for organization CHashSet. | //+------------------------------------------------------------------+ template struct Slot { public: int hash_code; T value; int next; Slot(void): hash_code(0),value((T)NULL),next(0) {} }; //+------------------------------------------------------------------+ //| Class CHashSet. | //| Usage: Represents a set of unique values. | //+------------------------------------------------------------------+ template class CHashSet: public ISet { protected: int m_buckets[]; Slot m_slots[]; int m_count; int m_last_index; int m_free_list; IEqualityComparer*m_comparer; bool m_delete_comparer; public: CHashSet(void); CHashSet(IEqualityComparer*comparer); CHashSet(ICollection*collection); CHashSet(ICollection*collection,IEqualityComparer*comparer); CHashSet(T &array[]); CHashSet(T &array[],IEqualityComparer*comparer); ~CHashSet(void); //--- methods of filling data bool Add(T value); //--- methods of access to protected data int Count(void) { return(m_count); } IEqualityComparer* Comparer(void) const { return(m_comparer); } bool Contains(T item); void TrimExcess(void); //--- methods of copy data from collection int CopyTo(T &ds_array[],const int dst_start=0); //--- methods of cleaning and deleting void Clear(void); bool Remove(T item); //--- methods of changing sets void ExceptWith(ICollection*collection); void ExceptWith(T &array[]); void IntersectWith(ICollection*collection); void IntersectWith(T &array[]); void SymmetricExceptWith(ICollection*collection); void SymmetricExceptWith(T &array[]); void UnionWith(ICollection*collection); void UnionWith(T &array[]); //--- methods for determining the relationship between sets bool IsProperSubsetOf(ICollection*collection); bool IsProperSubsetOf(T &array[]); bool IsProperSupersetOf(ICollection*collection); bool IsProperSupersetOf(T &array[]); bool IsSubsetOf(ICollection*collection); bool IsSubsetOf(T &array[]); bool IsSupersetOf(ICollection*collection); bool IsSupersetOf(T &array[]); bool Overlaps(ICollection*collection); bool Overlaps(T &array[]); bool SetEquals(ICollection*collection); bool SetEquals(T &array[]); private: void SetCapacity(const int new_size,bool new_hash_codes); bool AddIfNotPresent(T value); void Initialize(const int capacity); void InternalSymmetricExceptWith(CHashSet*set); bool InternalIsSubsetOf(CHashSet*set); bool InternalIsSupersetOf(CHashSet*set); bool InternalIsProperSubsetOf(CHashSet*set); bool InternalIsProperSupersetOf(CHashSet*set); }; //+------------------------------------------------------------------+ //| Initializes a new instance of the CHashSet class that is empty| //| and uses the default equality comparer for the set type. | //+------------------------------------------------------------------+ template CHashSet::CHashSet(void): m_count(0), m_last_index(0), m_free_list(-1) { //--- use default equality comaprer m_comparer=new CDefaultEqualityComparer(); m_delete_comparer=true; } //+------------------------------------------------------------------+ //| Initializes a new instance of the CHashSet class that is empty| //| and uses the specified equality comparer for the set type. | //+------------------------------------------------------------------+ template CHashSet::CHashSet(IEqualityComparer*comparer): m_count(0), m_last_index(0), m_free_list(-1) { //--- check equality comaprer if(CheckPointer(comparer)==POINTER_INVALID) { //--- use default equality comaprer m_comparer=new CDefaultEqualityComparer(); m_delete_comparer=true; } else { //--- use specified equality comaprer m_comparer=comparer; m_delete_comparer=false; } } //+------------------------------------------------------------------+ //| Initializes a new instance of the CHashSet class that uses the| //| default equality comparer for the set type, contains elements | //| copied from the specified collection, and has sufficient capacity| //| to accommodate the number of elements copied. | //+------------------------------------------------------------------+ template CHashSet::CHashSet(ICollection*collection): m_count(0), m_last_index(0), m_free_list(-1) { //--- use default equality comaprer m_comparer=new CDefaultEqualityComparer(); m_delete_comparer=true; //--- check collection if(CheckPointer(collection)==POINTER_INVALID) return; //--- set capacity for elements of the collection int count=collection.Count(); Initialize(count); //--- add element from collection to the set this.UnionWith(collection); if((m_count==0 && ArraySize(m_slots)>3) || (m_count>0 && ArraySize(m_slots)/m_count>3)) TrimExcess(); } //+------------------------------------------------------------------+ //| Initializes a new instance of the CHashSet class that uses the| //| specified equality comparer for the set type, contains elements | //| copied from the specified collection, and has sufficient capacity| //| to accommodate the number of elements copied. | //+------------------------------------------------------------------+ template CHashSet::CHashSet(ICollection*collection,IEqualityComparer*comparer): m_count(0), m_last_index(0), m_free_list(-1) { //--- check equality comaprer if(CheckPointer(comparer)==POINTER_INVALID) { //--- use default equality comaprer m_comparer=new CDefaultEqualityComparer(); m_delete_comparer=true; } else { //--- use specified comaprer m_comparer=comparer; m_delete_comparer=false; } //--- check collection if(CheckPointer(collection)==POINTER_INVALID) return; //--- set capacity for elements of the collection int count=collection.Count(); Initialize(count); //--- add element from collection to the set this.UnionWith(collection); if((m_count==0 && ArraySize(m_slots)>3) || (m_count>0 && ArraySize(m_slots)/m_count>3)) TrimExcess(); } //+------------------------------------------------------------------+ //| Initializes a new instance of the CHashSet class that uses the| //| default equality comparer for the set type, contains elements | //| copied from the specified array, and has sufficient capacity to | //| accommodate the number of elements copied. | //+------------------------------------------------------------------+ template CHashSet::CHashSet(T &array[]): m_count(0), m_last_index(0), m_free_list(-1) { //--- use default equality comaprer m_comparer=new CDefaultEqualityComparer(); m_delete_comparer=true; //--- set capacity for elements of the array int count=ArraySize(array); Initialize(count); //--- add element from array to the set this.UnionWith(array); if((m_count==0 && ArraySize(m_slots)>3) || (m_count>0 && ArraySize(m_slots)/m_count>3)) TrimExcess(); } //+------------------------------------------------------------------+ //| Initializes a new instance of the CHashSet class that uses the| //| specified equality comparer for the set type, contains elements | //| copied from the specified array, and has sufficient capacity to | //| accommodate the number of elements copied. | //+------------------------------------------------------------------+ template CHashSet::CHashSet(T &array[],IEqualityComparer*comparer): m_count(0), m_last_index(0), m_free_list(-1) { //--- check equality comaprer if(CheckPointer(comparer)==POINTER_INVALID) { //--- use default equality comaprer m_comparer=new CDefaultEqualityComparer(); m_delete_comparer=true; } else { //--- use specified comaprer m_comparer=comparer; m_delete_comparer=false; } //--- set capacity for elements of the array int count=ArraySize(array); Initialize(count); //--- add element from array to the set this.UnionWith(array); if((m_count==0 && ArraySize(m_slots)>3) || (m_count>0 && ArraySize(m_slots)/m_count>3)) TrimExcess(); } //+------------------------------------------------------------------+ //| Destructor. | //+------------------------------------------------------------------+ template CHashSet::~CHashSet(void) { if(m_delete_comparer) delete m_comparer; } //+------------------------------------------------------------------+ //| Adds the specified element to a set. | //+------------------------------------------------------------------+ template bool CHashSet::Add(T value) { return AddIfNotPresent(value); } //+------------------------------------------------------------------+ //| Determines whether a set contains the specified element. | //+------------------------------------------------------------------+ template bool CHashSet::Contains(T item) { //--- check buckets if(ArraySize(m_buckets)!=0) { //--- get hash code for item int hash_code=m_comparer.HashCode(item)&0x7FFFFFFF; //--- search item in the slots for(int i=m_buckets[hash_code%ArraySize(m_buckets)]-1; i>=0; i=m_slots[i].next) if(m_slots[i].hash_code==hash_code && m_comparer.Equals(m_slots[i].value,item)) return(true); } return(false); } //+------------------------------------------------------------------+ //| Sets the capacity of a set to the actual number of elements it | //| contains, rounded up to a nearby, implementation-specific value. | //+------------------------------------------------------------------+ template void CHashSet::TrimExcess(void) { if(m_count==0) { ArrayFree(m_buckets); ArrayFree(m_slots); } else { //--- calculate min prime size for current count int new_size=CPrimeGenerator::GetPrime(m_count); //--- resize buckets and slots ArrayResize(m_slots,new_size); ArrayResize(m_buckets,new_size); //--- restore buckets and slots int new_index=0; for(int i=0; i=0) { m_slots[new_index]=m_slots[i]; //--- rehash int bucket=m_slots[new_index].hash_code%new_size; m_slots[new_index].next=m_buckets[bucket]-1; m_buckets[bucket]=new_index+1; //--- increment index new_index++; } } m_last_index=new_index; m_free_list=-1; } } //+------------------------------------------------------------------+ //| Copies a range of elements from the set to a compatible | //| one-dimensional array. | //+------------------------------------------------------------------+ template int CHashSet::CopyTo(T &dst_array[],const int dst_start) { //--- resize array if(dst_start+m_count>ArraySize(dst_array)) ArrayResize(dst_array,dst_start+m_count); //--- start copy int index=0; for(int i=0; i=0) { if(dst_start+index>=ArraySize(dst_array) || index>=m_count) return(index); dst_array[dst_start+index++]=m_slots[i].value; } return(index); } //+------------------------------------------------------------------+ //| Removes all elements from a set. | //+------------------------------------------------------------------+ template void CHashSet::Clear(void) { if(m_last_index>0) { ArrayFree(m_slots); ArrayFree(m_buckets); m_last_index=0; m_count=0; m_free_list=-1; } } //+------------------------------------------------------------------+ //| Removes the specified element from a set. | //+------------------------------------------------------------------+ template bool CHashSet::Remove(T item) { if(ArraySize(m_buckets)!=0) { //--- get hash code for item int hash_code=m_comparer.HashCode(item)&0x7FFFFFFF; int bucket=hash_code%ArraySize(m_buckets); int last=-1; //--- search item for(int i=m_buckets[bucket]-1; i>=0; last=i,i=m_slots[i].next) { if(m_slots[i].hash_code==hash_code && m_comparer.Equals(m_slots[i].value,item)) { if(last<0) m_buckets[bucket]=m_slots[i].next+1; else m_slots[last].next=m_slots[i].next; //--- remove item m_slots[i].hash_code=-1; m_slots[i].value=(T)NULL; m_slots[i].next =m_free_list; //--- decrement count m_count--; if(m_count==0) { m_last_index= 0; m_free_list = -1; } else { m_free_list=i; } return(true); } } } return(false); } //+------------------------------------------------------------------+ //| Removes all elements in the specified collection from the current| //| set. | //+------------------------------------------------------------------+ template void CHashSet::ExceptWith(ICollection*collection) { //--- check collection if(CheckPointer(collection)==POINTER_INVALID) return; //--- this is already the enpty set if(m_count==0) return; //--- special case if collecion is this //--- a set minus itself is the empty set if(collection==GetPointer(this)) { Clear(); return; } //--- copy collection to array T array[]; collection.CopyTo(array,0); //--- remove every element in collection from this for(int i=0; i void CHashSet::ExceptWith(T &array[]) { //--- this is already the enpty set if(m_count==0) return; //--- remove every element in collection from this for(int i=0; i void CHashSet::IntersectWith(ICollection*collection) { //--- check collection if(CheckPointer(collection)==POINTER_INVALID) return; //--- intersection of anything with empty set is empty set, so return if count is 0 if(m_count==0) return; //--- if collection is empty, intersection is empty set if(collection.Count()==0) { Clear(); return; } //--- intersect for(int i=0; i=0) { T item=m_slots[i].value; if(!collection.Contains(item)) Remove(item); } } } //+------------------------------------------------------------------+ //| Modifies the current set to contain only elements that are | //| present in that object and in the specified array. | //+------------------------------------------------------------------+ template void CHashSet::IntersectWith(T &array[]) { //--- intersection of anything with empty set is empty set, so return if count is 0 if(m_count==0) return; //--- if collection is empty, intersection is empty set if(ArraySize(array)==0) { Clear(); return; } //--- intersect CHashSetset(array); for(int i=0; i=0) { T item=m_slots[i].value; if(!set.Contains(item)) Remove(item); } } } //+------------------------------------------------------------------+ //| Modifies the current set to contain only elements that are | //| present either in that set or in the specified collection, but | //| not both. | //+------------------------------------------------------------------+ template void CHashSet::SymmetricExceptWith(ICollection*collection) { //--- check collection if(CheckPointer(collection)==POINTER_INVALID) return; //--- if set is empty, then symmetric difference is other if(m_count==0) { UnionWith(collection); return; } //--- special case this; the symmetric difference of a set with itself is the empty set if(collection==GetPointer(this)) { Clear(); return; } //--- check collection is set CHashSet*ptr_set=dynamic_cast*>(collection); if(CheckPointer(ptr_set)!=POINTER_INVALID) { InternalSymmetricExceptWith(ptr_set); } else { //--- create a set based on a specified collection CHashSetset(collection); InternalSymmetricExceptWith(GetPointer(set)); } } //+------------------------------------------------------------------+ //| Modifies the current set to contain only elements that are | //| present either in that set or in the specified array, but not | //| both. | //+------------------------------------------------------------------+ template void CHashSet::SymmetricExceptWith(T &array[]) { //--- if set is empty, then symmetric difference is other if(m_count==0) { UnionWith(array); return; } //--- symmetric except CHashSetset(array); InternalSymmetricExceptWith(GetPointer(set)); } //+------------------------------------------------------------------+ //| Modifies the current set to contain all elements that are present| //| in itself, the specified collection, or both. | //+------------------------------------------------------------------+ template void CHashSet::UnionWith(ICollection*collection) { //--- check collection if(CheckPointer(collection)==POINTER_INVALID) return; //--- get array from collection T array[]; collection.CopyTo(array); //--- union array with the current set UnionWith(array); } //+------------------------------------------------------------------+ //| Modifies the current set to contain all elements that are present| //| in itself, the specified array, or both. | //+------------------------------------------------------------------+ template void CHashSet::UnionWith(T &array[]) { for(int i=0; i bool CHashSet::IsProperSubsetOf(ICollection*collection) { //--- check collection if(CheckPointer(collection)==POINTER_INVALID) return(false); //--- the empty set is a proper subset of anything but the empty set if(m_count==0) return(collection.Count()>0); //--- check collection is set CHashSet*ptr_set=dynamic_cast*>(collection); if(CheckPointer(ptr_set)!=POINTER_INVALID) { return InternalIsProperSubsetOf(ptr_set); } else { //--- create a set based on a specified collection CHashSetset(collection); return InternalIsProperSubsetOf(GetPointer(set)); } } //+------------------------------------------------------------------+ //| Determines whether a set is a proper subset of the specified | //| array. | //+------------------------------------------------------------------+ template bool CHashSet::IsProperSubsetOf(T &array[]) { //--- the empty set is a proper subset of anything but the empty set if(m_count==0) return(ArraySize(array)>0); //--- create a set based on a specified array CHashSetset(array); return InternalIsProperSubsetOf(GetPointer(set)); } //+------------------------------------------------------------------+ //| Determines whether a set is a proper superset of the specified | //| collection. | //+------------------------------------------------------------------+ template bool CHashSet::IsProperSupersetOf(ICollection*collection) { //--- check collection if(CheckPointer(collection)==POINTER_INVALID) return(m_count>0); //--- the empty set is a proper subset of anything but the empty set if(m_count==0) return(false); //--- if other is the empty set then this is a superset if(collection.Count()==0) return(true); //--- check collection is set CHashSet*ptr_set=dynamic_cast*>(collection); if(CheckPointer(ptr_set)!=POINTER_INVALID) { return InternalIsProperSupersetOf(ptr_set); } else { //--- create a set based on a specified collection CHashSetset(collection); return InternalIsProperSupersetOf(GetPointer(set)); } } //+------------------------------------------------------------------+ //| Determines whether a set is a proper superset of the specified | //| array. | //+------------------------------------------------------------------+ template bool CHashSet::IsProperSupersetOf(T &array[]) { //--- the empty set is a proper subset of anything but the empty set if(m_count==0) return(false); //--- if other is the empty set then this is a superset if(ArraySize(array)==0) return(true); //--- create a set based on a specified array CHashSetset(array); return InternalIsProperSupersetOf(GetPointer(set)); } //+------------------------------------------------------------------+ //| Determines whether a set is a subset of the specified collection.| //+------------------------------------------------------------------+ template bool CHashSet::IsSubsetOf(ICollection*collection) { if(CheckPointer(collection)==POINTER_INVALID) return(m_count==0); //--- The empty set is a subset of any set if(m_count==0) return(true); //--- check collection is set CHashSet*ptr_set=dynamic_cast*>(collection); if(CheckPointer(ptr_set)!=POINTER_INVALID) { return InternalIsSubsetOf(ptr_set); } else { //--- create a set based on a specified collection CHashSetset(collection); return InternalIsSubsetOf(GetPointer(set)); } } //+------------------------------------------------------------------+ //| Determines whether a set is a subset of the specified array. | //+------------------------------------------------------------------+ template bool CHashSet::IsSubsetOf(T &array[]) { //--- The empty set is a subset of any set if(m_count==0) return(true); //--- create a set based on a specified array CHashSetset(array); return InternalIsSubsetOf(GetPointer(set)); } //+------------------------------------------------------------------+ //| Determines whether a set is a superset of the specified | //| collection. | //+------------------------------------------------------------------+ template bool CHashSet::IsSupersetOf(ICollection*collection) { if(CheckPointer(collection)==POINTER_INVALID) return(m_count>=0); //--- if other is the empty set then this is a superset if(collection.Count()==0) return(true); //--- check collection is set CHashSet*ptr_set=dynamic_cast*>(collection); if(CheckPointer(ptr_set)!=POINTER_INVALID) { return InternalIsSupersetOf(ptr_set); } else { //--- create a set based on a specified collection CHashSetset(collection); return InternalIsSupersetOf(GetPointer(set)); } } //+------------------------------------------------------------------+ //| Determines whether a set is a superset of the specified array. | //+------------------------------------------------------------------+ template bool CHashSet::IsSupersetOf(T &array[]) { //--- if other is the empty set then this is a superset if(ArraySize(array)==0) return(true); //--- create a set based on a specified array CHashSetset(array); return InternalIsSupersetOf(GetPointer(set)); } //+------------------------------------------------------------------+ //| Determines whether the current set and a specified collection | //| share common elements. | //+------------------------------------------------------------------+ template bool CHashSet::Overlaps(ICollection*collection) { //--- check collection if(CheckPointer(collection)==POINTER_INVALID) return(false); //--- check current count if(m_count==0) return(false); //--- get array from collection T array[]; collection.CopyTo(array); //--- check overlaps between current set and array return Overlaps(array); } //+------------------------------------------------------------------+ //| Determines whether the current set and a specified array share | //| common elements. | //+------------------------------------------------------------------+ template bool CHashSet::Overlaps(T &array[]) { //--- check current count if(m_count==0) return(false); //--- try to find any elements from specified array in current set for(int i=0; i bool CHashSet::SetEquals(ICollection*collection) { //--- check collection if(CheckPointer(collection)==POINTER_INVALID) return(false); //--- check current set is equal specified collection if(collection==GetPointer(this)) return(true); //--- get array from collection T array[]; collection.CopyTo(array); //--- check current set is equal specified array return SetEquals(array); } //+------------------------------------------------------------------+ //| Determines whether a set and the specified array contain the same| //| elements. | //+------------------------------------------------------------------+ template bool CHashSet::SetEquals(T &array[]) { //--- check size if(ArraySize(array)!=m_count) return(false); //--- check current set is equal specified array for(int i=0; i void CHashSet::SetCapacity(const int new_size,bool new_hash_codes) { //--- resize slots ArrayResize(m_slots,new_size); //--- restore slots if(new_hash_codes) for(int i=0; i bool CHashSet::AddIfNotPresent(T value) { //--- set minimum capacity if(ArraySize(m_buckets)==0) Initialize(0); //--- get hash code and bucket for value int hash_code=m_comparer.HashCode(value)&0x7FFFFFFF; int bucket=hash_code%ArraySize(m_buckets); //--- check value already in the set for(int i=m_buckets[hash_code%ArraySize(m_buckets)]-1; i>=0; i=m_slots[i].next) if(m_slots[i].hash_code==hash_code && m_comparer.Equals(m_slots[i].value,value)) return(false); //--- calculate index for value int index=0; if(m_free_list>=0) { index=m_free_list; m_free_list=m_slots[index].next; } else { if(m_last_index==ArraySize(m_slots)) { int new_size=CPrimeGenerator::ExpandPrime(m_count); SetCapacity(new_size,false); bucket=hash_code%ArraySize(m_buckets); } index=m_last_index; m_last_index++; } //--- set value m_slots[index].hash_code=hash_code; m_slots[index].value=value; m_slots[index].next=m_buckets[bucket]-1; m_buckets[bucket]=index+1; //--- increase count m_count++; return(true); } //+------------------------------------------------------------------+ //| Initialize set with specified capacity. | //+------------------------------------------------------------------+ template void CHashSet::Initialize(const int capacity) { int size=CPrimeGenerator::GetPrime(capacity); ArrayResize(m_buckets,size); ArrayResize(m_slots,size); ZeroMemory(m_buckets); ZeroMemory(m_slots); } //+------------------------------------------------------------------+ //| Modifies the current set to contain only elements that are | //| present either in that set or in the specified set, but not both.| //+------------------------------------------------------------------+ template void CHashSet::InternalSymmetricExceptWith(CHashSet*set) { for(int i=0; i bool CHashSet::InternalIsSubsetOf(CHashSet*set) { //--- if this has more elements then it can't be a subset if(m_count>set.m_count) return(false); //--- try to find any elements from current set in specified set for(int i=0; i bool CHashSet::InternalIsSupersetOf(CHashSet*set) { //--- if this has less elements then it can't be a superset if(set.m_count>m_count) return(false); //--- try to find any elements from specified set in current set for(int i=0; i bool CHashSet::InternalIsProperSubsetOf(CHashSet*set) { //--- if this has more or equal elements then it can't be a proper subset if(m_count>=set.m_count) return(false); //--- try to find any elements from current set in specified set for(int i=0; i bool CHashSet::InternalIsProperSupersetOf(CHashSet*set) { //--- if this has less or equal elements then it can't be a proper superset if(m_count<=set.m_count) return(false); //--- try to find any elements from specified set in current set for(int i=0; i