mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
ENH: ListOp::inplaceMapValue using a Map<label> for the mapping.
For example, with some HashTable or Map container of models
{ model0 => 1, model1 => 4, model2 => 5, model3 => 12, model4 => 15, }
specify the remapping
Map<label> mapper({{1, 3}, {2, 6}, {3, 12}, {5, 8}});
inplaceMapValue(mapper, models) then yields
{ model0 => 3, model1 => 4, model2 => 8, model3 => 12, model4 => 15, }
--
ENH: extend bitSet::count() to optionally count unset bits instead.
--
ENH: BitOps compatibility methods for boolList.
- These ease coding that uses a boolList instead of bitSet and use
short-circuit logic when possible.
Eg, when 'bitset' and 'bools' contain the same information
bitset.count() <-> BitOps::count(bools)
bitset.all() <-> BitOps::all(bools)
bitset.any() <-> BitOps::any(bools)
bitset.none() <-> BitOps::none(bools)
These methods can then be used directly in parameters or in logic.
Eg,
returnReduce(bitset.any(), orOp<bool>());
returnReduce(BitOps::any(bools), orOp<bool>());
if (BitOps::any(bools)) ...
This commit is contained in:
@ -35,9 +35,82 @@ Description
|
||||
#include "SubList.H"
|
||||
#include "ListOps.H"
|
||||
#include "FlatOutput.H"
|
||||
#include "UPtrList.H"
|
||||
|
||||
using namespace Foam;
|
||||
|
||||
|
||||
// Proof-of-concept for sorted HashTable output
|
||||
// .. but yet not really convincing
|
||||
|
||||
|
||||
// Forward declarations
|
||||
template<class T, class Key, class Hash> class HashSorter;
|
||||
|
||||
template<class T, class Key, class Hash>
|
||||
Ostream& operator<<(Ostream& os, const HashSorter<T, Key, Hash>& sorter);
|
||||
|
||||
|
||||
template<class T, class Key, class Hash>
|
||||
class HashSorter
|
||||
{
|
||||
const HashTable<T,Key,Hash>& table;
|
||||
|
||||
public:
|
||||
|
||||
HashSorter(const HashTable<T,Key,Hash>& ht)
|
||||
:
|
||||
table(ht)
|
||||
{}
|
||||
|
||||
friend Ostream& operator<<
|
||||
(
|
||||
Ostream& os,
|
||||
const HashSorter<T, Key, Hash>& sorter
|
||||
)
|
||||
{
|
||||
const auto& tbl = sorter.table;
|
||||
const label len = tbl.size();
|
||||
|
||||
// Should actually be able to get the flat entries or iterators
|
||||
// and sort that instead.
|
||||
|
||||
UPtrList<const Key> keys(len);
|
||||
label count = 0;
|
||||
|
||||
for (auto iter = tbl.cbegin(); iter != tbl.cend(); ++iter)
|
||||
{
|
||||
keys.set(count, &(iter.key()));
|
||||
++count;
|
||||
}
|
||||
|
||||
labelList order(identity(len));
|
||||
std::sort
|
||||
(
|
||||
order.begin(),
|
||||
order.end(),
|
||||
ListOps::less<UPtrList<const Key>>(keys)
|
||||
);
|
||||
|
||||
// Size and start list delimiter
|
||||
os << nl << len << nl << token::BEGIN_LIST << nl;
|
||||
|
||||
// Contents
|
||||
for (const label idx : order)
|
||||
{
|
||||
const auto& k = keys[idx];
|
||||
|
||||
os << k << token::SPACE << tbl[k] << nl;
|
||||
}
|
||||
|
||||
os << token::END_LIST; // End list delimiter
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
template<class ListType>
|
||||
@ -135,6 +208,42 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
|
||||
// Test remapping
|
||||
{
|
||||
Info<< nl << "Test inplaceMapValue" << nl << nl;
|
||||
|
||||
HashTable<label> input;
|
||||
typedef HashSorter<label, label, Hash<label>> Mapper;
|
||||
typedef HashSorter<label, word, string::hash> Sorter;
|
||||
|
||||
for (label i=0; i < 10; ++i)
|
||||
{
|
||||
input.insert(word::printf("word%d", i), i);
|
||||
}
|
||||
|
||||
Map<label> mapper;
|
||||
{
|
||||
// A mapping that does some, but not all values
|
||||
|
||||
labelList rndList(identity(16)); // larger range
|
||||
shuffle(rndList);
|
||||
|
||||
for (label i=0; i < 8; ++i) // smaller sample
|
||||
{
|
||||
mapper.insert(rndList[i], 100*i);
|
||||
}
|
||||
}
|
||||
|
||||
Info<< nl
|
||||
<< "input: " << Sorter(input) << nl
|
||||
<< "mapper: " << Mapper(mapper) << nl << nl;
|
||||
|
||||
inplaceMapValue(mapper, input);
|
||||
|
||||
Info<< nl << "output: " << Sorter(input) << nl;
|
||||
}
|
||||
|
||||
|
||||
Info<< "\nEnd\n" << endl;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -49,6 +49,7 @@ inline Ostream& report
|
||||
{
|
||||
Info<< "size=" << bitset.size() << "/" << bitset.capacity()
|
||||
<< " count=" << bitset.count()
|
||||
<< " !count=" << bitset.count(false)
|
||||
<< " all:" << bitset.all()
|
||||
<< " any:" << bitset.any()
|
||||
<< " none:" << bitset.none() << nl;
|
||||
@ -63,6 +64,19 @@ inline Ostream& report
|
||||
}
|
||||
|
||||
|
||||
inline Ostream& report(const UList<bool>& bools)
|
||||
{
|
||||
Info<< "size=" << bools.size()
|
||||
<< " count=" << BitOps::count(bools)
|
||||
<< " !count=" << BitOps::count(bools, false)
|
||||
<< " all:" << BitOps::all(bools)
|
||||
<< " any:" << BitOps::any(bools)
|
||||
<< " none:" << BitOps::none(bools) << nl;
|
||||
|
||||
return Info;
|
||||
}
|
||||
|
||||
|
||||
template<class UIntType>
|
||||
std::string toString(UIntType value, char off='.', char on='1')
|
||||
{
|
||||
@ -304,6 +318,35 @@ int main(int argc, char *argv[])
|
||||
<< flatOutput(bools1.toc()) << endl;
|
||||
}
|
||||
|
||||
|
||||
// Check bitSet vs boolList
|
||||
{
|
||||
boolList bools(list4.values());
|
||||
|
||||
Info<< nl << "Check BitOps on boolList" << nl << nl;
|
||||
|
||||
Info<<"bitSet ";
|
||||
report(list4);
|
||||
|
||||
list4.shrink();
|
||||
Info<<"shrunk ";
|
||||
report(list4);
|
||||
|
||||
Info<< nl;
|
||||
|
||||
Info<<"bools ";
|
||||
report(bools);
|
||||
|
||||
bools = false;
|
||||
Info<<"bools (all unset) ";
|
||||
report(bools);
|
||||
|
||||
bools = true;
|
||||
Info<<"bools (all set) ";
|
||||
report(bools);
|
||||
}
|
||||
|
||||
Info<< "\nDone" << nl << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -184,9 +184,10 @@ int main(int argc, char *argv[])
|
||||
Info<< BitOps::bitInfo<unsigned>(BitOps::repeat_value<unsigned, 3>(1u))
|
||||
<< nl;
|
||||
|
||||
Info<< BitOps::bitInfo<unsigned>(BitOps::repeat_value<unsigned>(1u))
|
||||
Info<< BitOps::bitInfo<unsigned>(BitOps::repeat_value<unsigned, 1>(1u))
|
||||
<< nl;
|
||||
|
||||
|
||||
Info << "---\nEnd\n" << endl;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -36,7 +36,9 @@ Description
|
||||
#define BitOps_H
|
||||
|
||||
#include "label.H"
|
||||
#include "UList.H"
|
||||
#include "Ostream.H"
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
@ -54,6 +56,36 @@ namespace BitOps
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
//- Count number of 'true' entries.
|
||||
// \param val can be set to false to count the number of false values instead
|
||||
// For compatibility with bitSet::count()
|
||||
inline unsigned int count(const UList<bool>& bools, const bool val=true)
|
||||
{
|
||||
return std::count(bools.begin(), bools.end(), val);
|
||||
}
|
||||
|
||||
//- True if all entries are 'true' or if the set is empty.
|
||||
// For compatibility with bitSet::all()
|
||||
inline bool all(const UList<bool>& bools)
|
||||
{
|
||||
return std::all_of(bools.begin(), bools.end(), [](bool b){return b;});
|
||||
}
|
||||
|
||||
//- True if any entries are 'true'.
|
||||
// For compatibility with bitSet::any()
|
||||
inline bool any(const UList<bool>& bools)
|
||||
{
|
||||
return std::any_of(bools.begin(), bools.end(), [](bool b){return b;});
|
||||
}
|
||||
|
||||
//- True if no entries are 'true'.
|
||||
// For compatibility with bitSet::none()
|
||||
inline bool none(const UList<bool>& bools)
|
||||
{
|
||||
return std::none_of(bools.begin(), bools.end(), [](bool b){return b;});
|
||||
}
|
||||
|
||||
|
||||
//- Count arbitrary number of bits (of an integral type)
|
||||
template<class UIntType>
|
||||
inline unsigned int bit_count(UIntType x)
|
||||
@ -90,6 +122,11 @@ inline unsigned int bit_count(uint64_t x)
|
||||
|
||||
|
||||
//- Repeat a value of the given BitWidth into the destination output type.
|
||||
//
|
||||
// \note when BitWidth is 1, it is better to do directly.
|
||||
// \code
|
||||
// (val ? ~0u : 0u)
|
||||
// \endcode
|
||||
template<class UIntType, unsigned BitWidth>
|
||||
inline UIntType repeat_value(unsigned val)
|
||||
{
|
||||
@ -150,11 +187,10 @@ template<class UIntType>
|
||||
struct bitInfo
|
||||
{
|
||||
typedef UIntType value_type;
|
||||
|
||||
value_type value;
|
||||
|
||||
//- Null constructible as zero
|
||||
bitInfo() : value(0) {}
|
||||
constexpr bitInfo() noexcept : value(0) {}
|
||||
|
||||
//- Value construct
|
||||
explicit bitInfo(UIntType val) : value(val) {}
|
||||
|
||||
@ -189,7 +189,9 @@ public:
|
||||
inline bool uniform() const;
|
||||
|
||||
//- Count number of bits set.
|
||||
inline unsigned int count() const;
|
||||
// \param on can be set to false to count the number of unset bits
|
||||
// instead.
|
||||
inline unsigned int count(const bool on=true) const;
|
||||
|
||||
//- True if any bits in the other bitset intersect (are the same).
|
||||
//
|
||||
|
||||
@ -408,7 +408,7 @@ inline bool Foam::bitSet::uniform() const
|
||||
}
|
||||
|
||||
|
||||
inline unsigned int Foam::bitSet::count() const
|
||||
inline unsigned int Foam::bitSet::count(const bool on) const
|
||||
{
|
||||
unsigned int total = 0;
|
||||
|
||||
@ -419,6 +419,12 @@ inline unsigned int Foam::bitSet::count() const
|
||||
total += BitOps::bit_count(blocks_[blocki]);
|
||||
}
|
||||
|
||||
if (!on)
|
||||
{
|
||||
// Return the number of bits that are off.
|
||||
return (unsigned(size()) - total);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ License
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "ListOps.H"
|
||||
#include <numeric>
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
@ -83,7 +84,7 @@ Foam::labelListList Foam::invertOneToMany
|
||||
|
||||
for (label i=0; i<len; ++i)
|
||||
{
|
||||
inverse[i].setSize(sizes[i]);
|
||||
inverse[i].resize(sizes[i]);
|
||||
sizes[i] = 0; // reset size counter
|
||||
}
|
||||
|
||||
@ -101,15 +102,10 @@ Foam::labelListList Foam::invertOneToMany
|
||||
}
|
||||
|
||||
|
||||
Foam::labelList Foam::identity(const label len, const label start)
|
||||
Foam::labelList Foam::identity(const label len, label start)
|
||||
{
|
||||
labelList map(len);
|
||||
|
||||
// Same as std::iota(map.begin(), map.end(), start);
|
||||
for (label i = 0; i < len; ++i)
|
||||
{
|
||||
map[i] = i + start;
|
||||
}
|
||||
std::iota(map.begin(), map.end(), start);
|
||||
|
||||
return map;
|
||||
}
|
||||
@ -127,20 +123,22 @@ Foam::bitSet Foam::reorder
|
||||
bitSet output;
|
||||
output.reserve(len);
|
||||
|
||||
for (label i=0; i < len; ++i)
|
||||
for
|
||||
(
|
||||
label pos = input.find_first();
|
||||
pos >= 0 && pos < len;
|
||||
pos = input.find_next(pos)
|
||||
)
|
||||
{
|
||||
if (input.test(i))
|
||||
{
|
||||
const label newIdx = oldToNew[i];
|
||||
const label newIdx = oldToNew[pos];
|
||||
|
||||
if (newIdx >= 0)
|
||||
{
|
||||
output.set(newIdx);
|
||||
}
|
||||
else if (!prune)
|
||||
{
|
||||
output.set(i);
|
||||
}
|
||||
if (newIdx >= 0)
|
||||
{
|
||||
output.set(newIdx);
|
||||
}
|
||||
else if (!prune)
|
||||
{
|
||||
output.set(pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -45,6 +45,7 @@ SourceFiles
|
||||
#include "FlatOutput.H"
|
||||
#include "labelList.H"
|
||||
#include "HashSet.H"
|
||||
#include "Map.H"
|
||||
#include "bitSet.H"
|
||||
#include "ops.H"
|
||||
|
||||
@ -144,17 +145,45 @@ void inplaceReorder
|
||||
);
|
||||
|
||||
|
||||
// Variants to work with iterators and sparse tables.
|
||||
// Need to have iterators and insert()
|
||||
|
||||
//- Map values. Do not map negative values.
|
||||
template<class Container>
|
||||
void inplaceMapValue(const labelUList& oldToNew, Container& input);
|
||||
|
||||
//- Recreate with mapped keys. Do not map elements with negative key.
|
||||
//- Rewrite with mapped keys. Ignore elements with negative key.
|
||||
// The Container is some type of HashTable or Map with a label for its key.
|
||||
template<class Container>
|
||||
void inplaceMapKey(const labelUList& oldToNew, Container& input);
|
||||
|
||||
//- Map values. Ignore negative values.
|
||||
// \return number of values changed
|
||||
template<class Container>
|
||||
label inplaceMapValue(const labelUList& oldToNew, Container& input);
|
||||
|
||||
//- Use mapper as a lookup to modify the values of input.
|
||||
//
|
||||
// \return number of values changed
|
||||
//
|
||||
// \code
|
||||
// Map<label> mapper({{1, 3}, {2, 6}, {3, 12}, {5, 8}});
|
||||
//
|
||||
// HashTable<label> models with
|
||||
// {
|
||||
// model0 => 1,
|
||||
// model1 => 4,
|
||||
// model2 => 5,
|
||||
// }
|
||||
//
|
||||
// inplaceMapValue(mapper, models);
|
||||
//
|
||||
// Now contains
|
||||
// {
|
||||
// model0 => 3,
|
||||
// model1 => 4,
|
||||
// model2 => 8,
|
||||
// }
|
||||
// \endcode
|
||||
//
|
||||
// \note the modification occurs in a single pass and will not
|
||||
// remap more than once.
|
||||
template<class Container>
|
||||
label inplaceMapValue(const Map<label>& mapper, Container& input);
|
||||
|
||||
|
||||
//- Generate the (stable) sort order for the list
|
||||
template<class T>
|
||||
@ -284,7 +313,7 @@ List<OutputIntListType> invertManyToMany
|
||||
|
||||
//- Create identity map of the given length with (map[i] == i)
|
||||
// Optionally with an alternative start index, so that (map[i] == i+start)
|
||||
labelList identity(const label len, const label start=0);
|
||||
labelList identity(const label len, label start=0);
|
||||
|
||||
//- Find first occurence of given element and return index,
|
||||
// return -1 if not found. Linear search.
|
||||
@ -399,6 +428,44 @@ struct uniqueEqOp
|
||||
};
|
||||
|
||||
|
||||
// Public classes
|
||||
|
||||
//- A list compare binary predicate for normal sort
|
||||
template<class ListType>
|
||||
struct less
|
||||
{
|
||||
const ListType& values;
|
||||
|
||||
less(const ListType& list)
|
||||
:
|
||||
values(list)
|
||||
{}
|
||||
|
||||
bool operator()(const label a, const label b) const
|
||||
{
|
||||
return values[a] < values[b];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//- A list compare binary predicate for reverse sort
|
||||
template<class ListType>
|
||||
struct greater
|
||||
{
|
||||
const ListType& values;
|
||||
|
||||
greater(const ListType& list)
|
||||
:
|
||||
values(list)
|
||||
{}
|
||||
|
||||
bool operator()(const label a, const label b) const
|
||||
{
|
||||
return values[b] < values[a];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//- Set various locations of the list with a specified value.
|
||||
//
|
||||
// \param list the list to modify
|
||||
|
||||
@ -85,7 +85,7 @@ ListType Foam::reorder
|
||||
ListType output(len);
|
||||
output.resize(len); // Consistent sizing (eg, DynamicList)
|
||||
|
||||
label maxIdx = -1; // For pruning: newSize = maxIdx+1
|
||||
label maxIdx = -1; // For pruning: The new size = maxIdx+1
|
||||
for (label i=0; i < len; ++i)
|
||||
{
|
||||
const label newIdx = oldToNew[i];
|
||||
@ -136,7 +136,7 @@ void Foam::inplaceReorder
|
||||
ListType output(len);
|
||||
output.resize(len); // Consistent sizing (eg, DynamicList)
|
||||
|
||||
label maxIdx = -1; // For pruning: newSize = maxIdx+1
|
||||
label maxIdx = -1; // For pruning: The new size = maxIdx+1
|
||||
for (label i=0; i < len; ++i)
|
||||
{
|
||||
const label newIdx = oldToNew[i];
|
||||
@ -179,7 +179,7 @@ Foam::PackedList<Width> Foam::reorder
|
||||
|
||||
PackedList<Width> output(len);
|
||||
|
||||
label maxIdx = -1; // For pruning: newSize = maxIdx+1
|
||||
label maxIdx = -1; // For pruning: The new size = maxIdx+1
|
||||
for (label i=0; i < len; ++i)
|
||||
{
|
||||
const auto& val = input.get(i);
|
||||
@ -232,27 +232,6 @@ void Foam::inplaceReorder
|
||||
}
|
||||
|
||||
|
||||
template<class Container>
|
||||
void Foam::inplaceMapValue
|
||||
(
|
||||
const labelUList& oldToNew,
|
||||
Container& input
|
||||
)
|
||||
{
|
||||
for (auto iter = input.begin(); iter != input.end(); ++iter)
|
||||
{
|
||||
const label oldIdx = iter.object();
|
||||
if (oldIdx >= 0)
|
||||
{
|
||||
// Could enforce (oldIdx < oldToNew.size())
|
||||
// ... or just rely on FULLDEBUG from UList
|
||||
|
||||
iter.object() = oldToNew[oldIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class Container>
|
||||
void Foam::inplaceMapKey
|
||||
(
|
||||
@ -278,6 +257,67 @@ void Foam::inplaceMapKey
|
||||
}
|
||||
|
||||
|
||||
template<class Container>
|
||||
Foam::label Foam::inplaceMapValue
|
||||
(
|
||||
const labelUList& oldToNew,
|
||||
Container& input
|
||||
)
|
||||
{
|
||||
label nChanged = 0;
|
||||
|
||||
for (auto iter = input.begin(); iter != input.end(); ++iter)
|
||||
{
|
||||
const label oldIdx = iter.object();
|
||||
if (oldIdx >= 0)
|
||||
{
|
||||
// Could enforce (oldIdx < oldToNew.size())
|
||||
// ... or just rely on FULLDEBUG from UList
|
||||
|
||||
const label newIdx = oldToNew[oldIdx];
|
||||
|
||||
if (oldIdx != newIdx)
|
||||
{
|
||||
iter.object() = newIdx;
|
||||
++nChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nChanged;
|
||||
}
|
||||
|
||||
|
||||
template<class Container>
|
||||
Foam::label Foam::inplaceMapValue
|
||||
(
|
||||
const Map<label>& mapper,
|
||||
Container& input
|
||||
)
|
||||
{
|
||||
if (mapper.empty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
label nChanged = 0;
|
||||
|
||||
for (auto iter = input.begin(); iter != input.end(); ++iter)
|
||||
{
|
||||
label& value = iter.object();
|
||||
|
||||
auto mapIter = mapper.find(value);
|
||||
if (mapIter.found() && value != *mapIter)
|
||||
{
|
||||
value = *mapIter;
|
||||
++nChanged;
|
||||
}
|
||||
}
|
||||
|
||||
return nChanged;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
void Foam::sortedOrder
|
||||
(
|
||||
@ -299,7 +339,7 @@ void Foam::sortedOrder
|
||||
{
|
||||
const label len = input.size();
|
||||
|
||||
// list lengths must be identical
|
||||
// List lengths must be identical
|
||||
if (order.size() != len)
|
||||
{
|
||||
// Avoid copying elements, they are overwritten anyhow
|
||||
@ -562,10 +602,10 @@ void Foam::invertManyToMany
|
||||
}
|
||||
|
||||
// Size output
|
||||
output.setSize(len);
|
||||
output.resize(len);
|
||||
forAll(sizes, outi)
|
||||
{
|
||||
output[outi].setSize(sizes[outi]);
|
||||
output[outi].resize(sizes[outi]);
|
||||
}
|
||||
|
||||
// Fill output
|
||||
@ -1039,12 +1079,11 @@ Foam::List<T> Foam::ListOps::create
|
||||
{
|
||||
List_ACCESS(T, output, out);
|
||||
|
||||
InputIterator in = first;
|
||||
|
||||
for (label i = 0; i < len; ++i)
|
||||
while (first != last)
|
||||
{
|
||||
out[i] = op(*in);
|
||||
++in;
|
||||
*out = op(*first);
|
||||
++first;
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user