Commit 10b69fa2 authored by Mark Olesen's avatar Mark Olesen
Browse files

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)) ...
parent c0766ce8
......@@ -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,16 +145,44 @@ void inplaceReorder
);
// Variants to work with iterators and sparse tables.
// Need to have iterators and insert()
//- 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. Do not map negative values.
//- Map values. Ignore negative values.
// \return number of values changed
template<class Container>
void inplaceMapValue(const labelUList& oldToNew, Container& input);
label inplaceMapValue(const labelUList& oldToNew, Container& input);
//- Recreate with mapped keys. Do not map elements with negative key.
//- 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>
void inplaceMapKey(const labelUList& oldToNew, Container& input);
label inplaceMapValue(const Map<label>& mapper, Container& input);
//- Generate the (stable) sort order for the list
......@@ -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);
......@@ -233,48 +233,88 @@ void Foam::inplaceReorder
template<class Container>
void Foam::inplaceMapValue
void Foam::inplaceMapKey
(
const labelUList& oldToNew,
Container& input
)
{
Container output(input.capacity());
for (auto iter = input.begin(); iter != input.end(); ++iter)
{
const label oldIdx = iter.object();
const label oldIdx = iter.key();
if (oldIdx >= 0)
{
// Could enforce (oldIdx < oldToNew.size())
// ... or just rely on FULLDEBUG from UList
iter.object() = oldToNew[oldIdx];
output.insert(oldToNew[oldIdx], iter.object());
}
}
input.transfer(output);
}
template<class Container>
void Foam::inplaceMapKey
Foam::label Foam::inplaceMapValue
(
const labelUList& oldToNew,
Container& input
)
{
Container output(input.capacity());
label nChanged = 0;
for (auto iter = input.begin(); iter != input.end(); ++iter)
{
const label oldIdx = iter.key();
const label oldIdx = iter.object();
if (oldIdx >= 0)
{
// Could enforce (oldIdx < oldToNew.size())
// ... or just rely on FULLDEBUG from UList
output.insert(oldToNew[oldIdx], iter.object());
const label newIdx = oldToNew[oldIdx];
if (oldIdx != newIdx)
{
iter.object() = newIdx;
++nChanged;
}
}
}
input.transfer(output);
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;
}
......@@ -299,7 +339,7 @@ void Foam::sortedOrder