//+------------------------------------------------------------------+ //| Main.mqh | //| Copyright 2026,Niquel Mendoza. | //| https://www.mql5.com/en/users/nique_372 | //+------------------------------------------------------------------+ #property copyright "Copyright 2026,Niquel Mendoza." #property link "https://www.mql5.com/en/users/nique_372" #property strict #ifndef FASTCOLLECTIONSBYLEO_COLHASH_DICT_MAIN_MQH #define FASTCOLLECTIONSBYLEO_COLHASH_DICT_MAIN_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "Defines.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #define MQLARTICES_DICT_R_SIZE (4*MQLARTICLES_DICT_SLOT_SIZE) #define MQLARTICLES_DICT_BIT_START_BOOL_V (60) #define MQLARTICLES_DICT_BIT_START_SALTAR (52) #define MQLARTICLES_DICT_BIT_END_C_TYPE (20) #define MQLARTICLES_DICT_BIT_END_R_TYPE (4) //--- Mascaras (en 'long' para evitar overflow de literal 32bit) #define MQLARTICLES_DICT_MASK_R_TYPE (0xF) //--- #define MQLARTICLES_DICT_TYPE(ref) ENUM_MQLARTICLES_DICT_DTYPE(ref & MQLARTICLES_DICT_MASK_R_TYPE) #define MQLARTICLES_DICT_CTYPE(ref) short(ref >> MQLARTICLES_DICT_BIT_END_R_TYPE) #define MQLARTICLES_DICT_KEYIDX(ref) int(ref >> MQLARTICLES_DICT_BIT_END_C_TYPE) #define MQLARTICLES_DICT_SALTAR(ref) int(ref >> MQLARTICLES_DICT_BIT_START_SALTAR) #define MQLARTICLES_DICT_BOOLV(ref) bool((ref >> MQLARTICLES_DICT_BIT_START_BOOL_V) & 1) //--- #define MQLARTICLES_DICT_PACK(dtype, ctype, keyidx, saltar) \ ( long(dtype) | \ long(ctype) << MQLARTICLES_DICT_BIT_END_R_TYPE | \ long(keyidx) << MQLARTICLES_DICT_BIT_END_C_TYPE | \ long(saltar) << MQLARTICLES_DICT_BIT_START_SALTAR ) #define MQLARTICLES_DICT_PACK_BOOL(ctype, keyidx, saltar, boolval) \ ( MQLARTICLES_DICT_PACK(MQLARTICLES_DICT_DTYPE_BOOLEAN, ctype, keyidx, saltar) | \ (long(boolval) << MQLARTICLES_DICT_BIT_START_BOOL_V) ) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum ENUM_MQLARTICLES_DICT_DTYPE { MQLARTICLES_DICT_DTYPE_STRING, MQLARTICLES_DICT_DTYPE_REAL, MQLARTICLES_DICT_DTYPE_INTEGER, MQLARTICLES_DICT_DTYPE_BOOLEAN, }; #define MQLARTICLES_DICT_RESERVE_STR(R) \ if(m_strings_size + (R) >= m_strings_reserve) \ { \ m_strings_reserve += ((R) << 1) + 8; \ ArrayResize(m_strings, m_strings_reserve); \ } \ //--- // General (ref=[4bit=RealType|16Bit=CType|KeyIndex=32Bit|ASaltar=8Bit])([64bit=KeyHash]) // Integer ocupa un slot mas (total 3): [64bit=Number] // Real ocupa un slot mas (total 3): [64bit=Real Number] // String ocupa un slot mas (total 3): [64bit=String index] // Bool no ocupa otro slot.. sin oque aprovecha el slot 0 (total 2): [*ref|1bit=bool_value) // // "ASaltar" = tamano en slots de la entrada (2 para bool, 3 para el resto). // Se usa para recorrer el bucket saltando entradas completas (i += saltar) // y tambien para saber cuantos slots copiar/mover en Resize()/Remove(). //+------------------------------------------------------------------+ //| Clase CDict | //+------------------------------------------------------------------+ namespace TSN { class CDict { protected: //--- string m_strings[]; int m_strings_size; int m_strings_reserve; //--- struct DictBucket { long slots[MQLARTICES_DICT_R_SIZE]; int pos; // slots ocupados (no numero de entradas) }; //--- union UDoubleLong { double d; long l; }; //--- DictBucket m_buckets[]; int m_buckets_size; int m_count; //--- Resize void Resize(); template void Assing(const TKey& key[], int i, const int end, string& out); public: CDict(void) { Init(64, 4); } ~CDict(void) {} //--- Init void Init(int buckets_size, int slots_reserve); //--- Set bool Set(const string& key, const string val, short cts_type); template bool Set(const TKey& key[], const int s, const int end, const string val, short cts_type); bool Set(const string& key, const long val); bool Set(const string& key, const bool val); bool Set(const string& key, const double val); //--- Get bool TryGet(const string& key, string& val) const; template bool TryGet(const TKey& key[], const int s, const int end, string& val) const; bool TryGet(const string& key, long& val) const; bool TryGet(const string& key, bool& val) const; bool TryGet(const string& key, double& val) const; //--- Utils bool Contains(const string& key) const; bool Remove(const string& key); void Clear(); __forceinline int Count() const { return m_count; } //--- Get all (values como string para "universal") int GetValues(string& out[], string& keys[]) const; int GetKeys(string& keys[]) const; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CDict::Init(int buckets_size, int slots_reserve) { m_buckets_size = fmax(buckets_size, 32); slots_reserve = fmax(slots_reserve, 4); m_count = 0; ArrayResize(m_buckets, m_buckets_size); for(int i = 0; i < m_buckets_size; i++) { m_buckets[i].pos = 0; } //--- m_strings_size = 0; m_strings_reserve = slots_reserve; ArrayResize(m_strings, m_strings_reserve); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CDict::Resize() { const int new_size = m_buckets_size << 1; DictBucket new_buckets[]; ArrayResize(new_buckets, new_size); for(int i = 0; i < new_size; i++) { new_buckets[i].pos = 0; } for(int i = 0; i < m_buckets_size; i++) { const int t = m_buckets[i].pos; int p = 0; while(p < t) { const long ref = m_buckets[i].slots[p]; const int saltar = MQLARTICLES_DICT_SALTAR(ref); const ulong hash = ulong(m_buckets[i].slots[p + 1]); const uint idx = uint(hash % (ulong)new_size); const int dst = new_buckets[idx].pos; for(int k = 0; k < saltar; k++) new_buckets[idx].slots[dst + k] = m_buckets[i].slots[p + k]; new_buckets[idx].pos += saltar; p += saltar; } } ArraySwap(m_buckets, new_buckets); m_buckets_size = new_size; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template void CDict::Assing(const TKey &key[], int i, const int end, string &out) { out.Truncate((end - i) + 1); int k = 0; for(; i <= end; i++) { out.SetChar(k++, key[i]); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Notas: // - En caso no exista lo crea // - En caso se supera el factor de crecimiento 75% entonces resize al doble bool CDict::Set(const string& key, const string val, short cts_type) { //--- Check size if(m_count > 0 && float(m_count) / float(m_buckets_size) > MQLARTICLES_DICT_FACTOR_CRECIMIENTO) Resize(); //--- Alg ulong hash = FNV_OFFSET_BASIS; const int len = StringLen(key); FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME) //--- Index const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- Linear (recorre entradas completas, no slots sueltos) int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; if(ulong(m_buckets[idx].slots[i + 1]) == hash) { if(MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_STRING) { m_strings[m_buckets[idx].slots[i + 2]] = val; return true; } // Existe con otro tipo: no se soporta cambio de tipo in-place return false; } i += MQLARTICLES_DICT_SALTAR(ref); } //--- if(t + 3 >= MQLARTICES_DICT_R_SIZE) { Resize(); return Set(key, val, cts_type); } //--- Reservar strings (key + value), inline MQLARTICLES_DICT_RESERVE_STR(2) const int string_index = m_strings_size++; m_strings[string_index] = val; const int key_index = m_strings_size++; m_strings[key_index] = key; //--- Bucket m_buckets[idx].slots[t] = MQLARTICLES_DICT_PACK(MQLARTICLES_DICT_DTYPE_STRING, cts_type, key_index, 3); m_buckets[idx].slots[t + 1] = long(hash); m_buckets[idx].slots[t + 2] = string_index; m_buckets[idx].pos += 3; m_count++; return true; } //+------------------------------------------------------------------+ template bool CDict::Set(const TKey& key[], const int s, const int end, const string val, short cts_type) { //--- if(s < 0 || s > end) return false; //--- Check size if(m_count > 0 && float(m_count) / float(m_buckets_size) > MQLARTICLES_DICT_FACTOR_CRECIMIENTO) Resize(); //--- Alg ulong hash = FNV_OFFSET_BASIS; for(int i = s; i <= end; i++) { h ^= (uchar)key[i]; h *= FNV_PRIME; } //--- Index const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- Linear (recorre entradas completas, no slots sueltos) int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; if(ulong(m_buckets[idx].slots[i + 1]) == hash) { if(MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_STRING) { m_strings[m_buckets[idx].slots[i + 2]] = val; return true; } // Existe con otro tipo: no se soporta cambio de tipo in-place return false; } i += MQLARTICLES_DICT_SALTAR(ref); } //--- if(t + 3 >= MQLARTICES_DICT_R_SIZE) { Resize(); return Set(key, s, end, val, cts_type); } //--- Reservar strings (key + value), inline MQLARTICLES_DICT_RESERVE_STR(2) const int string_index = m_strings_size++; m_strings[string_index] = val; const int key_index = m_strings_size++; Assing(key, s, end, m_strings[key_index]); //--- Bucket m_buckets[idx].slots[t] = MQLARTICLES_DICT_PACK(MQLARTICLES_DICT_DTYPE_STRING, cts_type, key_index, 3); m_buckets[idx].slots[t + 1] = long(hash); m_buckets[idx].slots[t + 2] = string_index; m_buckets[idx].pos += 3; m_count++; return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CDict::Set(const string& key, const long val) { if(m_count > 0 && float(m_count) / float(m_buckets_size) > MQLARTICLES_DICT_FACTOR_CRECIMIENTO) Resize(); //--- ulong hash = FNV_OFFSET_BASIS; const int len = StringLen(key); FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME) //--- const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; if(ulong(m_buckets[idx].slots[i + 1]) == hash) { if(MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_INTEGER) { m_buckets[idx].slots[i + 2] = val; return true; } return false; } i += MQLARTICLES_DICT_SALTAR(ref); } //--- if(t + 3 >= MQLARTICES_DICT_R_SIZE) { Resize(); return Set(key, val); } //--- MQLARTICLES_DICT_RESERVE_STR(1) const int key_index = m_strings_size++; m_strings[key_index] = key; //--- m_buckets[idx].slots[t] = MQLARTICLES_DICT_PACK(MQLARTICLES_DICT_DTYPE_INTEGER, 0, key_index, 3); m_buckets[idx].slots[t + 1] = long(hash); m_buckets[idx].slots[t + 2] = val; m_buckets[idx].pos += 3; m_count++; return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CDict::Set(const string& key, const double val) { //--- if(m_count > 0 && float(m_count) / float(m_buckets_size) > MQLARTICLES_DICT_FACTOR_CRECIMIENTO) Resize(); //--- ulong hash = FNV_OFFSET_BASIS; const int len = StringLen(key); FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME) //--- const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- UDoubleLong conv; conv.d = val; //--- int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; if(ulong(m_buckets[idx].slots[i + 1]) == hash) { if(MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_REAL) { m_buckets[idx].slots[i + 2] = conv.l; return true; } return false; } i += MQLARTICLES_DICT_SALTAR(ref); } //--- ; if(t + 3 >= MQLARTICES_DICT_R_SIZE) { Resize(); return Set(key, val); } //--- MQLARTICLES_DICT_RESERVE_STR(1) const int key_index = m_strings_size++; m_strings[key_index] = key; //--- m_buckets[idx].slots[t] = MQLARTICLES_DICT_PACK(MQLARTICLES_DICT_DTYPE_REAL, 0, key_index, 3); m_buckets[idx].slots[t + 1] = long(hash); m_buckets[idx].slots[t + 2] = conv.l; m_buckets[idx].pos += 3; m_count++; return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CDict::Set(const string& key, const bool val) { if(m_count > 0 && float(m_count) / float(m_buckets_size) > MQLARTICLES_DICT_FACTOR_CRECIMIENTO) Resize(); //--- ulong hash = FNV_OFFSET_BASIS; const int len = StringLen(key); FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME) //--- const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; const int saltar = MQLARTICLES_DICT_SALTAR(ref); if(ulong(m_buckets[idx].slots[i + 1]) == hash) { if(MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_BOOLEAN) { m_buckets[idx].slots[i] = MQLARTICLES_DICT_PACK_BOOL(MQLARTICLES_DICT_CTYPE(ref), MQLARTICLES_DICT_KEYIDX(ref), saltar, val); return true; } return false; } i += saltar; } //--- if(t + 2 >= MQLARTICES_DICT_R_SIZE) { Resize(); return Set(key, val); } //--- MQLARTICLES_DICT_RESERVE_STR(1) const int key_index = m_strings_size++; m_strings[key_index] = key; //--- m_buckets[idx].slots[t] = MQLARTICLES_DICT_PACK_BOOL(0, key_index, 2, val); m_buckets[idx].slots[t + 1] = long(hash); m_buckets[idx].pos += 2; m_count++; return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CDict::TryGet(const string& key, string& val) const { //--- ulong hash = FNV_OFFSET_BASIS; const int len = StringLen(key); FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME) const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- UDoubleLong conv; //--- int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; if(ulong(m_buckets[idx].slots[i + 1]) == hash) { switch(MQLARTICLES_DICT_TYPE(ref)) { case MQLARTICLES_DICT_DTYPE_STRING: val = m_strings[m_buckets[idx].slots[i + 2]]; break; case MQLARTICLES_DICT_DTYPE_INTEGER: val = string(m_buckets[idx].slots[i + 2]); break; case MQLARTICLES_DICT_DTYPE_REAL: conv.l = m_buckets[idx].slots[i + 2]; val = string(conv.d); break; case MQLARTICLES_DICT_DTYPE_BOOLEAN: val = MQLARTICLES_DICT_BOOLV(ref) ? "true" : "false"; break; } return true; } i += MQLARTICLES_DICT_SALTAR(ref); } return false; } //+------------------------------------------------------------------+ template bool CDict::TryGet(const TKey& key[], const int s, const int end, string& val) const { //--- if(s < 0 || s > end) return false; //--- Alg ulong hash = FNV_OFFSET_BASIS; for(int i = s; i <= end; i++) { hash ^= (uchar)key[i]; hash *= FNV_PRIME; } //--- const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- UDoubleLong conv; //--- int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; if(ulong(m_buckets[idx].slots[i + 1]) == hash) { switch(MQLARTICLES_DICT_TYPE(ref)) { case MQLARTICLES_DICT_DTYPE_STRING: val = m_strings[m_buckets[idx].slots[i + 2]]; break; case MQLARTICLES_DICT_DTYPE_INTEGER: val = string(m_buckets[idx].slots[i + 2]); break; case MQLARTICLES_DICT_DTYPE_REAL: conv.l = m_buckets[idx].slots[i + 2]; val = string(conv.d); break; case MQLARTICLES_DICT_DTYPE_BOOLEAN: val = MQLARTICLES_DICT_BOOLV(ref) ? "true" : "false"; break; } return true; } i += MQLARTICLES_DICT_SALTAR(ref); } return false; } //+------------------------------------------------------------------+ bool CDict::TryGet(const string& key, long& val) const { //--- ulong hash = FNV_OFFSET_BASIS; const int len = StringLen(key); FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME) const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; if(ulong(m_buckets[idx].slots[i + 1]) == hash && MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_INTEGER) { val = m_buckets[idx].slots[i + 2]; return true; } i += MQLARTICLES_DICT_SALTAR(ref); } return false; } //+------------------------------------------------------------------+ bool CDict::TryGet(const string& key, double& val) const { //--- ulong hash = FNV_OFFSET_BASIS; const int len = StringLen(key); FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME) const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- UDoubleLong conv; //--- int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; if(ulong(m_buckets[idx].slots[i + 1]) == hash && MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_REAL) { conv.l = m_buckets[idx].slots[i + 2]; val = conv.d; return true; } i += MQLARTICLES_DICT_SALTAR(ref); } return false; } //+------------------------------------------------------------------+ bool CDict::TryGet(const string& key, bool& val) const { //--- ulong hash = FNV_OFFSET_BASIS; const int len = StringLen(key); FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME) const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; if(ulong(m_buckets[idx].slots[i + 1]) == hash && MQLARTICLES_DICT_TYPE(ref) == MQLARTICLES_DICT_DTYPE_BOOLEAN) { val = MQLARTICLES_DICT_BOOLV(ref); return true; } i += MQLARTICLES_DICT_SALTAR(ref); } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CDict::Contains(const string& key) const { //--- ulong hash = FNV_OFFSET_BASIS; const int len = StringLen(key); FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME) const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; if(ulong(m_buckets[idx].slots[i + 1]) == hash) return true; i += MQLARTICLES_DICT_SALTAR(ref); } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Notas: // - Se compacta el bucket desplazando el resto de entradas 'saltar' // posiciones a la izquierda (no vale el swap-with-last clasico porque // las entradas tienen tamano variable: 2 slots para bool, 3 para el resto). // - No se liberan los strings huerfanos en m_strings (limitacion conocida, // igual que en la version anterior). bool CDict::Remove(const string& key) { //--- ulong hash = FNV_OFFSET_BASIS; const int len = StringLen(key); FNV1a_64_AsM_Str(key, len, hash, FNV_PRIME) const uint idx = uint(hash % (ulong)m_buckets_size); const int t = m_buckets[idx].pos; //--- int i = 0; while(i < t) { const long ref = m_buckets[idx].slots[i]; const int saltar = MQLARTICLES_DICT_SALTAR(ref); if(ulong(m_buckets[idx].slots[i + 1]) == hash) { for(int k = i + saltar; k < t; k++) m_buckets[idx].slots[k - saltar] = m_buckets[idx].slots[k]; m_buckets[idx].pos -= saltar; m_count--; return true; } i += saltar; } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CDict::Clear() { for(int i = 0; i < m_buckets_size; i++) m_buckets[i].pos = 0; m_count = 0; m_strings_size = 0; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CDict::GetValues(string& out[], string& keys[]) const { //--- int r = ArrayResize(out, m_buckets_size); ArrayResize(keys, r); int f = 0; //--- UDoubleLong conv; //--- for(int i = 0; i < m_buckets_size; i++) { const int t = m_buckets[i].pos; int p = 0; //--- while(p < t) { //--- const long ref = m_buckets[i].slots[p]; const int saltar = MQLARTICLES_DICT_SALTAR(ref); //--- keys[f] = m_strings[MQLARTICLES_DICT_KEYIDX(ref)]; switch(MQLARTICLES_DICT_TYPE(ref)) { case MQLARTICLES_DICT_DTYPE_STRING: out[f] = m_strings[m_buckets[i].slots[p + 2]]; break; case MQLARTICLES_DICT_DTYPE_INTEGER: out[f] = IntegerToString(m_buckets[i].slots[p + 2]); break; case MQLARTICLES_DICT_DTYPE_REAL: conv.l = m_buckets[i].slots[p + 2]; out[f] = DoubleToString(conv.d); break; case MQLARTICLES_DICT_DTYPE_BOOLEAN: out[f] = MQLARTICLES_DICT_BOOLV(ref) ? "true" : "false"; break; } //--- f++; if(f >= r) { r <<= 1; ArrayResize(out, r, r); ArrayResize(keys, r, r); } p += saltar; } } //--- ArrayResize(keys, f); return ArrayResize(out, f); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CDict::GetKeys(string &keys[]) const { //--- int r = ArrayResize(keys, m_buckets_size); ArrayResize(keys, r); int f = 0; //--- for(int i = 0; i < m_buckets_size; i++) { const int t = m_buckets[i].pos; int p = 0; //--- while(p < t) { //--- const long ref = m_buckets[i].slots[p]; //--- keys[f++] = m_strings[MQLARTICLES_DICT_KEYIDX(ref)]; //--- if(f >= r) { r <<= 1; ArrayResize(keys, r, r); } p += MQLARTICLES_DICT_SALTAR(ref); } } //--- return ArrayResize(keys, f); } } //+------------------------------------------------------------------+ #endif // FASTCOLLECTIONSBYLEO_COLHASH_DICT_MAIN_MQH