csutil/hash.h
Go to the documentation of this file.00001 /* 00002 Copyright (C) 2003 by Mat Sutcliffe <oktal@gmx.co.uk> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Lesser General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public 00015 License along with this library; if not, write to the Free 00016 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00017 */ 00018 00019 #ifndef __CS_UTIL_HASH_H__ 00020 #define __CS_UTIL_HASH_H__ 00021 00026 #include "csextern.h" 00027 #include "csgeom/math.h" 00028 #include "csutil/array.h" 00029 #include "csutil/comparator.h" 00030 #include "csutil/util.h" 00031 #include "csutil/tuple.h" 00032 #include "csutil/hashcomputer.h" 00033 00041 template <typename T> 00042 class csPtrKey 00043 { 00044 T* ptr; 00045 public: 00046 csPtrKey () : ptr(0) {} 00047 csPtrKey (T* ptr) : ptr(ptr) {} 00048 csPtrKey (csPtrKey const& other) : ptr (other.ptr) {} 00049 00050 uint GetHash () const { return (uintptr_t)ptr; } 00051 inline friend bool operator < (const csPtrKey& r1, const csPtrKey& r2) 00052 { return r1.ptr < r2.ptr; } 00053 operator T* () const 00054 { return ptr; } 00055 T* operator -> () const 00056 { return ptr; } 00057 csPtrKey& operator = (csPtrKey const& other) 00058 { ptr = other.ptr; return *this; } 00059 }; 00060 00064 template <typename T> 00065 class csConstPtrKey 00066 { 00067 const T* ptr; 00068 public: 00069 csConstPtrKey () : ptr(0) {} 00070 csConstPtrKey (const T* ptr) : ptr(ptr) {} 00071 csConstPtrKey (csConstPtrKey const& other) : ptr (other.ptr) {} 00072 00073 uint GetHash () const { return (uintptr_t)ptr; } 00074 inline friend bool operator < (const csConstPtrKey& r1, const csConstPtrKey& r2) 00075 { return r1.ptr < r2.ptr; } 00076 operator const T* () const 00077 { return ptr; } 00078 const T* operator -> () const 00079 { return ptr; } 00080 csConstPtrKey& operator = (csConstPtrKey const& other) 00081 { ptr = other.ptr; return *this; } 00082 }; 00083 00084 template <class T, class K, 00085 class ArrayMemoryAlloc, class ArrayElementHandler> class csHash; 00086 00087 namespace CS 00088 { 00089 namespace Container 00090 { 00096 template <class T, class K> 00097 class HashElement 00098 { 00099 private: 00100 template <class _T, class _K, class ArrayMemoryAlloc, 00101 class ArrayElementHandler> friend class csHash; 00102 00103 K key; 00104 T value; 00105 public: 00106 HashElement (const K& key0, const T &value0) : key (key0), value (value0) {} 00107 HashElement (const HashElement& other) : key (other.key), value (other.value) {} 00108 00109 const K& GetKey() const { return key; } 00110 const T& GetValue() const { return value; } 00111 T& GetValue() { return value; } 00112 }; 00113 } // namespace Container 00114 } // namespace CS 00115 00125 template <class T, class K = unsigned int, 00126 class ArrayMemoryAlloc = CS::Memory::AllocatorMalloc, 00127 class ArrayElementHandler = csArrayElementHandler< 00128 CS::Container::HashElement<T, K> > > 00129 class csHash 00130 { 00131 public: 00132 typedef csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler> ThisType; 00133 typedef T ValueType; 00134 typedef K KeyType; 00135 typedef ArrayMemoryAlloc AllocatorType; 00136 00137 protected: 00138 typedef CS::Container::HashElement<T, K> Element; 00139 typedef csArray<Element, ArrayElementHandler, 00140 ArrayMemoryAlloc, csArrayCapacityDefault> ElementArray; 00141 csArray<ElementArray, csArrayElementHandler<ElementArray>, 00142 ArrayMemoryAlloc> Elements; 00143 00144 size_t Modulo; 00145 size_t Size; 00146 00147 size_t InitModulo; 00148 size_t GrowRate; 00149 size_t MaxSize; 00150 00151 void Grow () 00152 { 00153 static const size_t Primes[] = 00154 { 00155 53, 97, 193, 389, 769, 00156 1543, 3079, 6151, 12289, 24593, 00157 49157, 98317, 196613, 393241, 786433, 00158 1572869, 3145739, 6291469, 12582917, 25165843, 00159 50331653, 100663319, 201326611, 402653189, 805306457, 00160 1610612741, 0 00161 }; 00162 00163 const size_t *p; 00164 size_t elen = Elements.GetSize (); 00165 for (p = Primes; *p && *p <= elen; p++) ; 00166 Modulo = *p; 00167 CS_ASSERT (Modulo); 00168 00169 Elements.SetSize (Modulo, ElementArray (size_t (0), csMin (Modulo / GrowRate, size_t (4)))); 00170 00171 for (size_t i = 0; i < elen; i++) 00172 { 00173 ElementArray& src = Elements[i]; 00174 size_t slen = src.GetSize (); 00175 for (size_t j = slen; j > 0; j--) 00176 { 00177 const Element& srcElem = src[j - 1]; 00178 ElementArray& dst = 00179 Elements.Get (csHashComputer<K>::ComputeHash (srcElem.key) % Modulo); 00180 if (&src != &dst) 00181 { 00182 dst.Push (srcElem); 00183 src.DeleteIndex (j - 1); 00184 } 00185 } 00186 } 00187 } 00188 00189 public: 00204 csHash (size_t size = 23, size_t grow_rate = 5, size_t max_size = 20000) 00205 : Modulo (size), Size(0), InitModulo (size), 00206 GrowRate (csMin (grow_rate, size)), MaxSize (max_size) 00207 { 00208 } 00209 00211 csHash (const csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler> &o) : 00212 Elements (o.Elements), 00213 Modulo (o.Modulo), Size(o.Size), InitModulo (o.InitModulo), 00214 GrowRate (o.GrowRate), MaxSize (o.MaxSize) {} 00215 00223 T& Put (const K& key, const T &value) 00224 { 00225 if (Elements.GetSize() == 0) Elements.SetSize (Modulo); 00226 ElementArray& values = 00227 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00228 size_t idx = values.Push (Element (key, value)); 00229 Size++; 00230 if (values.GetSize () > Elements.GetSize () / GrowRate 00231 && Elements.GetSize () < MaxSize) 00232 { 00233 Grow (); 00234 /* can't use 'values[idx]' since that is no longer the place where 00235 the item is stored. */ 00236 return *(GetElementPointer (key)); 00237 } 00238 return values[idx].value; 00239 } 00240 00242 csArray<T> GetAll () const 00243 { 00244 if (Elements.GetSize() == 0) return csArray<T> (); 00245 00246 ConstGlobalIterator itr = GetIterator(); 00247 csArray<T> ret; 00248 while(itr.HasNext()) 00249 { 00250 ret.Push(itr.Next()); 00251 } 00252 00253 return ret; 00254 } 00255 00257 csArray<T> GetAll (const K& key) const 00258 { 00259 return GetAll<typename csArray<T>::ElementHandlerType, 00260 typename csArray<T>::AllocatorType> (key); 00261 } 00262 00264 template<typename H, typename M> 00265 csArray<T, H, M> GetAll (const K& key) const 00266 { 00267 if (Elements.GetSize() == 0) return csArray<T> (); 00268 const ElementArray& values = 00269 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00270 csArray<T> ret (values.GetSize () / 2); 00271 const size_t len = values.GetSize (); 00272 for (size_t i = 0; i < len; ++i) 00273 { 00274 const Element& v = values[i]; 00275 if (csComparator<K, K>::Compare (v.key, key) == 0) 00276 ret.Push (v.value); 00277 } 00278 return ret; 00279 } 00280 00282 T& PutUnique (const K& key, const T &value) 00283 { 00284 if (Elements.GetSize() == 0) Elements.SetSize (Modulo); 00285 ElementArray& values = 00286 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00287 const size_t len = values.GetSize (); 00288 for (size_t i = 0; i < len; ++i) 00289 { 00290 Element& v = values[i]; 00291 if (csComparator<K, K>::Compare (v.key, key) == 0) 00292 { 00293 v.value = value; 00294 return v.value; 00295 } 00296 } 00297 00298 size_t idx = values.Push (Element (key, value)); 00299 Size++; 00300 if (values.GetSize () > Elements.GetSize () / GrowRate 00301 && Elements.GetSize () < MaxSize) 00302 { 00303 Grow (); 00304 /* can't use 'values[idx]' since that is no longer the place where 00305 the item is stored. */ 00306 return *(GetElementPointer (key)); 00307 } 00308 return values[idx].value; 00309 } 00310 00312 bool Contains (const K& key) const 00313 { 00314 if (Elements.GetSize() == 0) return false; 00315 const ElementArray& values = 00316 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00317 const size_t len = values.GetSize (); 00318 for (size_t i = 0; i < len; ++i) 00319 if (csComparator<K, K>::Compare (values[i].key, key) == 0) 00320 return true; 00321 return false; 00322 } 00323 00329 bool In (const K& key) const 00330 { return Contains(key); } 00331 00336 const T* GetElementPointer (const K& key) const 00337 { 00338 if (Elements.GetSize() == 0) return 0; 00339 const ElementArray& values = 00340 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00341 const size_t len = values.GetSize (); 00342 for (size_t i = 0; i < len; ++i) 00343 { 00344 const Element& v = values[i]; 00345 if (csComparator<K, K>::Compare (v.key, key) == 0) 00346 return &v.value; 00347 } 00348 00349 return 0; 00350 } 00351 00356 T* GetElementPointer (const K& key) 00357 { 00358 if (Elements.GetSize() == 0) return 0; 00359 ElementArray& values = 00360 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00361 const size_t len = values.GetSize (); 00362 for (size_t i = 0; i < len; ++i) 00363 { 00364 Element& v = values[i]; 00365 if (csComparator<K, K>::Compare (v.key, key) == 0) 00366 return &v.value; 00367 } 00368 00369 return 0; 00370 } 00371 00375 T* operator[] (const K& key) 00376 { 00377 return GetElementPointer (key); 00378 } 00379 00384 const T& Get (const K& key, const T& fallback) const 00385 { 00386 if (Elements.GetSize() == 0) return fallback; 00387 const ElementArray& values = 00388 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00389 const size_t len = values.GetSize (); 00390 for (size_t i = 0; i < len; ++i) 00391 { 00392 const Element& v = values[i]; 00393 if (csComparator<K, K>::Compare (v.key, key) == 0) 00394 return v.value; 00395 } 00396 00397 return fallback; 00398 } 00399 00404 T& Get (const K& key, T& fallback) 00405 { 00406 if (Elements.GetSize() == 0) return fallback; 00407 ElementArray& values = 00408 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00409 const size_t len = values.GetSize (); 00410 for (size_t i = 0; i < len; ++i) 00411 { 00412 Element& v = values[i]; 00413 if (csComparator<K, K>::Compare (v.key, key) == 0) 00414 return v.value; 00415 } 00416 00417 return fallback; 00418 } 00419 00424 T& GetOrCreate (const K& key, const T& defaultValue = T()) 00425 { 00426 if (Elements.GetSize() != 0) 00427 { 00428 ElementArray& values = 00429 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00430 const size_t len = values.GetSize (); 00431 for (size_t i = 0; i < len; ++i) 00432 { 00433 Element& v = values[i]; 00434 if (csComparator<K, K>::Compare (v.key, key) == 0) 00435 return v.value; 00436 } 00437 } 00438 00439 return Put (key, defaultValue); 00440 } 00441 00443 void DeleteAll () 00444 { 00445 Elements.DeleteAll(); 00446 Modulo = InitModulo; 00447 Size = 0; 00448 } 00449 00451 void Empty() { DeleteAll(); } 00452 00454 bool DeleteAll (const K& key) 00455 { 00456 bool ret = false; 00457 if (Elements.GetSize() == 0) return ret; 00458 ElementArray& values = 00459 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00460 for (size_t i = values.GetSize (); i > 0; i--) 00461 { 00462 const size_t idx = i - 1; 00463 if (csComparator<K, K>::Compare (values[idx].key, key) == 0) 00464 { 00465 values.DeleteIndexFast (idx); 00466 ret = true; 00467 Size--; 00468 } 00469 } 00470 return ret; 00471 } 00472 00474 bool Delete (const K& key, const T &value) 00475 { 00476 bool ret = false; 00477 if (Elements.GetSize() == 0) return ret; 00478 ElementArray& values = 00479 Elements[csHashComputer<K>::ComputeHash (key) % Modulo]; 00480 for (size_t i = values.GetSize (); i > 0; i--) 00481 { 00482 const size_t idx = i - 1; 00483 if ((csComparator<K, K>::Compare (values[idx].key, key) == 0) && 00484 (csComparator<T, T>::Compare (values[idx].value, value) == 0 )) 00485 { 00486 values.DeleteIndexFast (idx); 00487 ret = true; 00488 Size--; 00489 } 00490 } 00491 return ret; 00492 } 00493 00495 size_t GetSize () const 00496 { 00497 return Size; 00498 } 00499 00505 bool IsEmpty() const 00506 { 00507 return GetSize() == 0; 00508 } 00509 00511 class Iterator 00512 { 00513 private: 00514 csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler>* hash; 00515 const K key; 00516 size_t bucket, size, element; 00517 00518 void Seek () 00519 { 00520 while ((element < size) && 00521 (csComparator<K, K>::Compare (hash->Elements[bucket][element].GetKey(), 00522 key) != 0)) 00523 element++; 00524 } 00525 00526 protected: 00527 Iterator (csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler>* hash0, 00528 const K& key0) : 00529 hash(hash0), 00530 key(key0), 00531 bucket(csHashComputer<K>::ComputeHash (key) % hash->Modulo), 00532 size((hash->Elements.GetSize() > 0) ? hash->Elements[bucket].GetSize () : 0) 00533 { Reset (); } 00534 00535 friend class csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler>; 00536 public: 00538 Iterator (const Iterator &o) : 00539 hash (o.hash), 00540 key(o.key), 00541 bucket(o.bucket), 00542 size(o.size), 00543 element(o.element) {} 00544 00546 Iterator& operator=(const Iterator& o) 00547 { 00548 hash = o.hash; 00549 key = o.key; 00550 bucket = o.bucket; 00551 size = o.size; 00552 element = o.element; 00553 return *this; 00554 } 00555 00557 bool HasNext () const 00558 { 00559 return element < size; 00560 } 00561 00563 T& Next () 00564 { 00565 T &ret = hash->Elements[bucket][element].GetValue(); 00566 element++; 00567 Seek (); 00568 return ret; 00569 } 00570 00572 void Reset () { element = 0; Seek (); } 00573 }; 00574 friend class Iterator; 00575 00577 class GlobalIterator 00578 { 00579 private: 00580 csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler> *hash; 00581 size_t bucket, size, element; 00582 00583 void Zero () { bucket = element = 0; } 00584 void Init () 00585 { 00586 size = 00587 (hash->Elements.GetSize() > 0) ? hash->Elements[bucket].GetSize () : 0; 00588 } 00589 00590 void FindItem () 00591 { 00592 if (element >= size) 00593 { 00594 while (++bucket < hash->Elements.GetSize ()) 00595 { 00596 Init (); 00597 if (size != 0) 00598 { 00599 element = 0; 00600 break; 00601 } 00602 } 00603 } 00604 } 00605 00606 protected: 00607 GlobalIterator (csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler> *hash0) 00608 : hash (hash0) 00609 { 00610 Zero (); 00611 Init (); 00612 FindItem (); 00613 } 00614 00615 friend class csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler>; 00616 public: 00618 GlobalIterator () : hash (0), size (0) { Zero (); } 00619 00621 GlobalIterator (const GlobalIterator &o) : 00622 hash (o.hash), 00623 bucket (o.bucket), 00624 size (o.size), 00625 element (o.element) {} 00626 00628 GlobalIterator& operator=(const GlobalIterator& o) 00629 { 00630 hash = o.hash; 00631 bucket = o.bucket; 00632 size = o.size; 00633 element = o.element; 00634 return *this; 00635 } 00636 00638 bool HasNext () const 00639 { 00640 if (hash->Elements.GetSize () == 0) return false; 00641 return element < size || bucket < hash->Elements.GetSize (); 00642 } 00643 00645 void Advance () 00646 { 00647 element++; 00648 FindItem (); 00649 } 00650 00652 T& NextNoAdvance () 00653 { 00654 return hash->Elements[bucket][element].GetValue(); 00655 } 00656 00658 T& Next () 00659 { 00660 T &ret = NextNoAdvance (); 00661 Advance (); 00662 return ret; 00663 } 00664 00666 T& NextNoAdvance (K &key) 00667 { 00668 key = hash->Elements[bucket][element].GetKey(); 00669 return NextNoAdvance (); 00670 } 00671 00673 T& Next (K &key) 00674 { 00675 key = hash->Elements[bucket][element].GetKey(); 00676 return Next (); 00677 } 00678 00680 const csTuple2<T, K> NextTuple () 00681 { 00682 csTuple2<T, K> t (NextNoAdvance (), 00683 hash->Elements[bucket][element].GetKey()); 00684 Advance (); 00685 return t; 00686 } 00687 00689 void Reset () { Zero (); Init (); FindItem (); } 00690 }; 00691 friend class GlobalIterator; 00692 00694 class ConstIterator 00695 { 00696 private: 00697 const csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler>* hash; 00698 const K key; 00699 size_t bucket, size, element; 00700 00701 void Seek () 00702 { 00703 while ((element < size) && 00704 (csComparator<K, K>::Compare (hash->Elements[bucket][element].GetKey(), 00705 key) != 0)) 00706 element++; 00707 } 00708 00709 protected: 00710 ConstIterator (const csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler>* 00711 hash0, const K& key0) : 00712 hash(hash0), 00713 key(key0), 00714 bucket(csHashComputer<K>::ComputeHash (key) % hash->Modulo), 00715 size((hash->Elements.GetSize() > 0) ? hash->Elements[bucket].GetSize () : 0) 00716 { Reset (); } 00717 00718 friend class csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler>; 00719 public: 00721 ConstIterator (const ConstIterator &o) : 00722 hash (o.hash), 00723 key(o.key), 00724 bucket(o.bucket), 00725 size(o.size), 00726 element(o.element) {} 00727 00729 ConstIterator& operator=(const ConstIterator& o) 00730 { 00731 hash = o.hash; 00732 key = o.key; 00733 bucket = o.bucket; 00734 size = o.size; 00735 element = o.element; 00736 return *this; 00737 } 00738 00740 bool HasNext () const 00741 { 00742 return element < size; 00743 } 00744 00746 const T& Next () 00747 { 00748 const T &ret = hash->Elements[bucket][element].GetValue(); 00749 element++; 00750 Seek (); 00751 return ret; 00752 } 00753 00755 void Reset () { element = 0; Seek (); } 00756 }; 00757 friend class ConstIterator; 00758 00760 class ConstGlobalIterator 00761 { 00762 private: 00763 const csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler> *hash; 00764 size_t bucket, size, element; 00765 00766 void Zero () { bucket = element = 0; } 00767 void Init () 00768 { 00769 size = 00770 (hash->Elements.GetSize() > 0) ? hash->Elements[bucket].GetSize () : 0; 00771 } 00772 00773 void FindItem () 00774 { 00775 if (element >= size) 00776 { 00777 while (++bucket < hash->Elements.GetSize ()) 00778 { 00779 Init (); 00780 if (size != 0) 00781 { 00782 element = 0; 00783 break; 00784 } 00785 } 00786 } 00787 } 00788 00789 protected: 00790 ConstGlobalIterator (const csHash<T, K, ArrayMemoryAlloc, 00791 ArrayElementHandler> *hash0) : hash (hash0) 00792 { 00793 Zero (); 00794 Init (); 00795 FindItem (); 00796 } 00797 00798 friend class csHash<T, K, ArrayMemoryAlloc, ArrayElementHandler>; 00799 public: 00801 ConstGlobalIterator () : hash (0), size (0) { Zero (); } 00802 00804 ConstGlobalIterator (const ConstGlobalIterator &o) : 00805 hash (o.hash), 00806 bucket (o.bucket), 00807 size (o.size), 00808 element (o.element) {} 00809 00811 ConstGlobalIterator& operator=(const ConstGlobalIterator& o) 00812 { 00813 hash = o.hash; 00814 bucket = o.bucket; 00815 size = o.size; 00816 element = o.element; 00817 return *this; 00818 } 00819 00821 bool HasNext () const 00822 { 00823 if (hash->Elements.GetSize () == 0) return false; 00824 return element < size || bucket < hash->Elements.GetSize (); 00825 } 00826 00828 void Advance () 00829 { 00830 element++; 00831 FindItem (); 00832 } 00833 00835 const T& NextNoAdvance () 00836 { 00837 return hash->Elements[bucket][element].GetValue(); 00838 } 00839 00841 const T& Next () 00842 { 00843 const T &ret = NextNoAdvance (); 00844 Advance (); 00845 return ret; 00846 } 00847 00849 const T& NextNoAdvance (K &key) 00850 { 00851 key = hash->Elements[bucket][element].GetKey(); 00852 return NextNoAdvance (); 00853 } 00854 00856 const T& Next (K &key) 00857 { 00858 key = hash->Elements[bucket][element].GetKey(); 00859 return Next (); 00860 } 00861 00863 const csTuple2<T, K> NextTuple () 00864 { 00865 csTuple2<T, K> t (NextNoAdvance (), 00866 hash->Elements[bucket][element].GetKey()); 00867 Advance (); 00868 return t; 00869 } 00870 00872 void Reset () { Zero (); Init (); FindItem (); } 00873 }; 00874 friend class ConstGlobalIterator; 00875 00878 void DeleteElement (GlobalIterator& iterator) 00879 { 00880 Elements[iterator.bucket].DeleteIndex(iterator.element); 00881 Size--; 00882 iterator.size--; 00883 iterator.FindItem (); 00884 } 00885 00888 void DeleteElement (ConstGlobalIterator& iterator) 00889 { 00890 Elements[iterator.bucket].DeleteIndex(iterator.element); 00891 Size--; 00892 iterator.size--; 00893 iterator.FindItem (); 00894 } 00895 00902 Iterator GetIterator (const K& key) 00903 { 00904 return Iterator (this, key); 00905 } 00906 00912 GlobalIterator GetIterator () 00913 { 00914 return GlobalIterator (this); 00915 } 00916 00923 ConstIterator GetIterator (const K& key) const 00924 { 00925 return ConstIterator (this, key); 00926 } 00927 00933 ConstGlobalIterator GetIterator () const 00934 { 00935 return ConstGlobalIterator (this); 00936 } 00937 }; 00938 00941 #endif
Generated for Crystal Space 2.1 by doxygen 1.6.1
