Evo C++ Library v0.5.1
sethash.h
Go to the documentation of this file.
1 // Evo C++ Library
2 /* Copyright 2019 Justin Crowell
3 Distributed under the BSD 2-Clause License -- see included file LICENSE.txt for details.
4 */
6 
7 #pragma once
8 #ifndef INCL_evo_sethash_h
9 #define INCL_evo_sethash_h
10 
11 #include "set.h"
12 #include "array.h"
13 #include "ptrlist.h"
14 #include "impl/hash.h"
15 
16 namespace evo {
19 
21 
166 template<class TKey, class THash=CompareHash<TKey>, class TSize=SizeT>
167 class SetHash : public Set<TKey,TSize> {
168 protected:
170 
171 public:
173 #if defined(_MSC_VER) || defined(EVO_OLDCC) // avoid errors with older compilers and MSVC
174  typedef Set<TKey,TSize> SetBaseType;
175  typedef typename SetBaseType::Size Size;
176  typedef typename SetBaseType::Key Key;
177  typedef typename SetBaseType::Value Value;
178  typedef typename SetBaseType::Item Item;
179  typedef typename SetBaseType::IterKey IterKey;
180  typedef typename SetBaseType::IterItem IterItem;
181 #else
182  using typename Set<TKey,TSize>::SetBaseType;
183  using typename Set<TKey,TSize>::Size;
184  using typename Set<TKey,TSize>::Key;
185  using typename Set<TKey,TSize>::Value;
186  using typename Set<TKey,TSize>::Item;
187  using typename Set<TKey,TSize>::IterKey;
188  using typename Set<TKey,TSize>::IterItem;
189 #endif
191  typedef THash Hash;
192 
195 
198  { }
199 
203  SetHash(const SetBaseType& src) {
204  for (typename SetBaseType::Iter iter(src); iter; ++iter)
205  add(*iter);
206  }
207 
211  SetHash(const ThisType& src)
212  { set(src); }
213 
214 #if defined(EVO_CPP11)
215 
218  SetHash(std::initializer_list<Value> init) : SetHash() {
219  assert( init.size() < IntegerT<Size>::MAX );
220  capacitymin((Size)init.size());
221  for (const auto& val : init)
222  add(val);
223  }
224 
228  SetHash(ThisType&& src) : buckets_(std::move(src.buckets_)), data_(std::move(src.data_)) {
229  SetBaseType::size_ = src.SetBaseType::size_;
230  src.SetBaseType::size_ = 0;
231  }
232 
237  ThisType& operator=(ThisType&& src) {
238  buckets_ = std::move(src.buckets_);
239  data_ = std::move(src.data_);
240  SetBaseType::size_ = src.SetBaseType::size_;
241  src.SetBaseType::size_ = 0;
242  return *this;
243  }
244 #endif
245 
247  const ThisType& asconst() const {
248  return *this;
249  }
250 
251  // SET
252 
254  ThisType& operator=(const SetBaseType& src)
255  { set(src); return *this; }
256 
258  ThisType& operator=(const ThisType& src)
259  { set(src); return *this; }
260 
261  ThisType& set()
262  { buckets_.set(); size_ = 0; return *this; }
263 
265  ThisType& set(const SetBaseType& src) {
266  clear();
267  for (typename SetBaseType::Iter iter(src); iter; ++iter)
268  add(*iter);
269  return *this;
270  }
271 
273  ThisType& set(const ThisType& src) {
274  buckets_.set(src.buckets_);
275  data_ = src.data_;
276  size_ = src.size_;
277  return *this;
278  }
279 
280  ThisType& setempty()
281  { buckets_.setempty(); size_ = 0; return *this; }
282 
283  ThisType& clear()
284  { buckets_.clear(); size_ = 0; return *this; }
285 
286  // INFO
287 
288  bool null() const
289  { return buckets_.null(); }
290 
291  bool shared() const
292  { return buckets_.shared(); }
293 
294  Size capacity() const
295  { return buckets_.size(); }
296 
297  bool ordered() const
298  { return false; }
299 
300  // COMPARE
301 
305  const Hash& get_compare() const
306  { return data_; }
307 
311  Hash& get_compare()
312  { return data_; }
313 
314  using SetBaseType::operator==;
315  using SetBaseType::operator!=;
316 
318  bool operator==(const ThisType& set) const {
319  if (this == &set)
320  return true;
321  if (buckets_.size() == set.buckets_.size())
322  return (buckets_ == set.buckets_);
323  return SetBaseType::operator==(set);
324  }
325 
327  bool operator!=(const ThisType& set) const
328  { return !(*this == set); }
329 
330  // FIND
331 
333  Iter cbegin() const
334  { return Iter(*this); }
335 
337  Iter cend() const
338  { return Iter(); }
339 
341  IterM begin()
342  { return IterM(*this); }
343 
345  Iter begin() const
346  { return Iter(*this); }
347 
349  IterM end()
350  { return IterM(); }
351 
353  Iter end() const
354  { return Iter(); }
355 
356  bool contains(const Value& value) const
357  { return (search(value) != NULL); }
358 
360  Iter iter(const Value& value) const {
361  if (size_ > 0) {
362  IterKey iterkey( data_.hash(value) & data_.sizemask );
363  const Bucket* bucket = buckets_.item(iterkey.a);
364  if (bucket != NULL) {
365  if (data_(bucket->first, value) == 0) {
366  return Iter(*this, iterkey, (IterItem*)&bucket->first);
367  } else {
368  const IterItem* item = (IterItem*)bucket->search(iterkey.b, value, data_);
369  if (item != NULL) {
370  ++iterkey.b;
371  return Iter(*this, iterkey, item);
372  }
373  }
374  }
375  }
376  return Iter(*this, iterEND);
377  }
378 
380  IterM iterM(const Value& value) {
381  if (size_ > 0) {
382  IterKey iterkey( data_.hash(value) & data_.sizemask );
383  Bucket* bucket = buckets_.itemM(iterkey.a);
384  if (bucket != NULL) {
385  if (data_(bucket->first, value) == 0) {
386  return IterM(*this, iterkey, (IterItem*)&bucket->first);
387  } else {
388  IterItem* item = (IterItem*)bucket->search(iterkey.b, value, data_);
389  if (item != NULL) {
390  ++iterkey.b;
391  return IterM(*this, iterkey, item);
392  }
393  }
394  }
395  }
396  return IterM(*this, iterEND);
397  }
398 
399  Value& get(const Value& value, bool* created=NULL) {
400  if (size_ == 0 || data_.threshold == 0)
401  // Empty, use init size
402  rehash(SIZE_INIT);
403  else if (size_ >= data_.threshold)
404  // Reached size threshold, time to grow
405  rehash(buckets_.size() << 1);
406 
407  // Get item, create if needed
408  bool created_item;
409  Value* item;
410  Bucket* bucket = buckets_.getitem((data_.hash(value) & data_.sizemask), &created_item);
411  if (created_item) {
412  ++size_;
413  item = &bucket->first;
414  *item = value;
415  } else if (data_(bucket->first, value) == 0) {
416  item = &bucket->first;
417  } else {
418  Size index;
419  item = (Value*)bucket->search(index, value, data_);
420  if (item == NULL) {
421  created_item = true;
422  ++size_;
423  item = (Value*)&(bucket->others[bucket->others.insert(index, value)]);
424  }
425  }
426  if (created != NULL)
427  *created = created_item;
428  return *item;
429  }
430 
431  // INFO_SET
432 
433  ThisType& unshare()
434  { buckets_.unshare(); return *this; }
435 
444  ThisType& capacity(Size size) {
445  const Size cursize = buckets_.size();
446 
447  if (size != cursize) {
448  // Find new size (must be power of 2)
449  Size newsize = SIZE_INIT;
450  while (newsize < size) newsize <<= 1;
451  while (newsize < size_) newsize <<= 1;
452 
453  if (newsize != cursize)
454  // Rehash with new size
455  rehash(newsize);
456  }
457  return *this;
458  }
459 
464  ThisType& capacitymin(Size min) {
465  if (min > buckets_.size())
466  capacity(min);
467  return *this;
468  }
469 
471  ThisType& reserve(Size size)
472  { return capacitymin(size_ + size); }
473 
474  // ADD
475 
476  Value& add(const Value& value, bool update=false) {
477  bool created_val;
478  Value& upditem = get(value, &created_val);
479  if (!created_val && update)
480  upditem = value;
481  return upditem;
482  }
483 
484  // REMOVE
485 
486  bool remove(const Value& value) {
487  bool result;
488  Size bucket_index = data_.hash(value) & data_.sizemask;
489  Bucket* bucket = buckets_.itemM(bucket_index);
490  if (bucket != NULL) {
491  Size index;
492  if (data_(bucket->first, value) == 0) {
493  if (bucket->others.size() > 0) {
494  // Remove "first" and replace with last item in "others"
495  Value* item1 = &bucket->first;
496  Value* item2 = bucket->others.data();
497  EVO_IMPL_CONTAINER_SWAP(item1, item2, Item);
498  bucket->others.remove(0);
499  } else
500  // Remove bucket since "first" is only item
501  buckets_.remove(bucket_index);
502  result = true;
503  --size_;
504  } else if (bucket->search(index, value, data_) != NULL) {
505  // Remove from "others"
506  bucket->others.remove(index);
507  result = true;
508  --size_;
509  } else
510  result = false;
511  } else
512  result = false;
513  return result;
514  }
515 
516  bool remove(typename SetBaseType::IterM& iter, IteratorDir dir=iterNONE)
517  { return remove((IterM&)iter, dir); }
518 
519  bool remove(IterM& iter, IteratorDir dir=iterNONE) {
520  if (iter && this == &iter.getParent()) {
521  IterKey& iterkey = iter.getKey();
522  assert( iterkey.a < buckets_.size() );
523  Bucket* bucket = buckets_.itemM(iterkey.a);
524  assert( bucket != NULL );
525  if (iterkey.b > 0) {
526  // Remove from "others"
527  bucket->others.remove(iterkey.b-1);
528  } else {
529  if (bucket->others.size() > 0) {
530  // Remove "first" and replace with last item in "others"
531  Value* item1 = &bucket->first;
532  Value* item2 = bucket->others.data();
533  EVO_IMPL_CONTAINER_SWAP(item1, item2, Item);
534  bucket->others.remove(0);
535  } else {
536  // Remove bucket since "first" is only item
537  buckets_.remove(iterkey.a);
538  bucket = NULL;
539  }
540  }
541 
542  // Update size and iterator
543  if (--size_ > 0 && dir != iterNONE) {
544  if (dir == iterRV) {
545  // Previous
546  if (iterkey.b > 0 && bucket != NULL) {
547  iter.setData( (IterItem*)(--iterkey.b == 0 ? &(bucket->first) : &(bucket->others[iterkey.b-1])) );
548  } else {
549  bucket = (Bucket*)buckets_.iterPrev(iterkey.a);
550  if (bucket != NULL) {
551  iterkey.b = bucket->others.size();
552  iter.setData( (IterItem*)(iterkey.b == 0 ? &bucket->first : &(bucket->others[iterkey.b-1])) );
553  } else
554  iter = iterEND;
555  }
556  } else {
557  // Next
558  if (bucket != NULL && iterkey.b <= bucket->others.size()) {
559  iter.setData( (IterItem*)(iterkey.b == 0 ? &bucket->first : &(bucket->others[iterkey.b-1])) );
560  } else {
561  iterkey.b = 0;
562  if ((bucket=(Bucket*)buckets_.iterNext(iterkey.a)) != NULL)
563  iter.setData( (IterItem*)&bucket->first );
564  else
565  iter = iterEND;
566  }
567  }
568  } else
569  iter = iterEND;
570  return true;
571  }
572  return false;
573  }
574 
575  // INTERNAL
576 
577  // Iterator support methods
579  void iterInitMutable()
580  { buckets_.unshare(); }
581  const IterItem* iterFirst(IterKey& key) const {
582  key.b = 0;
583  if (size_ > 0)
584  return (IterItem*)&(buckets_.iterFirst(key.a)->first);
585  key.a = END;
586  return NULL;
587  }
588  const IterItem* iterNext(IterKey& key) const {
589  if (key.a != END) {
590  const Bucket* bucket = buckets_.item(key.a);
591  if (++key.b <= bucket->others.size())
592  return (IterItem*)&(bucket->others[key.b-1]);
593  key.b = 0;
594  if ((bucket=buckets_.iterNext(key.a)) != NULL)
595  return (IterItem*)&bucket->first;
596  }
597  return NULL;
598  }
599  const IterItem* iterLast(IterKey& key) const {
600  if (size_ > 0) {
601  const Bucket* bucket = buckets_.iterLast(key.a);
602  assert( bucket != NULL );
603  key.b = bucket->others.size();
604  return (IterItem*)(key.b == 0 ? &bucket->first : &(bucket->others[key.b-1]));
605  }
606  key.a = END;
607  key.b = 0;
608  return NULL;
609 
610  }
611  const IterItem* iterPrev(IterKey& key) const {
612  if (key.a != END) {
613  if (key.b > 0) {
614  return (IterItem*)(--key.b == 0 ? &(buckets_.item(key.a)->first) : &(buckets_.item(key.a)->others[key.b-1]));
615  } else {
616  const Bucket* bucket = buckets_.iterPrev(key.a);
617  if (bucket != NULL) {
618  key.b = bucket->others.size();
619  return (IterItem*)(key.b == 0 ? &bucket->first : &(bucket->others[key.b-1]));
620  }
621  }
622  key.a = END;
623  }
624  return NULL;
625  }
628 protected:
629  const Value* getiter(IterKey& iterkey, const Value& value) const {
630  if (size_ > 0) {
631  iterkey.a = (data_.hash(value) & data_.sizemask);
632  const Bucket* bucket = buckets_.item(iterkey.a);
633  if (bucket != NULL) {
634  if (data_(bucket->first, value) == 0) {
635  iterkey.b = 0;
636  return &bucket->first;
637  } else {
638  const Value* item = bucket->search(iterkey.b, value, data_);
639  if (item != NULL) {
640  ++iterkey.b;
641  return item;
642  }
643  }
644  }
645  }
646  iterkey.a = END;
647  iterkey.b = 0;
648  return NULL;
649  }
650 
651 private:
652  static const Size SIZE_INIT = 64; // initial hash size -- must be power of 2
653  static const Size MIN_SIZE = 8; // minimum hash size -- must be power of 2
654 
655  struct Bucket {
656  Value first;
657  Array<Value> others;
658 
659  Bucket()
660  { others.setempty(); }
661  Bucket(const Bucket& src) : first(src.first), others(src.others)
662  { }
663  bool operator==(const Bucket& data)
664  { return (first == data.first && others == data.others); }
665 
666  // Search "others" for key, using compare, set index (insertion point if not found)
667  const Value* search(Size& index, const Value& key, const THash& compare) const {
668  int cmp;
669  Size left = 0, right = others.size(), mid = 0;
670  const Value* items = others.data();
671  while (left < right) {
672  mid = left + ((right-left) / 2);
673  const Value* item = items + mid;
674  cmp = compare(key, *item);
675  if (cmp < 0) {
676  right = mid;
677  } else if (cmp == 0) {
678  index = mid;
679  return item;
680  } else
681  left = mid + 1;
682  }
683  index = left;
684  return NULL;
685  }
686 
687  private:
688  // Copying is done via copy constructor
689  Bucket& operator=(const Bucket&) EVO_ONCPP11(= delete);
690  };
691 
693 
694  Buckets buckets_;
695 
696  // Use inheritance to reduce size bloat with empty Hash
697  struct Data : public THash {
698  Size sizemask;
699  Size threshold;
700 
701  Data() : sizemask(0), threshold(0) {
702  }
703  Data& operator=(const Data& src) {
704  THash::operator=(src);
705  sizemask = src.sizemask;
706  threshold = src.threshold;
707  return *this;
708  }
709 
710  #if defined(EVO_CPP11)
711  Data(Data&& src) {
712  THash::operator=(std::move(src));
713  sizemask = src.sizemask;
714  threshold = src.threshold;
715  src.sizemask = 0;
716  src.threshold = 0;
717  }
718 
719  Data& operator=(Data&& src) {
720  THash::operator=(std::move(src));
721  sizemask = src.sizemask;
722  threshold = src.threshold;
723  src.sizemask = 0;
724  src.threshold = 0;
725  return *this;
726  }
727  #endif
728  private:
729  // Copying is done via assignment operator
730  Data(const Data&);
731  };
732 
733  Data data_;
734 
735  void rehash(Size newsize) {
736  const float THRESHOLD = 0.7f;
737  data_.sizemask = newsize - 1;
738  data_.threshold = (Size)(newsize * THRESHOLD);
739  if (size_ == 0) {
740  // Empty, just resize buckets if needed
741  buckets_.resize(newsize);
742  } else {
743  // Grow and rehash
744  Buckets oldbuckets;
745  evo::swap(buckets_, oldbuckets);
746  buckets_.resize(newsize);
747 
748  bool created;
749  const Bucket* bucket;
750  const Bucket** cur = (const Bucket**)oldbuckets.data();
751  for (const Bucket** end=cur+oldbuckets.size(); cur < end; ++cur) {
752  if ((bucket=*cur) != NULL) {
753  for (Size i=0, iend=bucket->others.size(); i <= iend; ++i) {
754  const Value* item = (i == 0 ? &bucket->first : &bucket->others[i-1]);
755  Bucket* newbucket = buckets_.getitem(data_.hash(*item) & data_.sizemask, &created);
756  if (created) {
757  newbucket->first = *item;
758  } else {
759  Size index;
760  Value* newitem = (Value*)newbucket->search(index, *item, data_);
761  if (newitem == NULL)
762  newbucket->others.insert(index, *item);
763  }
764  }
765  }
766  }
767  }
768  }
769 
770  const Value* search(const Value& value) const {
771  if (size_ > 0) {
772  const Bucket* bucket = buckets_.item( data_.hash(value) & data_.sizemask );
773  if (bucket != NULL) {
774  if (data_(value, bucket->first) == 0) {
775  return &bucket->first;
776  } else {
777  Size index;
778  const Value* item = bucket->search(index, value, data_);
779  if (item != NULL)
780  return item;
781  }
782  }
783  }
784  return NULL;
785  }
786 };
787 
789 
790 #if defined(INCL_evo_string_h) || defined(DOXYGEN)
791 
796 #endif
797 
799 
800 }
801 #endif
Iter end() const
Get iterator at end (const).
Definition: sethash.h:353
Associative container with unique values for fast lookup.
Definition: set.h:119
Item getitem(const Key &key, bool *created=NULL)
Get item for key, creating if needed (mutable).
Definition: ptrlist.h:717
ThisType & capacitymin(Size min)
Set set capacity to at least given minimum.
Definition: sethash.h:464
Size size() const
Get size.
Definition: array.h:453
ThisType & operator=(const SetBaseType &src)
Assignment operator.
Definition: sethash.h:254
Evo Set interface.
#define EVO_CONTAINER_TYPE
Identify current class/struct as an EvoContainer.
Definition: meta.h:482
ThisType & clear()
Clear by removing all items.
Definition: sethash.h:283
Iter cbegin() const
Get iterator at first item (const).
Definition: sethash.h:333
TKey Item
Item type (same as Value)
Definition: set.h:127
T first(T val1, T val2)
Definition: alg.h:85
Set< TKey, TSize > SetBaseType
Set base type
Definition: set.h:123
#define EVO_ONCPP11(EXPR)
Compile EXPR only if C++11 support is detected, otherwise this is a no-op.
Definition: sys.h:259
Size size() const
Get set size (number of items).
Definition: set.h:230
Evo implementation detail: Hashing support.
Size capacity() const
Get set capacity.
Definition: sethash.h:294
ThisType & setempty()
Set as empty but not null.
Definition: sethash.h:280
ThisType & unshare()
Make sure data is not shared by allocating new buffer if needed (modifier).
Definition: ptrlist.h:790
void swap(T &a, T &b)
Swap contents of given objects.
Definition: sys.h:1602
Evo Pointer List.
Basic integer type.
Definition: type.h:980
SetHash(const SetBaseType &src)
Copy constructor.
Definition: sethash.h:203
bool operator==(const ThisType &set) const
Equality operator.
Definition: sethash.h:318
Iter begin() const
Get iterator at first item (const).
Definition: sethash.h:345
Size size_
Set size (number of items, automatically updated by concrete set members)
Definition: set.h:146
Evo Array container.
ThisType & operator=(ThisType &&src)
Move assignment operator (C++11).
Definition: sethash.h:237
const T * data() const
Get data pointer (const).
Definition: array.h:469
bool ordered() const
Get whether set is ordered.
Definition: sethash.h:297
bool null() const
Get whether null.
Definition: ptrlist.h:400
ThisType & resize(Size newsize)
Resize while preserving existing data (modifier).
Definition: ptrlist.h:814
bool operator!=(const ThisType &set) const
Inequality operator.
Definition: sethash.h:327
SetHash< TKey, THash, TSize > ThisType
This type.
Definition: sethash.h:190
bool null() const
Get whether set is null.
Definition: sethash.h:288
IteratorBi< ThisType > IterM
Iterator (mutable) - IteratorBi.
Definition: sethash.h:194
Bidirectional iterator.
Definition: iter.h:611
Value & add(const Value &value, bool update=false)
Add or update using given item.
Definition: sethash.h:476
ThisType & setempty()
Set as empty but not null.
Definition: ptrlist.h:385
static const EndT END
Special integer value for indicating end of items or no item.
Definition: type.h:1846
Hash & get_compare()
Get hash & comparison object being used for hashing and comparisons.
Definition: sethash.h:311
const ThisType & asconst() const
Explicitly use a const reference to this.
Definition: sethash.h:247
IterM iterM(const Value &value)
Find (lookup) iterator for given value (mutable).
Definition: sethash.h:380
IteratorDir
Iterator direction value.
Definition: iter.h:27
ThisType & operator=(const ThisType &src)
Assignment operator.
Definition: sethash.h:258
bool shared() const
Get whether shared.
Definition: ptrlist.h:428
THash Hash
Hashing type – default: CompareHash.
Definition: sethash.h:191
Reverse iterator direction.
Definition: iter.h:30
Evo C++ Library namespace.
Definition: alg.h:11
No iterator direction.
Definition: iter.h:28
SetHash< String > StrSetHash
SetHash using String values.
Definition: sethash.h:795
ThisType & setempty()
Set as empty but not null.
Definition: array.h:422
ThisType & capacity(Size size)
Set hash set capacity (capacity).
Definition: sethash.h:444
IterM end()
Get iterator at end.
Definition: sethash.h:349
ThisType & reserve(Size size)
Reserve space for new items.
Definition: sethash.h:471
TKey Key
Key type (same as Value)
Definition: set.h:125
Iter cend() const
Get iterator at end (const).
Definition: sethash.h:337
TSize Size
Size type for size values (must be unsigned integer) – default: SizeT.
Definition: set.h:124
Iter iter(const Value &value) const
Find (lookup) iterator for given value (const).
Definition: sethash.h:360
ThisType & unshare()
Make data unique by allocating new buffer, if needed (modifier).
Definition: sethash.h:433
const Item first() const
Get first non-null item (const).
Definition: ptrlist.h:474
Item itemM(Key index)
Get item at position (mutable).
Definition: ptrlist.h:780
bool shared() const
Get whether shared.
Definition: sethash.h:291
End iterator position.
Definition: iter.h:23
SetHash()
Constructor.
Definition: sethash.h:197
SetHash(ThisType &&src)
Move constructor (C++11).
Definition: sethash.h:228
TKey Value
Value type.
Definition: set.h:126
ThisType & clear()
Clear by removing all items.
Definition: ptrlist.h:313
bool contains(const Value &value) const
Get whether the set contains the given value.
Definition: sethash.h:356
const Value * getiter(IterKey &iterkey, const Value &value) const
Used by base class to get data to initialize iterator.
Definition: sethash.h:629
const Hash & get_compare() const
Get hash & comparison object being used for hashing and comparisons (const).
Definition: sethash.h:305
ThisType & remove(Key key)
Remove item and set as null (modifier).
Definition: ptrlist.h:987
IteratorBi< ThisType >::Const Iter
Iterator (const) - IteratorBi.
Definition: sethash.h:193
const Item item(Key index) const
Get item at position (const).
Definition: ptrlist.h:461
Size size() const
Get list size.
Definition: ptrlist.h:414
ThisType & set()
Set as null and empty.
Definition: ptrlist.h:339
const Item * data() const
Get data pointer for direct access (const).
Definition: ptrlist.h:437
SetHash(std::initializer_list< Value > init)
Sequence constructor (C++11).
Definition: sethash.h:218
IterM begin()
Get iterator at first item (mutable).
Definition: sethash.h:341
bool operator==(const SetBaseType &set) const
Equality operator.
Definition: set.h:251
SetHash(const ThisType &src)
Copy constructor.
Definition: sethash.h:211
Set implemented as a hash table.
Definition: sethash.h:167
T & min(T &a, T &b)
Returns lowest of given values.
Definition: alg.h:26