00001 //---------------------------------------------------------------------------- 00002 /** @file SgHashTable.h 00003 Hash table. 00004 */ 00005 //---------------------------------------------------------------------------- 00006 00007 #ifndef SG_HASHTABLE_H 00008 #define SG_HASHTABLE_H 00009 00010 #include "SgHash.h" 00011 #include "SgWrite.h" 00012 00013 //---------------------------------------------------------------------------- 00014 00015 /** Entry in a HashTable: code and data */ 00016 template <class DATA> 00017 struct SgHashEntry 00018 { 00019 SgHashEntry() 00020 : m_hash(), 00021 m_data() 00022 { } 00023 00024 SgHashEntry(const SgHashCode& code, const DATA& data) 00025 : m_hash(code), 00026 m_data(data) 00027 { } 00028 00029 SgHashCode m_hash; 00030 00031 DATA m_data; 00032 }; 00033 00034 //---------------------------------------------------------------------------- 00035 00036 /** HashTable implements an array of DATA */ 00037 template <class DATA> 00038 class SgHashTable 00039 { 00040 public: 00041 /** Create a hash table with 'maxHash' entries. */ 00042 explicit SgHashTable(int maxHash); 00043 00044 ~SgHashTable(); 00045 00046 /** Leaves the positions in the hash table, but set all depths to zero, so 00047 that only the best move is valid, not the value. The hash entries will 00048 easily be replaced by fresh information. 00049 */ 00050 void Age(); 00051 00052 /** Clear the hash table by marking all entries as invalid. */ 00053 void Clear(); 00054 00055 /** Return true and the data stored under that code, or false if 00056 none stored. 00057 */ 00058 bool Lookup(const SgHashCode& code, DATA* data) const; 00059 00060 /** Size of hash table. */ 00061 int MaxHash() const; 00062 00063 /** Try to store 'data' under the hash code 'code'. 00064 Return whether the data was stored. The only reason for not storing 00065 it would be some 'better' data already hashing to the same hash code. 00066 */ 00067 bool Store(const SgHashCode& code, const DATA& data); 00068 00069 /** number of collisions on store */ 00070 int NuCollisions() const 00071 { 00072 return m_nuCollisions; 00073 } 00074 00075 /** total number of stores attempted */ 00076 int NuStores() const 00077 { 00078 return m_nuStores; 00079 } 00080 00081 /** total number of lookups attempted */ 00082 int NuLookups() const 00083 { 00084 return m_nuLookups; 00085 } 00086 00087 /** number of successful lookups */ 00088 int NuFound() const 00089 { 00090 return m_nuFound; 00091 } 00092 00093 private: 00094 /** complete hash code for each entry */ 00095 SgHashEntry<DATA>* m_entry; 00096 00097 /** size of hash table */ 00098 int m_maxHash; 00099 00100 // AR: the following statistics can be made debug only 00101 // AR: pass a HashStatistics class to the HashTable when constructed 00102 00103 /** number of collisions on store */ 00104 mutable int m_nuCollisions; 00105 00106 /** total number of stores attempted */ 00107 mutable int m_nuStores; 00108 00109 /** total number of lookups attempted */ 00110 mutable int m_nuLookups; 00111 00112 /** number of successful lookups */ 00113 mutable int m_nuFound; 00114 00115 /** not implemented */ 00116 SgHashTable(const SgHashTable&); 00117 00118 /** not implemented */ 00119 SgHashTable& operator=(const SgHashTable&); 00120 }; 00121 00122 template <class DATA> 00123 SgHashTable<DATA>::SgHashTable(int maxHash) 00124 : m_entry(0), 00125 m_maxHash(maxHash), 00126 m_nuCollisions(0), 00127 m_nuStores(0), 00128 m_nuLookups(0), 00129 m_nuFound(0) 00130 { 00131 m_entry = new SgHashEntry<DATA>[m_maxHash]; 00132 Clear(); 00133 } 00134 00135 template <class DATA> 00136 SgHashTable<DATA>::~SgHashTable() 00137 { 00138 delete[] m_entry; 00139 } 00140 00141 template <class DATA> 00142 void SgHashTable<DATA>::Age() 00143 { 00144 for (int i = m_maxHash-1; i >= 0; --i) 00145 { 00146 m_entry[i].m_data.AgeData(); 00147 } 00148 } 00149 00150 template <class DATA> 00151 void SgHashTable<DATA>::Clear() 00152 { 00153 for (int i = m_maxHash-1; i >= 0; --i) 00154 { 00155 m_entry[i].m_data.Invalidate(); 00156 } 00157 } 00158 00159 template <class DATA> 00160 int SgHashTable<DATA>::MaxHash() const 00161 { 00162 return m_maxHash; 00163 } 00164 00165 template <class DATA> 00166 bool SgHashTable<DATA>::Store(const SgHashCode& code, const DATA& data) 00167 { 00168 ++m_nuStores; 00169 int h = code.Hash(m_maxHash); 00170 SgHashEntry<DATA>& entry = m_entry[h]; 00171 if (entry.m_data.IsValid() && code != entry.m_hash) 00172 ++m_nuCollisions; 00173 if (! entry.m_data.IsValid() || data.IsBetterThan(entry.m_data)) 00174 { 00175 entry.m_hash = code; 00176 entry.m_data = data; 00177 return true; 00178 } 00179 return false; 00180 } 00181 00182 template <class DATA> 00183 bool SgHashTable<DATA>::Lookup(const SgHashCode& code, DATA* data) const 00184 { 00185 ++m_nuLookups; 00186 int h = code.Hash(m_maxHash); 00187 const SgHashEntry<DATA>& entry = m_entry[h]; 00188 if (entry.m_data.IsValid() && entry.m_hash == code) 00189 { 00190 *data = entry.m_data; 00191 ++m_nuFound; 00192 return true; 00193 } 00194 return false; 00195 } 00196 00197 //---------------------------------------------------------------------------- 00198 00199 /** Writes statistics on hash table use (not the content) */ 00200 template <class DATA> 00201 std::ostream& operator<<(std::ostream& out, const SgHashTable<DATA>& hash) 00202 { 00203 out << "HashTableStatistics:\n" 00204 << SgWriteLabel("Stores") << hash.NuStores() << '\n' 00205 << SgWriteLabel("LookupAttempt") << hash.NuLookups() << '\n' 00206 << SgWriteLabel("LookupSuccess") << hash.NuFound() << '\n' 00207 << SgWriteLabel("Collisions") << hash.NuCollisions() << '\n'; 00208 return out; 00209 } 00210 00211 #endif // SG_HASHTABLE_H