From cf889306d00c6c8c79ce05c79bbdd6cd35853f0d Mon Sep 17 00:00:00 2001 From: Mark Olesen <Mark.Olesen@esi-group.com> Date: Wed, 17 May 2017 10:18:14 +0200 Subject: [PATCH] ENH: added HashTable count, filter and generalized toc methods - Generalized means over filtering table entries based on their keys, values, or both. Either filter (retain), or optionally prune elements that satisfy the specified predicate. filterKeys and filterValues: - Take a unary predicate with the signature bool operator()(const Key& k); - filterEntries: Takes a binary predicate with the signature bool operator()(const Key& k, const T& v); == The predicates can be normal class methods, or provide on-the-fly using a C++ lambda. For example, wordRes goodFields = ...; allFieldNames.filterKeys ( [&goodFields](const word& k){ return goodFields.match(k); } ); Note that all classes that can match a string (eg, regExp, keyType, wordRe, wordRes) or that are derived from a Foam::string (eg, fileName, word) are provided with a corresponding bool operator()(const std::string&) that either performs a regular expression or a literal match. This allows such objects to be used directly as a unary predicate when filtering any string hash keys. Note that HashSet and hashedWordList both have the proper operator() methods that also allow them to be used as a unary predicate. - Similar predicate selection with the following: * tocKeys, tocValues, tocEntries * countKeys, countValues, countEntries except that instead of pruning, there is a simple logic inversion. --- applications/test/HashTable/Test-hashTable.C | 91 +++++++ .../containers/HashTables/HashSet/HashSet.C | 20 +- .../containers/HashTables/HashSet/HashSet.H | 49 +++- .../HashTables/HashTable/HashTable.C | 239 +++++++++++++++++- .../HashTables/HashTable/HashTable.H | 138 +++++++++- .../HashTables/HashTable/HashTableCore.C | 14 +- .../StaticHashTable/StaticHashTableCore.C | 14 +- .../primitives/strings/wordRes/wordRes.H | 2 +- 8 files changed, 525 insertions(+), 42 deletions(-) diff --git a/applications/test/HashTable/Test-hashTable.C b/applications/test/HashTable/Test-hashTable.C index 8352a88ae0c..5dd3ca0fa4e 100644 --- a/applications/test/HashTable/Test-hashTable.C +++ b/applications/test/HashTable/Test-hashTable.C @@ -234,6 +234,97 @@ int main() Info<<"\ntable1: " << table1 << endl; + // Start again + HashTable<scalar> table1start + { + {"aaa", 1.0}, + {"aba", 2.0}, + {"a_ca", 3.0}, + {"ada", 4.0}, + {"aeq_", 5.0}, + {"aaw", 6.0}, + {"abs", 7.0}, + {"a_cr", 8.0}, + {"adx", 9.0}, + {"ae_c", 10.0} + }; + + table1 = table1start; + Info<< "\ntable has keys: " + << flatOutput(table1.sortedToc()) << nl; + + wordRe matcher(".*_.*", wordRe::REGEX); + table1.filterKeys + ( + [&matcher](const word& k){ return matcher.match(k); } + ); + Info<< "retain things matching " << matcher << " => " + << flatOutput(table1.sortedToc()) << nl; + + table1 = table1start; + table1.filterKeys + ( + [&matcher](const word& k){ return matcher.match(k); }, + true + ); + + Info<< "prune things matching " << matcher << " => " + << flatOutput(table1.sortedToc()) << nl; + + // Same, without a lambda + table1 = table1start; + table1.filterKeys(matcher, true); + + Info<< "prune things matching " << matcher << " => " + << flatOutput(table1.sortedToc()) << nl; + + + // Same idea, but inverted logic inside the lambda + table1 = table1start; + table1.filterKeys + ( + [&matcher](const word& k){ return !matcher.match(k); }, + true + ); + + Info<< "prune things matching " << matcher << " => " + << flatOutput(table1.sortedToc()) << nl; + + + table1 = table1start; + Info<< "\ntable:" << table1 << nl; + + table1.filterValues + ( + [](const scalar& v){ return (v >= 5); } + ); + + Info<< "\ntable with values >= 5:" << table1 << nl; + + table1 = table1start; + Info<< "\ntable:" << table1 << nl; + + table1.filterEntries + ( + [&matcher](const word& k, const scalar& v) + { + return matcher(k) && (v >= 5); + } + ); + + Info<< "\ntable with values >= 5 and matching " << matcher + << table1 << nl; + + + table1 = table1start; + Info<< "\ntable:" << table1 << nl; + Info<< "has " + << table1.countValues([](const scalar& v) { return v >= 7; }) + << " values >= 7 with these keys: " + << table1.tocValues([](const scalar& v) { return v >= 7; }) + << nl; + + Info<< "\nDone\n"; return 0; diff --git a/src/OpenFOAM/containers/HashTables/HashSet/HashSet.C b/src/OpenFOAM/containers/HashTables/HashSet/HashSet.C index 77ab200a42c..5b1f45b8395 100644 --- a/src/OpenFOAM/containers/HashTables/HashSet/HashSet.C +++ b/src/OpenFOAM/containers/HashTables/HashSet/HashSet.C @@ -158,6 +158,20 @@ Foam::label Foam::HashSet<Key, Hash>::insert(std::initializer_list<Key> lst) // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * // +template<class Key, class Hash> +inline bool Foam::HashSet<Key, Hash>::operator()(const Key& key) const +{ + return this->found(key); +} + + +template<class Key, class Hash> +inline bool Foam::HashSet<Key, Hash>::operator[](const Key& key) const +{ + return this->found(key); +} + + template<class Key, class Hash> void Foam::HashSet<Key, Hash>::operator=(const UList<Key>& lst) { @@ -180,12 +194,6 @@ void Foam::HashSet<Key, Hash>::operator=(std::initializer_list<Key> lst) } -template<class Key, class Hash> -inline bool Foam::HashSet<Key, Hash>::operator[](const Key& key) const -{ - return this->found(key); -} - template<class Key, class Hash> bool Foam::HashSet<Key, Hash>::operator==(const HashSet<Key, Hash>& rhs) const diff --git a/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H b/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H index d6171ad5854..fe82253bc0f 100644 --- a/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H +++ b/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H @@ -156,22 +156,24 @@ public: // Edit //- Insert a new entry + // \return True if the entry inserted, which means that it did + // not previously exist in the set. bool insert(const Key& key) { return this->parent_type::insert(key, nil()); } //- Insert keys from the list of Key - // Return the number of new elements inserted + // \return The number of new elements inserted label insert(const UList<Key>& lst); //- Insert keys from the list of Key - // Return the number of new elements inserted + // \return The number of new elements inserted template<unsigned Size> label insert(const FixedList<Key, Size>& lst); //- Insert keys from a initializer list of Key - // Return the number of new elements inserted + // \return The number of new elements inserted label insert(std::initializer_list<Key> lst); //- Same as insert (cannot overwrite nil content) @@ -200,18 +202,21 @@ public: } //- Unset the specified key - same as erase + // \return True if the entry existed and was removed bool unset(const Key& key) { return this->parent_type::erase(key); } //- Unset the listed keys - same as erase + // \return The number of items removed label unset(const UList<Key>& lst) { return this->parent_type::erase(lst); } //- Unset the listed keys - same as erase + // \return The number of items removed template<unsigned Size> label unset(const FixedList<Key, Size>& lst) { @@ -219,11 +224,36 @@ public: } //- Unset the listed keys - same as erase + // \return The number of items removed label unset(std::initializer_list<Key> lst) { return this->parent_type::erase(lst); } + //- Not applicable for HashSet + template<class UnaryPredicate> + List<Key> tocValues(const UnaryPredicate&, const bool) = delete; + + //- Not applicable for HashSet + template<class BinaryPredicate> + List<Key> tocEntries(const BinaryPredicate&, const bool) = delete; + + //- Not applicable for HashSet + template<class UnaryPredicate> + label countValues(const UnaryPredicate&, const bool) = delete; + + //- Not applicable for HashSet + template<class BinaryPredicate> + label countEntries(const BinaryPredicate&, const bool) = delete; + + //- Not applicable for HashSet + template<class UnaryPredicate> + label filterValues(const UnaryPredicate&, const bool) = delete; + + //- Not applicable for HashSet + template<class BinaryPredicate> + label filterEntries(const BinaryPredicate&, const bool) = delete; + // STL iterators @@ -248,12 +278,15 @@ public: // Member Operators - //- This operation doesn't make much sense for a hash-set - void operator()(const Key& key) = delete; - //- Return true if the entry exists, same as found() + inline bool operator()(const Key& key) const; + + //- Return true if the entry exists, same as found(). inline bool operator[](const Key& key) const; + + // Comparison + //- Equality. Two hashset are equal when they have the same keys. // Independent of table size or order. bool operator==(const this_type& rhs) const; @@ -262,6 +295,8 @@ public: bool operator!=(const this_type& rhs) const; + // Assignment + //- Assignment from a UList of keys void operator=(const UList<Key>& lst); @@ -273,6 +308,8 @@ public: void operator=(std::initializer_list<Key> lst); + // Logical operations + //- Combine entries from HashSets void operator|=(const HashSet<Key, Hash>& rhs); diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C index 276f617dba1..2c8167adcee 100644 --- a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C +++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C @@ -231,25 +231,169 @@ Foam::HashTable<T, Key, Hash>::find template<class T, class Key, class Hash> Foam::List<Key> Foam::HashTable<T, Key, Hash>::toc() const { - List<Key> keys(nElmts_); - label keyI = 0; + List<Key> keyLst(nElmts_); + label count = 0; for (const_iterator iter = cbegin(); iter != cend(); ++iter) { - keys[keyI++] = iter.key(); + keyLst[count++] = iter.key(); } - return keys; + return keyLst; } template<class T, class Key, class Hash> Foam::List<Key> Foam::HashTable<T, Key, Hash>::sortedToc() const { - List<Key> sortedLst = this->toc(); - sort(sortedLst); + List<Key> keyLst = this->toc(); + Foam::sort(keyLst); - return sortedLst; + return keyLst; +} + + +template<class T, class Key, class Hash> +template<class UnaryPredicate> +Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocKeys +( + const UnaryPredicate& pred, + const bool invert +) const +{ + List<Key> keyLst(nElmts_); + label count = 0; + + for (const_iterator iter = cbegin(); iter != cend(); ++iter) + { + if ((pred(iter.key()) ? !invert : invert)) + { + keyLst[count++] = iter.key(); + } + } + + keyLst.setSize(count); + Foam::sort(keyLst); + + return keyLst; +} + + +template<class T, class Key, class Hash> +template<class UnaryPredicate> +Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocValues +( + const UnaryPredicate& pred, + const bool invert +) const +{ + List<Key> keyLst(nElmts_); + label count = 0; + + for (const_iterator iter = cbegin(); iter != cend(); ++iter) + { + if ((pred(iter.object()) ? !invert : invert)) + { + keyLst[count++] = iter.key(); + } + } + + keyLst.setSize(count); + Foam::sort(keyLst); + + return keyLst; +} + + +template<class T, class Key, class Hash> +template<class BinaryPredicate> +Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocEntries +( + const BinaryPredicate& pred, + const bool invert +) const +{ + List<Key> keyLst(nElmts_); + label count = 0; + + for (const_iterator iter = cbegin(); iter != cend(); ++iter) + { + if ((pred(iter.key(), iter.object()) ? !invert : invert)) + { + keyLst[count++] = iter.key(); + } + } + + keyLst.setSize(count); + Foam::sort(keyLst); + + return keyLst; +} + + +template<class T, class Key, class Hash> +template<class UnaryPredicate> +Foam::label Foam::HashTable<T, Key, Hash>::countKeys +( + const UnaryPredicate& pred, + const bool invert +) const +{ + label count = 0; + + for (const_iterator iter = cbegin(); iter != cend(); ++iter) + { + if ((pred(iter.key()) ? !invert : invert)) + { + ++count; + } + } + + return count; +} + + +template<class T, class Key, class Hash> +template<class UnaryPredicate> +Foam::label Foam::HashTable<T, Key, Hash>::countValues +( + const UnaryPredicate& pred, + const bool invert +) const +{ + label count = 0; + + for (const_iterator iter = cbegin(); iter != cend(); ++iter) + { + if ((pred(iter.object()) ? !invert : invert)) + { + ++count; + } + } + + return count; +} + + +template<class T, class Key, class Hash> +template<class BinaryPredicate> +Foam::label Foam::HashTable<T, Key, Hash>::countEntries +( + const BinaryPredicate& pred, + const bool invert +) const +{ + label count = 0; + + for (const_iterator iter = cbegin(); iter != cend(); ++iter) + { + if ((pred(iter.key(), iter.object()) ? !invert : invert)) + { + ++count; + } + } + + return count; } @@ -617,6 +761,87 @@ void Foam::HashTable<T, Key, Hash>::transfer(HashTable<T, Key, Hash>& ht) } +template<class T, class Key, class Hash> +template<class UnaryPredicate> +Foam::label Foam::HashTable<T, Key, Hash>::filterKeys +( + const UnaryPredicate& pred, + const bool pruning +) +{ + label changed = 0; + + for (iterator iter = begin(); iter != end(); ++iter) + { + // Matches? either prune (pruning) or keep (!pruning) + if + ( + (pred(iter.key()) ? pruning : !pruning) + && erase(iter) + ) + { + ++changed; + } + } + + return changed; +} + + +template<class T, class Key, class Hash> +template<class UnaryPredicate> +Foam::label Foam::HashTable<T, Key, Hash>::filterValues +( + const UnaryPredicate& pred, + const bool pruning +) +{ + label changed = 0; + + for (iterator iter = begin(); iter != end(); ++iter) + { + // Matches? either prune (pruning) or keep (!pruning) + if + ( + (pred(iter.object()) ? pruning : !pruning) + && erase(iter) + ) + { + ++changed; + } + } + + return changed; +} + + +template<class T, class Key, class Hash> +template<class BinaryPredicate> +Foam::label Foam::HashTable<T, Key, Hash>::filterEntries +( + const BinaryPredicate& pred, + const bool pruning +) +{ + label changed = 0; + + for (iterator iter = begin(); iter != end(); ++iter) + { + // Matches? either prune (pruning) or keep (!pruning) + if + ( + (pred(iter.key(), iter.object()) ? pruning : !pruning) + && erase(iter) + ) + { + ++changed; + } + } + + return changed; +} + + // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * // template<class T, class Key, class Hash> diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H index 9eef011d10e..3b3338084d9 100644 --- a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H +++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H @@ -255,7 +255,7 @@ private: inline label hashKeyIndex(const Key& key) const; //- Assign a new hash-entry to a possibly already existing key. - // Return true if the new entry was set. + // \return True if the new entry was set. bool set(const Key& key, const T& obj, const bool protect); @@ -330,22 +330,83 @@ public: //- Return hashed entry if it exists, or return the given default inline const T& lookup(const Key& key, const T& deflt) const; + + // Table of contents + //- Return the table of contents List<Key> toc() const; //- Return the table of contents as a sorted list List<Key> sortedToc() const; + //- Return the sorted table of contents with keys that satisfy + // the unary predicate, optionally with inverted logic. + template<class UnaryPredicate> + List<Key> tocKeys + ( + const UnaryPredicate& pred, + const bool invert = false + ) const; + + //- Return the sorted table of contents with values that satisfy + // the unary predicate, optionally with inverted logic. + template<class UnaryPredicate> + List<Key> tocValues + ( + const UnaryPredicate& pred, + const bool invert = false + ) const; + + //- Return the sorted table of contents with keys/values that satisfy + // the binary predicate, optionally with inverted logic. + template<class BinaryPredicate> + List<Key> tocEntries + ( + const BinaryPredicate& pred, + const bool invert = false + ) const; + + + // Counting + + //- Count the number of keys that satisfy the unary predicate, + // optionally with inverted logic. + template<class UnaryPredicate> + label countKeys + ( + const UnaryPredicate& pred, + const bool invert = false + ) const; + + //- Count the number of values that satisfy the unary predicate, + // optionally with inverted logic. + template<class UnaryPredicate> + label countValues + ( + const UnaryPredicate& pred, + const bool invert = false + ) const; + + //- Count the number of entries that satisfy the binary predicate, + // optionally with inverted logic. + template<class BinaryPredicate> + label countEntries + ( + const BinaryPredicate& pred, + const bool invert = false + ) const; + // Edit //- Insert a new entry - // Return true if the entry inserted, which means that it did + // \return True if the entry inserted, which means that it did // not previously exist in the table. inline bool insert(const Key& key, const T& obj); //- Assign a new entry, overwriting existing entries. - // Returns true. + // + // \return True, since it always overwrites any entries. inline bool set(const Key& key, const T& obj); //- Erase an entry specified by given iterator @@ -357,30 +418,34 @@ public: // auto iter = table.find(unknownKey); // table.erase(iter); // \endcode - // which is what \code table.erase(unknownKey) \endcode does anyhow + // which is what \code table.erase(unknownKey) \endcode does anyhow. + // + // \return True if the corresponding entry existed and was removed bool erase(const iterator& iter); //- Erase an entry specified by the given key + // \return True if the entry existed and was removed bool erase(const Key& key); //- Remove table entries given by the listed keys - // Return the number of elements removed + // \return The number of items removed label erase(const UList<Key>& keys); //- Remove table entries given by the listed keys - // Return the number of elements removed + // \return The number of items removed template<unsigned Size> label erase(const FixedList<Key, Size>& keys); //- Remove table entries given by the listed keys - // Return the number of elements removed + // \return The number of items removed label erase(std::initializer_list<Key> keys); //- Remove table entries given by keys of the other hash-table. - // Return the number of elements removed. // // The other hash-table must have the same type of key, but the // type of values held and the hashing function are arbitrary. + // + // \return The number of items removed template<class AnyType, class AnyHash> label erase(const HashTable<AnyType, Key, AnyHash>& other); @@ -388,9 +453,66 @@ public: // // The other hash-table must have the same type of key, but the // type of values held and the hashing function are arbitrary. + // + // \return The number of items changed (removed) template<class AnyType, class AnyHash> label retain(const HashTable<AnyType, Key, AnyHash>& other); + //- Generalized means to filter table entries based on their keys. + // Keep (or optionally prune) entries with keys that satisfy + // the unary predicate, which has the following signature: + // \code + // bool operator()(const Key& k); + // \endcode + // + // For example, + // \code + // wordRes goodFields = ...; + // allFieldNames.filterKeys + // ( + // [&goodFields](const word& k){ return goodFields.match(k); } + // ); + // \endcode + // + // \return The number of items changed (removed) + template<class UnaryPredicate> + label filterKeys + ( + const UnaryPredicate& pred, + const bool pruning = false + ); + + //- Generalized means to filter table entries based on their values. + // Keep (or optionally prune) entries with values that satisfy + // the unary predicate, which has the following signature: + // \code + // bool operator()(const T& v); + // \endcode + // + // \return The number of items changed (removed) + template<class UnaryPredicate> + label filterValues + ( + const UnaryPredicate& pred, + const bool pruning = false + ); + + //- Generalized means to filter table entries based on their key/value. + // Keep (or optionally prune) entries with keys/values that satisfy + // the binary predicate, which has the following signature: + // \code + // bool operator()(const Key& k, const T& v); + // \endcode + // + // \return The number of items changed (removed) + template<class BinaryPredicate> + label filterEntries + ( + const BinaryPredicate& pred, + const bool pruning = false + ); + + //- Resize the hash table for efficiency void resize(const label sz); diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.C b/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.C index e57192f1e36..9a8b205b9ed 100644 --- a/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.C +++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.C @@ -46,26 +46,26 @@ Foam::label Foam::HashTableCore::canonicalSize(const label requested_size) { return 0; } + else if (requested_size >= maxTableSize) + { + return maxTableSize; + } - // Enforce power of two - makes for a vey fast modulus etc. + // Enforce power of two - makes for a very fast modulus. // Use unsigned for these calculations. // // - The lower limit (8) is somewhat arbitrary, but if the hash table // is too small, there will be many direct table collisions. - // - The uper limit (approx. labelMax/4) must be a power of two, + // - The upper limit (approx. labelMax/4) must be a power of two, // need not be extremely large for hashing. uLabel powerOfTwo = 8; // lower-limit const uLabel size = requested_size; - if (size < powerOfTwo) + if (size <= powerOfTwo) { return powerOfTwo; } - else if (requested_size >= maxTableSize) - { - return maxTableSize; - } else if (size & (size-1)) // <- Modulus of i^2 { // Determine power-of-two. Brute-force is fast enough. diff --git a/src/OpenFOAM/containers/HashTables/StaticHashTable/StaticHashTableCore.C b/src/OpenFOAM/containers/HashTables/StaticHashTable/StaticHashTableCore.C index 31c02ebf5ec..6ce8fc31ba9 100644 --- a/src/OpenFOAM/containers/HashTables/StaticHashTable/StaticHashTableCore.C +++ b/src/OpenFOAM/containers/HashTables/StaticHashTable/StaticHashTableCore.C @@ -46,26 +46,26 @@ Foam::label Foam::StaticHashTableCore::canonicalSize(const label requested_size) { return 0; } + else if (requested_size >= maxTableSize) + { + return maxTableSize; + } - // Enforce power of two - makes for a vey fast modulus etc. + // Enforce power of two - makes for a very fast modulus. // Use unsigned for these calculations. // // - The lower limit (8) is somewhat arbitrary, but if the hash table // is too small, there will be many direct table collisions. - // - The uper limit (approx. labelMax/4) must be a power of two, + // - The upper limit (approx. labelMax/4) must be a power of two, // need not be extremely large for hashing. uLabel powerOfTwo = 8; // lower-limit const uLabel size = requested_size; - if (size < powerOfTwo) + if (size <= powerOfTwo) { return powerOfTwo; } - else if (requested_size >= maxTableSize) - { - return maxTableSize; - } else if (size & (size-1)) // <- Modulus of i^2 { // Determine power-of-two. Brute-force is fast enough. diff --git a/src/OpenFOAM/primitives/strings/wordRes/wordRes.H b/src/OpenFOAM/primitives/strings/wordRes/wordRes.H index 7a62d9d101f..5cfe6b9b575 100644 --- a/src/OpenFOAM/primitives/strings/wordRes/wordRes.H +++ b/src/OpenFOAM/primitives/strings/wordRes/wordRes.H @@ -69,7 +69,7 @@ public: // Constructors //- Construct from a list of wordRe - inline wordRes(const UList<wordRe>& lst); + inline wordRes(const UList<wordRe>& list); // Static Constructors, Helpers -- GitLab