#ifndef FILE_HASHTABL #define FILE_HASHTABL /**************************************************************************/ /* File: hashtabl.hh */ /* Author: Joachim Schoeberl */ /* Date: 01. Jun. 95 */ /**************************************************************************/ namespace netgen { /** Abstract data type HASHTABLE. Hash is done by one INDEX */ class BASE_INDEX_HASHTABLE { protected: /// keys are stored in this table TABLE hash; public: /// BASE_INDEX_HASHTABLE (int size) : hash (size) { }; protected: /// int HashValue (const INDEX & ind) const { return ind % hash.Size() + 1; } /// int Position (int bnr, const INDEX & ind) const; }; /// template class INDEX_HASHTABLE : private BASE_INDEX_HASHTABLE { /// TABLE cont; public: /// inline INDEX_HASHTABLE (int size); /// inline void Set (const INDEX & hash, const T & acont); /// inline const T & Get (const INDEX & ahash) const; /// inline bool Used (const INDEX & ahash) const; /// inline int GetNBags () const; /// inline int GetBagSize (int bnr) const; /// inline void GetData (int bnr, int colnr, INDEX & ahash, T & acont) const; /// inline void PrintMemInfo (ostream & ost) const; }; /// class BASE_INDEX_2_HASHTABLE { protected: /// TABLE hash; public: /// BASE_INDEX_2_HASHTABLE (int size) : hash (size) { }; /// void PrintStat (ostream & ost) const; void BaseSetSize(int s) {hash.SetSize(s);} //protected: /// int HashValue (const INDEX_2 & ind) const { return (ind.I1() + ind.I2()) % hash.Size() + 1; } /// int Position (int bnr, const INDEX_2 & ind) const { for (int i = 1; i <= hash.EntrySize (bnr); i++) if (hash.Get(bnr, i) == ind) return i; return 0; } }; /// template class INDEX_2_HASHTABLE : public BASE_INDEX_2_HASHTABLE { /// TABLE cont; public: /// INDEX_2_HASHTABLE (int size) : BASE_INDEX_2_HASHTABLE (size), cont(size) { ; } /// void SetSize(int s) { cont.SetSize(s); BaseSetSize(s); } /// void Set (const INDEX_2 & ahash, const T & acont) { int bnr = HashValue (ahash); int pos = Position (bnr, ahash); if (pos) cont.Set (bnr, pos, acont); else { hash.Add1 (bnr, ahash); cont.Add1 (bnr, acont); } } /// const T & Get (const INDEX_2 & ahash) const { int bnr = HashValue (ahash); int pos = Position (bnr, ahash); return cont.Get (bnr, pos); } /// bool Used (const INDEX_2 & ahash) const { return Position (HashValue (ahash), ahash) > 0; } /// int GetNBags () const { return cont.Size(); } /// int GetBagSize (int bnr) const { return cont.EntrySize (bnr); } /// void GetData (int bnr, int colnr, INDEX_2 & ahash, T & acont) const { ahash = hash.Get(bnr, colnr); acont = cont.Get(bnr, colnr); } /// void SetData (int bnr, int colnr, const INDEX_2 & ahash, const T & acont) { hash.Set(bnr, colnr, ahash); cont.Set(bnr, colnr, acont); } /// void PrintMemInfo (ostream & ost) const { ost << "Hash: " << endl; hash.PrintMemInfo (ost); ost << "Cont: " << endl; cont.PrintMemInfo (ost); } void DeleteData () { int n = hash.Size(); hash.SetSize (n); cont.SetSize (n); } class Iterator { const INDEX_2_HASHTABLE & ht; int bagnr, pos; public: Iterator (const INDEX_2_HASHTABLE & aht, int abagnr, int apos) : ht(aht), bagnr(abagnr), pos(apos) { ; } int BagNr() const { return bagnr; } int Pos() const { return pos; } void operator++ (int) { // cout << "begin Operator ++: bagnr = " << bagnr << " - pos = " << pos << endl; pos++; while (bagnr < ht.GetNBags() && pos == ht.GetBagSize(bagnr+1)) { pos = 0; bagnr++; } // cout << "end Operator ++: bagnr = " << bagnr << " - pos = " << pos << endl; } bool operator != (int i) const { return bagnr != i; } }; Iterator Begin () const { Iterator it(*this, 0, -1); it++; return it; } int End() const { return GetNBags(); } void GetData (const Iterator & it, INDEX_2 & ahash, T & acont) const { ahash = hash[it.BagNr()][it.Pos()]; acont = cont[it.BagNr()][it.Pos()]; } const INDEX_2 & GetHash (const Iterator & it) const { return hash[it.BagNr()][it.Pos()]; } const T & GetData (const Iterator & it) const { return cont[it.BagNr()][it.Pos()]; } }; template inline ostream & operator<< (ostream & ost, const INDEX_2_HASHTABLE & ht) { for (typename INDEX_2_HASHTABLE::Iterator it = ht.Begin(); it != ht.End(); it++) { ost << ht.GetHash(it) << ": " << ht.GetData(it) << endl; } return ost; } /// class BASE_INDEX_3_HASHTABLE { protected: /// TABLE hash; public: /// BASE_INDEX_3_HASHTABLE (int size) : hash (size) { }; protected: /// int HashValue (const INDEX_3 & ind) const { return (ind.I1() + ind.I2() + ind.I3()) % hash.Size() + 1; } /// int Position (int bnr, const INDEX_3 & ind) const { const INDEX_3 * pi = &hash.Get(bnr, 1); int n = hash.EntrySize(bnr); for (int i = 1; i <= n; ++i, ++pi) { if (*pi == ind) return i; } return 0; } }; /// template class INDEX_3_HASHTABLE : private BASE_INDEX_3_HASHTABLE { /// TABLE cont; public: /// inline INDEX_3_HASHTABLE (int size); /// inline void Set (const INDEX_3 & ahash, const T & acont); /// inline const T & Get (const INDEX_3 & ahash) const; /// inline bool Used (const INDEX_3 & ahash) const; /// inline int GetNBags () const; /// inline int GetBagSize (int bnr) const; /// inline void SetData (int bnr, int colnr, const INDEX_3 & ahash, const T & acont); /// inline void GetData (int bnr, int colnr, INDEX_3 & ahash, T & acont) const; /// returns position, if not existing, will create (create == return 1) inline int PositionCreate (const INDEX_3 & ahash, int & bnr, int & colnr); /// inline void SetSize (int size); /// inline void PrepareSet (const INDEX_3 & ahash); /// inline void AllocateElements (); /// inline void PrintMemInfo (ostream & ost) const; /// inline void DeleteData (); class Iterator { const INDEX_3_HASHTABLE & ht; int bagnr, pos; public: Iterator (const INDEX_3_HASHTABLE & aht, int abagnr, int apos) : ht(aht), bagnr(abagnr), pos(apos) { ; } int BagNr() const { return bagnr; } int Pos() const { return pos; } void operator++ (int) { // cout << "begin Operator ++: bagnr = " << bagnr << " - pos = " << pos << endl; pos++; while (bagnr < ht.GetNBags() && pos == ht.GetBagSize(bagnr+1)) { pos = 0; bagnr++; } // cout << "end Operator ++: bagnr = " << bagnr << " - pos = " << pos << endl; } bool operator != (int i) const { return bagnr != i; } }; Iterator Begin () const { Iterator it(*this, 0, -1); it++; return it; } int End() const { return GetNBags(); } void GetData (const Iterator & it, INDEX_3 & ahash, T & acont) const { ahash = hash[it.BagNr()][it.Pos()]; acont = cont[it.BagNr()][it.Pos()]; } const INDEX_3 & GetHash (const Iterator & it) const { return hash[it.BagNr()][it.Pos()]; } const T & GetData (const Iterator & it) const { return cont[it.BagNr()][it.Pos()]; } }; template inline ostream & operator<< (ostream & ost, const INDEX_3_HASHTABLE & ht) { for (typename INDEX_3_HASHTABLE::Iterator it = ht.Begin(); it != ht.End(); it++) { ost << ht.GetHash(it) << ": " << ht.GetData(it) << endl; } return ost; } /// Closed Hashing HT class BASE_INDEX_CLOSED_HASHTABLE { protected: /// // MoveableArray hash; Array hash; /// int invalid; public: /// BASE_INDEX_CLOSED_HASHTABLE (int size); int Size() const { return hash.Size(); } int UsedPos (int pos) const { return ! (hash.Get(pos) == invalid); } int UsedElements () const; /// int HashValue (const INDEX & ind) const { return (3*ind) % hash.Size() + 1; } int Position (const INDEX & ind) const { int i = HashValue(ind); while (1) { if (hash.Get(i) == ind) return i; if (hash.Get(i) == invalid) return 0; i++; if (i > hash.Size()) i = 1; } } int CalcPositionCosts (const INDEX & ind) const { int i = HashValue(ind); int costs = 1; while (1) { if (hash.Get(i) == ind) return costs; if (hash.Get(i) == invalid) return costs; i++; if (i > hash.Size()) i = 1; costs++; } } // returns 1, if new postion is created int PositionCreate (const INDEX & ind, int & apos) { int i = HashValue (ind); if (hash.Get(i) == ind) { apos = i; return 0; } if (hash.Get(i) == invalid) { hash.Elem(i) = ind; apos = i; return 1; } return PositionCreate2 (ind, apos); } protected: int Position2 (const INDEX & ind) const; int PositionCreate2 (const INDEX & ind, int & apos); void BaseSetSize (int asize); }; template class INDEX_CLOSED_HASHTABLE : public BASE_INDEX_CLOSED_HASHTABLE { /// // MoveableArray cont; Array cont; public: /// INDEX_CLOSED_HASHTABLE (int size) : BASE_INDEX_CLOSED_HASHTABLE(size), cont(size) { ; // cont.SetName ("ind-hashtable, contents"); } void Set (const INDEX & ahash, const T & acont) { int pos; PositionCreate (ahash, pos); hash.Elem(pos) = ahash; cont.Elem(pos) = acont; } const T & Get (const INDEX & ahash) const { int pos = Position (ahash); return cont.Get(pos); } /// bool Used (const INDEX & ahash) const { int pos = Position (ahash); return (pos != 0); } /// inline void SetData (int pos, const INDEX & ahash, const T & acont) { hash.Elem(pos) = ahash; cont.Elem(pos) = acont; } /// void GetData (int pos, INDEX & ahash, T & acont) const { ahash = hash.Get(pos); acont = cont.Get(pos); } /// inline void SetData (int pos, const T & acont) { cont.Elem(pos) = acont; } /// void GetData (int pos, T & acont) const { acont = cont.Get(pos); } /// const T & GetData (int pos) { return cont.Get(pos); } /// inline void SetSize (int size) { BaseSetSize(size); cont.SetSize(size); } /// inline void DeleteData () { SetSize (cont.Size()); } void SetName (const char * aname) { // cont.SetName(aname); // hash.SetName(aname); } }; /// Closed Hashing HT class BASE_INDEX_2_CLOSED_HASHTABLE { protected: /// // MoveableArray hash; Array hash; /// int invalid; public: /// BASE_INDEX_2_CLOSED_HASHTABLE (int size); int Size() const { return hash.Size(); } int UsedPos (int pos) const { return ! (hash.Get(pos).I1() == invalid); } int UsedElements () const; /// int HashValue (const INDEX_2 & ind) const { return (ind.I1() + 71 * ind.I2()) % hash.Size() + 1; } int Position (const INDEX_2 & ind) const { int i = HashValue(ind); while (1) { if (hash.Get(i) == ind) return i; if (hash.Get(i).I1() == invalid) return 0; i++; if (i > hash.Size()) i = 1; } } // returns 1, if new postion is created int PositionCreate (const INDEX_2 & ind, int & apos) { int i = HashValue (ind); if (hash.Get(i) == ind) { apos = i; return 0; } if (hash.Get(i).I1() == invalid) { hash.Elem(i) = ind; apos = i; return 1; } return PositionCreate2 (ind, apos); } protected: /// int Position2 (const INDEX_2 & ind) const; int PositionCreate2 (const INDEX_2 & ind, int & apos); void BaseSetSize (int asize); }; template class INDEX_2_CLOSED_HASHTABLE : public BASE_INDEX_2_CLOSED_HASHTABLE { /// // MoveableArray cont; Array cont; public: /// inline INDEX_2_CLOSED_HASHTABLE (int size); /// inline void Set (const INDEX_2 & ahash, const T & acont); /// inline const T & Get (const INDEX_2 & ahash) const; /// inline bool Used (const INDEX_2 & ahash) const; /// inline void SetData (int pos, const INDEX_2 & ahash, const T & acont); /// inline void GetData (int pos, INDEX_2 & ahash, T & acont) const; /// inline void SetData (int pos, const T & acont); /// inline void GetData (int pos, T & acont) const; /// const T & GetData (int pos) { return cont.Get(pos); } /// inline void SetSize (int size); /// inline void PrintMemInfo (ostream & ost) const; /// inline void DeleteData () { SetSize (cont.Size()); } void SetName (const char * aname) { ; // cont.SetName(aname); // hash.SetName(aname); } }; template inline ostream & operator<< (ostream & ost, const INDEX_2_CLOSED_HASHTABLE & ht) { for (int i = 0; i < ht.Size(); i++) if (ht.UsedPos(i)) { INDEX_2 hash; T data; ht.GetData (i, hash, data); ost << "hash = " << hash << ", data = " << data << endl; } return ost; } class BASE_INDEX_3_CLOSED_HASHTABLE { protected: // MoveableArray hash; Array hash; int invalid; protected: BASE_INDEX_3_CLOSED_HASHTABLE (int size) : hash(size) { // hash.SetName ("i3-hashtable, hash"); invalid = -1; for (int i = 0; i < size; i++) hash[i].I1() = invalid; } public: int Size() const { return hash.Size(); } bool UsedPos (int pos) const { return ! (hash[pos].I1() == invalid); } int UsedElements () const { int n = hash.Size(); int cnt = 0; for (int i = 0; i < n; i++) if (hash[i].I1() != invalid) cnt++; return cnt; } int HashValue (const INDEX_3 & ind) const { return (ind.I1() + 15 * ind.I2() + 41 * ind.I3()) % hash.Size(); } int Position (const INDEX_3 & ind) const { int i = HashValue(ind); while (1) { if (hash[i] == ind) return i; if (hash[i].I1() == invalid) return -1; i = (i+1) % hash.Size(); } } int Costs (const INDEX_3 & ind) const { int i = HashValue(ind); int c = 1; while (1) { if (hash[i] == ind) return c; if (hash[i].I1() == invalid) return c; i = (i+1) % hash.Size(); c++; } } // returns true, if new postion is created bool PositionCreate (const INDEX_3 & ind, int & apos) { int i = HashValue (ind); if (hash[i] == ind) { apos = i; return false; } if (hash[i].I1() == invalid) { hash[i] = ind; apos = i; return true; } return PositionCreate2 (ind, apos); } protected: bool PositionCreate2 (const INDEX_3 & ind, int & apos); void BaseSetSize (int asize); }; template class INDEX_3_CLOSED_HASHTABLE : public BASE_INDEX_3_CLOSED_HASHTABLE { // MoveableArray cont; Array cont; public: INDEX_3_CLOSED_HASHTABLE (int size) : BASE_INDEX_3_CLOSED_HASHTABLE(size), cont(size) { ; //cont.SetName ("i3-hashtable, contents"); } void Set (const INDEX_3 & ahash, const T & acont) { int pos; PositionCreate (ahash, pos); hash[pos] = ahash; cont[pos] = acont; } const T & Get (const INDEX_3 & ahash) const { return cont[Position (ahash)]; } bool Used (const INDEX_3 & ahash) const { return (Position (ahash) != -1); } void SetData (int pos, const INDEX_3 & ahash, const T & acont) { hash[pos] = ahash; cont[pos] = acont; } void GetData (int pos, INDEX_3 & ahash, T & acont) const { ahash = hash[pos]; acont = cont[pos]; } void SetData (int pos, const T & acont) { cont[pos] = acont; } void GetData (int pos, T & acont) const { acont = cont[pos]; } const T & GetData (int pos) const { return cont[pos]; } void SetSize (int size) { BaseSetSize(size); cont.SetSize(size); } void PrintMemInfo (ostream & ost) const { cout << "Hashtable: " << Size() << " entries of size " << sizeof(INDEX_3) << " + " << sizeof(T) << " = " << Size() * (sizeof(INDEX_3) + sizeof(T)) << " bytes" << endl; } void DeleteData () { SetSize (cont.Size()); } void SetName (const char * aname) { ; // cont.SetName(aname); // hash.SetName(aname); } }; template inline ostream & operator<< (ostream & ost, const INDEX_3_CLOSED_HASHTABLE & ht) { for (int i = 0; i < ht.Size(); i++) if (ht.UsedPos(i)) { INDEX_3 hash; T data; ht.GetData (i, hash, data); ost << "hash = " << hash << ", data = " << data << endl; } return ost; } template inline INDEX_3_HASHTABLE :: INDEX_3_HASHTABLE (int size) : BASE_INDEX_3_HASHTABLE (size), cont(size) { ; } template inline int INDEX_3_HASHTABLE :: PositionCreate (const INDEX_3 & ahash, int & bnr, int & colnr) { bnr = HashValue (ahash); colnr = Position (bnr, ahash); if (!colnr) { hash.Add (bnr, ahash); cont.AddEmpty (bnr); colnr = cont.EntrySize (bnr); return 1; } return 0; } template inline void INDEX_3_HASHTABLE :: Set (const INDEX_3 & ahash, const T & acont) { int bnr = HashValue (ahash); int pos = Position (bnr, ahash); if (pos) cont.Set (bnr, pos, acont); else { hash.Add1 (bnr, ahash); cont.Add1 (bnr, acont); } } template inline const T & INDEX_3_HASHTABLE :: Get (const INDEX_3 & ahash) const { int bnr = HashValue (ahash); int pos = Position (bnr, ahash); return cont.Get (bnr, pos); } template inline bool INDEX_3_HASHTABLE :: Used (const INDEX_3 & ahash) const { return (Position (HashValue (ahash), ahash)) ? 1 : 0; } template inline int INDEX_3_HASHTABLE :: GetNBags () const { return cont.Size(); } template inline int INDEX_3_HASHTABLE :: GetBagSize (int bnr) const { return cont.EntrySize (bnr); } template inline void INDEX_3_HASHTABLE :: GetData (int bnr, int colnr, INDEX_3 & ahash, T & acont) const { ahash = hash.Get(bnr, colnr); acont = cont.Get(bnr, colnr); } template inline void INDEX_3_HASHTABLE :: SetData (int bnr, int colnr, const INDEX_3 & ahash, const T & acont) { hash.Set(bnr, colnr, ahash); cont.Set(bnr, colnr, acont); } template inline void INDEX_3_HASHTABLE :: SetSize (int size) { hash.SetSize (size); cont.SetSize (size); } template inline void INDEX_3_HASHTABLE :: DeleteData () { int n = hash.Size(); hash.SetSize (n); cont.SetSize (n); } template inline void INDEX_3_HASHTABLE :: PrepareSet (const INDEX_3 & ahash) { int bnr = HashValue (ahash); hash.IncSizePrepare (bnr-1); cont.IncSizePrepare (bnr-1); } template inline void INDEX_3_HASHTABLE :: AllocateElements () { hash.AllocateElementsOneBlock(); cont.AllocateElementsOneBlock(); } template inline void INDEX_3_HASHTABLE :: PrintMemInfo (ostream & ost) const { ost << "Hash: " << endl; hash.PrintMemInfo (ost); ost << "Cont: " << endl; cont.PrintMemInfo (ost); } template inline INDEX_HASHTABLE :: INDEX_HASHTABLE (int size) : BASE_INDEX_HASHTABLE (size), cont(size) { ; } template inline void INDEX_HASHTABLE :: Set (const INDEX & ahash, const T & acont) { int bnr = HashValue (ahash); int pos = Position (bnr, ahash); if (pos) cont.Set (bnr, pos, acont); else { hash.Add (bnr, ahash); cont.Add (bnr, acont); } } template inline const T & INDEX_HASHTABLE :: Get (const INDEX & ahash) const { int bnr = HashValue (ahash); int pos = Position (bnr, ahash); return cont.Get (bnr, pos); } template inline bool INDEX_HASHTABLE :: Used (const INDEX & ahash) const { return (Position (HashValue (ahash), ahash)) ? 1 : 0; } template inline int INDEX_HASHTABLE :: GetNBags () const { return hash.Size(); } template inline int INDEX_HASHTABLE :: GetBagSize (int bnr) const { return hash.EntrySize(bnr); } template inline void INDEX_HASHTABLE :: GetData (int bnr, int colnr, INDEX & ahash, T & acont) const { ahash = hash.Get(bnr, colnr); acont = cont.Get(bnr, colnr); } template inline void INDEX_HASHTABLE :: PrintMemInfo (ostream & ost) const { ost << "Hash: " << endl; hash.PrintMemInfo (ost); ost << "Cont: " << endl; cont.PrintMemInfo (ost); } /* *********** Closed Hashing ************************* */ template inline INDEX_2_CLOSED_HASHTABLE :: INDEX_2_CLOSED_HASHTABLE (int size) : BASE_INDEX_2_CLOSED_HASHTABLE(size), cont(size) { // cont.SetName ("i2-hashtable, contents"); } template inline void INDEX_2_CLOSED_HASHTABLE :: Set (const INDEX_2 & ahash, const T & acont) { int pos; PositionCreate (ahash, pos); hash.Elem(pos) = ahash; cont.Elem(pos) = acont; } template inline const T & INDEX_2_CLOSED_HASHTABLE :: Get (const INDEX_2 & ahash) const { int pos = Position (ahash); return cont.Get(pos); } template inline bool INDEX_2_CLOSED_HASHTABLE :: Used (const INDEX_2 & ahash) const { int pos = Position (ahash); return (pos != 0); } template inline void INDEX_2_CLOSED_HASHTABLE :: SetData (int pos, const INDEX_2 & ahash, const T & acont) { hash.Elem(pos) = ahash; cont.Elem(pos) = acont; } template inline void INDEX_2_CLOSED_HASHTABLE :: GetData (int pos, INDEX_2 & ahash, T & acont) const { ahash = hash.Get(pos); acont = cont.Get(pos); } template inline void INDEX_2_CLOSED_HASHTABLE :: SetData (int pos, const T & acont) { cont.Elem(pos) = acont; } template inline void INDEX_2_CLOSED_HASHTABLE :: GetData (int pos, T & acont) const { acont = cont.Get(pos); } template inline void INDEX_2_CLOSED_HASHTABLE :: SetSize (int size) { BaseSetSize(size); cont.SetSize(size); } template inline void INDEX_2_CLOSED_HASHTABLE :: PrintMemInfo (ostream & ost) const { cout << "Hashtable: " << Size() << " entries of size " << sizeof(INDEX_2) << " + " << sizeof(T) << " = " << Size() * (sizeof(INDEX_2) + sizeof(T)) << " bytes." << " Used els: " << UsedElements() << endl; } /* template inline INDEX_3_CLOSED_HASHTABLE :: INDEX_3_CLOSED_HASHTABLE (int size) : BASE_INDEX_3_CLOSED_HASHTABLE(size), cont(size) { cont.SetName ("i3-hashtable, contents"); } template inline void INDEX_3_CLOSED_HASHTABLE :: Set (const INDEX_3 & ahash, const T & acont) { int pos; PositionCreate (ahash, pos); hash.Elem(pos) = ahash; cont.Elem(pos) = acont; } template inline const T & INDEX_3_CLOSED_HASHTABLE :: Get (const INDEX_3 & ahash) const { int pos = Position (ahash); return cont[pos]; } template inline bool INDEX_3_CLOSED_HASHTABLE :: Used (const INDEX_3 & ahash) const { int pos = Position (ahash); return (pos != 0); } template inline void INDEX_3_CLOSED_HASHTABLE :: SetData (int pos, const INDEX_3 & ahash, const T & acont) { hash.Elem(pos) = ahash; cont.Elem(pos) = acont; } template inline void INDEX_3_CLOSED_HASHTABLE :: GetData (int pos, INDEX_3 & ahash, T & acont) const { ahash = hash.Get(pos); acont = cont.Get(pos); } template inline void INDEX_3_CLOSED_HASHTABLE :: SetData (int pos, const T & acont) { cont.Elem(pos) = acont; } template inline void INDEX_3_CLOSED_HASHTABLE :: GetData (int pos, T & acont) const { acont = cont.Get(pos); } template inline const T & INDEX_3_CLOSED_HASHTABLE :: GetData (int pos) const { return cont.Get(pos); } template inline void INDEX_3_CLOSED_HASHTABLE :: SetSize (int size) { BaseSetSize(size); cont.SetSize(size); } template inline void INDEX_3_CLOSED_HASHTABLE :: PrintMemInfo (ostream & ost) const { cout << "Hashtable: " << Size() << " entries of size " << sizeof(INDEX_3) << " + " << sizeof(T) << " = " << Size() * (sizeof(INDEX_3) + sizeof(T)) << " bytes" << endl; } */ } #endif