//+------------------------------------------------------------------+ //| Main.mqh | //| Copyright 2026, Niquel Mendoza. | //| https://www.mql5.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza." #property link "https://www.mql5.com/" #property strict #ifndef PERFECTHASHBYLEO_SRC_MAIN_MQH #define PERFECTHASHBYLEO_SRC_MAIN_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "Defines.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template class CPerfectHashByLeo { public: // Solo lectura int m_final_table_size; // Tamaño de la tabla final (valores) [m] int m_buckets_size; // Tamaño de buckets inciales private: //--- Parametros ulong m_max_attems; // Maximo valor de la semilla (intentos.. casi) //--- Hashesers bool m_delete_hashers; CPerfectHashHasher* m_hasher_0; CPerfectHashHasherWSeed* m_hasher_1; //--- Arrays internos long m_buckets[]; // Buckets (size=m_buckets_size) bool m_ocupped[]; // Ocuapdos en la tabla final (size=m_final_table_size) int m_slots_oc_buckket[TSN_PHBL_MAX_SIZE_BUCKETS]; // Slots ocupados por buckets.. // Buckets count struct BucketPos { int array_pos[TSN_PHBL_MAX_SIZE_BUCKETS]; }; BucketPos m_pos[]; int m_pos_size; int m_pos_reserve; //--- int m_table_size; //--- void Sort(int left, int right); public: CPerfectHashByLeo(void); ~CPerfectHashByLeo(void); //--- Getters\Setters // Delete hashers void DeleteHashers(bool v) { m_delete_hashers = v; } __forceinline bool DeleteHashers() const { return m_delete_hashers; } // Hash 0 CPerfectHashHasher* Hasher0() { return m_hasher_0; } void Hasher0(CPerfectHashHasher* v) { m_hasher_0 = v; } // Hash 1 CPerfectHashHasherWSeed* Hasher1() { return m_hasher_1; } void Hasher1(CPerfectHashHasherWSeed* v) { m_hasher_1 = v; } // Max attemps void MaxValSeed(ulong v) { m_max_attems = v; } __forceinline ulong MaxValSeed() { return m_max_attems; } //--- Final getters __forceinline int BuckestSize() const { return m_buckets_size; } __forceinline int FinalTableSize() const { return m_final_table_size; } //--- // Completo void InitAlg(const int size_keys, double factor_f, int elementos_por_bucket); // Mas bajo nivel void InitAlg(const int bucket_size, const int final_table_size); //--- Run bool Run(const TKey& keys[], ulong& out_seeds[]); template bool RunWValue(const TKey & keys[], const TValue& in_value[], ulong & out_seeds[], TValue& out_value[], TKey& out_keys[]); // Tamaño de out_seeds = m_buckets_size }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template CPerfectHashByLeo::CPerfectHashByLeo() : m_hasher_0(NULL), m_hasher_1(NULL), m_delete_hashers(false), m_final_table_size(0), m_buckets_size(0), m_max_attems(1500) { //--- m_pos_reserve = 16; // Solo para 16 buckets.. luego crece.. ArrayResize(m_pos, m_pos_reserve, m_pos_reserve); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template CPerfectHashByLeo::~CPerfectHashByLeo() { if(m_delete_hashers) { if(m_hasher_0 != NULL) delete m_hasher_0; if(m_hasher_1 != NULL) delete m_hasher_1; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template void CPerfectHashByLeo::Sort(int left, int right) { //--- if(left >= right) return; //--- int pivotIndex = (left + right) >> 1; int pivotValue = TSN_PHBL_GET_COUNT(m_buckets[pivotIndex]); int i = left, j = right; while(i <= j) { while(TSN_PHBL_GET_COUNT(m_buckets[i]) > pivotValue) i++; while(TSN_PHBL_GET_COUNT(m_buckets[j]) < pivotValue) j--; if(i <= j) { long temp = m_buckets[i]; m_buckets[i] = m_buckets[j]; m_buckets[j] = temp; i++; j--; } } //--- Sort(left, j); Sort(i, right); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template void CPerfectHashByLeo::InitAlg(const int size_keys, double factor_f, int elementos_por_bucket) { //--- Prev const int prev_bucket_size = m_buckets_size; const int prev_fs = m_final_table_size; //--- Calc m_buckets_size = int(size_keys / (double)elementos_por_bucket); m_final_table_size = int(size_keys / factor_f); //--- Resize if(m_buckets_size >= prev_bucket_size) { ArrayResize(m_buckets, m_buckets_size, m_buckets_size); } if(m_final_table_size >= prev_fs) { ArrayResize(m_ocupped, m_final_table_size, m_final_table_size); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template void CPerfectHashByLeo::InitAlg(const int bucket_size, const int final_table_size) { //--- Prev const int prev_bucket_size = m_buckets_size; const int prev_fs = m_final_table_size; //--- Calc m_buckets_size = bucket_size; m_final_table_size = final_table_size; //--- Resize if(bucket_size >= prev_bucket_size) { ArrayResize(m_buckets, m_buckets_size, m_buckets_size); } if(final_table_size >= prev_fs) { ArrayResize(m_ocupped, m_final_table_size, m_final_table_size); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template bool CPerfectHashByLeo::Run(const TKey & keys[], ulong & out_seeds[]) { //--- m_table_size = ArraySize(keys); // Tamaño real de keys... //--- Inicializacion // Arrays check if(m_table_size >= m_final_table_size) { m_final_table_size = m_table_size; ArrayResize(m_ocupped, m_final_table_size, m_final_table_size); } // Out seed ArrayResize(out_seeds, m_buckets_size); // Pos m_pos_size = 0; //--- Relleno for(int i = 0; i < m_buckets_size; i++) { m_buckets[i] = long(0x7FFFF) << TSN_PHBL_BIT_START_I_BUCKET | long(i) << TSN_PHBL_BIT_START_POS_I; } ArrayInitialize(m_ocupped, false); ArrayInitialize(m_slots_oc_buckket, -1); //--- Hash inicial // 12 bit = num | 16 bit = bucket start | 32 bit (posicion en array pos de este key) for(int i = 0; i < m_table_size; i++) { const int p = int(m_hasher_0.Hash(keys[i]) % m_buckets_size); const long lv = m_buckets[p]; int sib = TSN_PHBL_GET_INDEX(lv); if(sib != 0x7FFFF) { sib = m_pos_size++; // Obtenemos nuevo indice.. if(m_pos_size >= m_pos_reserve) { m_pos_reserve += (m_pos_size >> 1) + 1; // 50 % mas.. ArrayResize(m_pos, m_pos_reserve, m_pos_reserve); } } //--- int curr = TSN_PHBL_GET_COUNT(lv); // Tamaño actual m_pos[sib].array_pos[curr++] = i; // Nueva posicion a este array (vista) //--- Final empaquetamos todo m_buckets[p] = TSN_PHBL_EMPAQUETAR(curr, sib, TSN_PHBL_GET_POS(lv)); } //--- Ordenamos buckets.. Sort(0, m_buckets_size - 1); //--- Algoritmo... for(int bi = 0; bi < m_buckets_size; bi++) { //--- const long full_v = m_buckets[bi]; const int bucket_size = TSN_PHBL_GET_COUNT(full_v); if(bucket_size < 1) continue; //--- const int start_pos = TSN_PHBL_GET_POS(full_v); const int pos_index = TSN_PHBL_GET_INDEX(full_v); //--- Iteramos por este bucket... ulong d = 0; while(true) { bool ok = true; //--- for(int k = 0; k < bucket_size && ok; k++) { //--- const int key_index = m_pos[pos_index].array_pos[k]; // Indice de la clave // Posicion final.. const int pos = int(m_hasher_1.Hash(keys[key_index], d) % m_final_table_size); //--- Verificar si este slot ya se ocupa if(m_ocupped[pos]) { ok = false; break; } //--- for(int j = 0; j < k; j++) { if(m_slots_oc_buckket[j] == pos) { ok = false; break; } } //--- m_slots_oc_buckket[k] = pos; } //--- if(ok) { out_seeds[start_pos] = d; // guardar seed for(int k = 0; k < bucket_size; k++) m_ocupped[m_slots_oc_buckket[k]] = true; // marcar ocupadas break; // Salimos.. } //--- d++; if(d >= m_max_attems) { return false; } } } //--- return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Notas: // out_value y out keys requieren iniclaicion previa con el tamaño de final tabla size.. template template bool CPerfectHashByLeo::RunWValue(const TKey & keys[], const TValue& in_value[], ulong & out_seeds[], TValue& out_value[], TKey& out_keys[]) { // tamaño de in_value = keys //--- m_table_size = ArraySize(keys); // Tamaño real de keys... //--- Inicializacion // Arrays check if(m_table_size >= m_final_table_size) { m_final_table_size = m_table_size; ArrayResize(m_ocupped, m_final_table_size, m_final_table_size); } // Out seed ArrayResize(out_seeds, m_buckets_size); ArrayInitialize(out_seeds, 0); // ArrayResize(out_value, m_final_table_size); // Pos m_pos_size = 0; //--- Relleno for(int i = 0; i < m_buckets_size; i++) { m_buckets[i] = long(0x7FFFF) << TSN_PHBL_BIT_START_I_BUCKET | long(i) << TSN_PHBL_BIT_START_POS_I; } ArrayInitialize(m_ocupped, false); ArrayInitialize(m_slots_oc_buckket, -1); //--- Hash inicial // 12 bit = num | 16 bit = bucket start | 32 bit (posicion en array pos de este key) for(int i = 0; i < m_table_size; i++) { const int p = int(m_hasher_0.Hash(keys[i]) % (ulong)m_buckets_size); const long lv = m_buckets[p]; int sib = TSN_PHBL_GET_INDEX(lv); if(sib == 0x7FFFF) // Si aun no tiene inciado su sib.. le otorgamos uno.. { sib = m_pos_size++; // Obtenemos nuevo indice.. if(m_pos_size >= m_pos_reserve) { m_pos_reserve += (m_pos_size >> 1) + 1; // 50 % mas.. ArrayResize(m_pos, m_pos_reserve, m_pos_reserve); } } //--- int curr = TSN_PHBL_GET_COUNT(lv); // Tamaño actual m_pos[sib].array_pos[curr++] = i; // Nueva posicion a este array (vista) //--- Final empaquetamos todo m_buckets[p] = TSN_PHBL_EMPAQUETAR(curr, sib, TSN_PHBL_GET_POS(lv)); } //--- Ordenamos buckets.. Sort(0, m_buckets_size - 1); //--- Algoritmo... for(int bi = 0; bi < m_buckets_size; bi++) { //--- const long full_v = m_buckets[bi]; const int bucket_size = TSN_PHBL_GET_COUNT(full_v); if(bucket_size < 1) continue; //--- const int start_pos = TSN_PHBL_GET_POS(full_v); const int pos_index = TSN_PHBL_GET_INDEX(full_v); //--- Iteramos por este bucket... ulong d = 0; while(true) { bool ok = true; //--- for(int k = 0; k < bucket_size && ok; k++) { //--- const int key_index = m_pos[pos_index].array_pos[k]; // Indice de la clave //Print("bi=", bi, " pos_index=", pos_index, " bucket_size=", bucket_size, // " k=", k, " key_index=", key_index, " table_size=", m_table_size); // Posicion final.. const int pos = int(m_hasher_1.Hash(keys[key_index], d) % (ulong)m_final_table_size); //--- Verificar si este slot ya se ocupa if(m_ocupped[pos]) { ok = false; break; } //--- for(int j = 0; j < k; j++) { if(m_slots_oc_buckket[j] == pos) { ok = false; break; } } //--- m_slots_oc_buckket[k] = pos; } //--- if(ok) { out_seeds[start_pos] = d; // guardar seed for(int k = 0; k < bucket_size; k++) { const int ra = m_slots_oc_buckket[k]; m_ocupped[ra] = true; // marcar ocupadas const int fi = m_pos[pos_index].array_pos[k]; out_value[ra] = in_value[fi]; out_keys[ra] = keys[fi]; } break; // Salimos.. } //--- d++; if(d >= m_max_attems) { return false; } } } //--- return true; } //--- End namespace } //+------------------------------------------------------------------+ #endif // PERFECTHASHBYLEO_SRC_MAIN_MQH