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