TBB: initialize concurrent_hash_map - c++

I am trying to initialize a 2D concurrent_hash_map, a container available in the Intel TBB library. Compilation passes and there is no error at runtime. However, not all initialized values are available in the container leading to incorrect behavior.
The hash map is defined as
template<typename K>
struct MyHashCompare {
static size_t hash(const K& key) { return boost::hash_value(key); }
static bool equal(const K& key1, const K& key2) { return (key1 == key2); }
};
typedef concurrent_hash_map<int, int, MyHashCompare<int> > ColMap;
typedef concurrent_hash_map<int, ColMap, MyHashCompare<int> > RowMap;
The function object is defined as follows. Could the reason for the incorrect behavior originate here?
class ColumnInit {
RowMap *const pMyEdgeMap;
public:
void operator()(const blocked_range<size_t>& r) const {
RowMap* pEdgeMap = pMyEdgeMap;
RowMap::accessor accessX;
ColMap::accessor accessY;
for(size_t n1 = r.begin(); n1 != r.end(); n1++)
{
pEdgeMap->insert(accessX, n1);
for(int n2 = 1; n2 <= 64; n2++)
{
int diff = abs((int)n1 - n2);
if ((diff == 8) || (diff == 1))
{
assert((accessX->second).insert(accessY, n2));
accessY->second = -1;
}
else
{
assert((accessX->second).insert(accessY, n2));
accessY->second = 0;
}
}
}
}
ColumnInit(RowMap* pEdgeMap): pMyEdgeMap(pEdgeMap)
{
}
};
The function object is invoked from a call to parallel_for as follows:
parallel_for(blocked_range<size_t>(1,64,16), ColumnInit((RowMap*)&mEdges), simple_partitioner());
Any suggestions or feedback would be great.
Thanks.

If you intend to create a 64x64 table, use blocked_range(1,65,16) as the first argument to parallel_for. The reason is that a blocked_range represents a half-open interval, which includes the lower bound but excludes the upper bound.

Related

How to use a custom class as key with std::map if there is no logical way to have a comparison operator defined?

I'm trying to use std::map with a custom class and in the course of the process the program has to call std::map::find in order to resolve a key to a pair. The custom class doesn't seem to fit well in terms of comparisons.
This is probably better explained in code; I have a class that I want to use as a key:
class index_t
{
int vertex_index;
int normal_index;
int texture_index;
}
std::map<index_t, int> reindexer;
I would like to use
reindexer.find(index_to_find);
In order to find a key with exactly same parameters (exactly same vertex/normal/texture indices) exists in the map already.
So technically I want the std::map::find function to behave like this:
bool find(key_to_find) //this is what I'm expecting from a find function of std::map
{
if(existing_key.vertex == key_to_find.vertex && existing_key.texture == key_to_find.texture && existing_key.normal == key_to_find.normal)
return true;
else return false;
}
However, I'm not sure how to overload the comparison operator appropriately in this situation for it to behave like that (since I can think of no logical less than operator that would suit this class). This is the current operator I'm using:
bool operator<(const index_t& rhv)
{
if(vertex_index < rhv && normal_index < rhv && texture_index < rhv)
return true;
else return false;
}
It doesn't work, since the find relies on the function returning "false" reflexively when comparison orders reversed.
How can I get around this?
This is some more specific, compilable code that reproduces the problem:
class index_t
{
public:
int vertex;
int normal;
int texture;
bool operator< (const index_t& rhv) const
{
if (vertex < rhv.vertex && normal < rhv.normal && texture < rhv.texture)
return true;
else return false;
}
};
map<index_t, int> indexMap;
int main()
{
index_t i;
i.vertex = 0;
i.normal = 0;
i.texture = 0;
index_t i2;
i2.vertex = 1;
i2.normal = 0;
i2.texture = 3;
index_t i4;
i4.vertex = 1;
i4.normal = 0;
i4.texture = 3;
index_t i5;
i5.vertex = 6;
i5.normal = 0;
i5.texture = 3;
index_t i8;
i8.vertex = 7;
i8.normal = 5;
i8.texture = 4;
indexMap.insert(pair<index_t, int>(i, 0));
indexMap.insert(pair<index_t, int > (i2, 1));
if (indexMap.find(i5) != indexMap.end())
cout << "found" << endl;
else
cout << "not found" << endl;
system("pause");
return 0;
}
This results in "found" even though i5 is not a part of the map
I also tried this:
class index_t
{
public:
int vertex;
int normal;
int texture;
};
class index_comparator
{
public:
bool operator()(const index_t& lhv, const index_t& rhv) const
{
if (lhv.vertex == rhv.vertex && lhv.normal == rhv.normal && lhv.texture == rhv.texture)
return true;
else return false;
}
};
map<index_t, int, index_comparator> indexMap;
int main()
{
index_t i;
i.vertex = 0;
i.normal = 0;
i.texture = 0;
index_t i2;
i2.vertex = 1;
i2.normal = 0;
i2.texture = 3;
index_t i4;
i4.vertex = 1;
i4.normal = 0;
i4.texture = 3;
index_t i5;
i5.vertex = 6;
i5.normal = 0;
i5.texture = 3;
index_t i8;
i8.vertex = 7;
i8.normal = 5;
i8.texture = 4;
indexMap.insert(pair<index_t, int>(i, 0));
indexMap.insert(pair<index_t, int > (i2, 1));
if (indexMap.find(i5) != indexMap.end())
cout << "found" << endl;
else
cout << "not found" << endl;
system("pause");
return 0;
}
This also results in "found"
The expected results are that when I call std::map::find on a custom class it compares it other keys in the map and only returns true if an exactly same class (containing the same parameters) exists. Otherwise it should return false.
You have to define a strict order to use class index_t as key in a std::map.
It doesn't need to make sense to you – it just has to provide a unique result of less-than for any pairs of index_t instances (and to grant a < b && b < c => a < c).
The (in question) exposed attempt doesn't seem to fulfil this but the following example would:
bool operator<(const index_t &index1, const index_t &index2)
{
if (index1.vertex != index2.vertex) return index1.vertex < index2.vertex;
if (index1.normal != index2.normal) return index1.normal < index2.normal;
return index1.texture < index2.texture;
}
The simplest way to implement the operator is with tuples, it does all the hard work for you:
bool operator<(const index_t& rhv)
{
return std::tie(vertex_index, normal_index, texture_index) < std::tie(rhv.vertex_index, rhv.normal_index, rhv.texture_index);
}
This is equivalent to the required logic:
bool operator<(const index_t& rhv)
{
if (vertex_index != rhv.vertex_index)
{
return vertex_index < rhv.vertex_index;
}
if (normal_index!= rhv.normal_index)
{
return normal_index< rhv.normal_index;
}
return texture_index< rhv.texture_index;
}
In c++20 this gets even easier with the spaceship operator which does everything for you:
auto operator<=>(const index_t&) const = default;
Your ordering doesn't fulfill the requirements, it has to be what is called a "strict weak ordering relation". It's easiest to not implement that yourself, but instead use existing functionality. Examle:
#include <tuple>
bool operator()(const index_t& lhv, const index_t& rhv) const
{
return std::tie(lhv.vertex, lhv.normal, lhv.texture) <
std::tie(rhv.vertex, rhv.normal, rhv.texture);
}
Your comparison function doesn't have to be logical, it just has to impose a strict weak ordering. Here's a version that works.
bool operator<(const index_t& rhv) const
{
if (vertex < rhv.vertex)
return true;
if (vertex > rhv.vertex)
return false;
if (normal < rhv.normal)
return true;
if (normal > rhv.normal)
return false;
if (texture < rhv.texture)
return true;
if (texture > rhv.texture)
return false;
return false;
}
Since this is not a reasonable operator< for your class it would be better to rename it, to avoid confusion.
struct IndexLT
{
bool operator()(const index_t& lhs, const index_t& rhs)
{
// logic as before
}
};
Then use this newly declared functor like this
std::map<index_t, whatever, IndexLT> my_map;
Yet another alternative would be to use a std::unordered_map since ordering doesn't seem to be significant.

std::map<Custom, int>::find() does not find anything

For some reason std::map does not find my objects.
Here is my simplified object :
class LangueISO3 {
public:
enum {
SIZE_ISO3 = 3
};
static constexpr char DEF_LANG[SIZE_ISO3] = {'-','-','-'};
constexpr LangueISO3():code() {
for(size_t i(0); i < SIZE_ISO3; i++){
code[i] = DEF_LANG[i];
}
};
LangueISO3(const std::string& s) {strncpy(code, s.c_str(), 3);};
bool operator==(const LangueISO3& lg) const { return strncmp(code, lg.code, 3) == 0;};
bool operator<(const LangueISO3& lg)const { return code < lg.code;};
private:
char code[SIZE_ISO3];
};
My test is :
{
CPPUNIT_ASSERT_EQUAL(LangueISO3("eng"), LangueISO3("eng"));
std::map<LangueISO3, int> lmap;
lmap.emplace(LangueISO3("fra"), 0);
lmap.emplace(LangueISO3("deu"), 1);
lmap.emplace(LangueISO3("eng"), 2);
auto it = lmap.find(LangueISO3("deu"));
CPPUNIT_ASSERT_EQUAL(1, it->second);
}
The first test has no problem, however the second fails. lmap.find() always return lmap.end()
What did I do wrong ?
You can't compare character arrays with the < operator. When you write code < lg.code, the compiler will compare the address of the code array, and not the contents of the arrays.
Change the definition for operator< to use strncmp:
bool operator<(const LangueISO3& lg)const {
return strncmp(code, lg.code, SIZE_ISO3) < 0;
}
Also, the comparison for operator== should use the SIZE_ISO3 constant instead of hardcoding the size at 3.

Generic C++ multidimensional iterators

In my current project I am dealing with a multidimensional datastructure.
The underlying file is stored sequentially (i.e. one huge array, no vector of vectors).
The algorithms that use these datastructures need to know the size of the individual dimensions.
I am wondering if a multidimensional iterator class has been definied somewhere in a generic way and if there are any standards or preferred ways on how to tackle this.
At the moment I am just using a linear iterator with some additional methods that return the size of each dimension and how many dimensions are there in the first part. The reason I don't like it is because I can't use std:: distance in a reasonable way for example (i.e. only returns distance of the whole structure, but not for each dimension separately).
For the most part I will access the datastructure in a linear fashion (first dimension start to finish -> next dimension+...and so on), but it would be good to know when one dimension "ends". I don't know how to do this with just operator*(), operator+() and operator==() in such an approach.
A vector of vectors approach is disfavored, because I don't want to split up the file. Also the algorithms must operate on structure with different dimensionality and are therefore hard to generalize (or maybe there is a way?).
Boost multi_array has the same problems (multiple "levels" of iterators).
I hope this is not too vague or abstract. Any hint in the right direction would be appreciated.
I was looking for a solution myself again and revisited boost:: multi_array. As it turns out it is possible to generate sub views on the data with them, but at the same time also take a direct iterator at the top level and implicitely "flatten" the data structure. The implemented versions of multi_array however do not suit my needs, therefore I probably will implement one myself (that handles the caching of the files in the background) that is compatible with the other multi_arrays.
I will update it again once the implementation is done.
I have just decided to open a public repository on Github : MultiDim Grid which might help for your needs. This is an ongoing project so
I would be glad if you can try it and tell me what you miss / need.
I have started working on this with this topic on codereview.
Put it simply :
MultiDim Grid proposes a flat uni-dimensional array which offer a
generic fast access between multi-dimension coordinates and flatten
index.
You get a container behaviour so you have access to iterators.
That's not that difficult to implement. Just state precisely what functionality your project requires. Here's a dumb sample.
#include <iostream>
#include <array>
#include <vector>
#include <cassert>
template<typename T, int dim>
class DimVector : public std::vector<T> {
public:
DimVector() {
clear();
}
void clear() {
for (auto& i : _sizes)
i = 0;
std::vector<T>::clear();
}
template<class ... Types>
void resize(Types ... args) {
std::array<int, dim> new_sizes = { args ... };
resize(new_sizes);
}
void resize(std::array<int, dim> new_sizes) {
clear();
for (int i = 0; i < dim; ++i)
if (new_sizes[i] == 0)
return;
_sizes = new_sizes;
int realsize = _sizes[0];
for (int i = 1; i < dim; ++i)
realsize *= _sizes[i];
std::vector<T>::resize(static_cast<size_t>(realsize));
}
decltype(auto) operator()(std::array<int, dim> pos) {
// check indexes and compute original index
size_t index;
for (int i = 0; i < dim; ++i) {
assert(0 <= pos[i] && pos[i] < _sizes[i]);
index = (i == 0) ? pos[i] : (index * _sizes[i] + pos[i]);
}
return std::vector<T>::at(index);
}
template<class ... Types>
decltype(auto) at(Types ... args) {
std::array<int, dim> pos = { args ... };
return (*this)(pos);
}
int size(int d) const {
return _sizes[d];
}
class Iterator {
public:
T& operator*() const;
T* operator->() const;
bool operator!=(const Iterator& other) const {
if (&_vec != &other._vec)
return true;
for (int i = 0; i < dim; ++i)
if (_pos[i] != other._pos[i])
return true;
return false;
}
int get_dim(int d) const {
assert(0 <= d && d < dim);
return _pos[d];
}
void add_dim(int d, int value = 1) {
assert(0 <= d && d < dim);
_pos[d] += value;
assert(0 <= _pos[i] && _pos[i] < _vec._sizes[i]);
}
private:
DimVector &_vec;
std::array<int, dim> _pos;
Iterator(DimVector& vec, std::array<int, dim> pos) : _vec(vec), _pos(pos) { }
};
Iterator getIterator(int pos[dim]) {
return Iterator(*this, pos);
}
private:
std::array<int, dim> _sizes;
};
template<typename T, int dim>
inline T& DimVector<T, dim>::Iterator::operator*() const {
return _vec(_pos);
}
template<typename T, int dim>
inline T* DimVector<T, dim>::Iterator::operator->() const {
return &_vec(_pos);
}
using namespace std;
int main() {
DimVector<int, 4> v;
v.resize(1, 2, 3, 4);
v.at(0, 0, 0, 1) = 1;
v.at(0, 1, 0, 0) = 1;
for (int w = 0; w < v.size(0); ++w) {
for (int z = 0; z < v.size(1); ++z) {
for (int y = 0; y < v.size(2); ++y) {
for (int x = 0; x < v.size(3); ++x) {
cout << v.at(w, z, y, x) << ' ';
}
cout << endl;
}
cout << "----------------------------------" << endl;
}
cout << "==================================" << endl;
}
return 0;
}
TODO list:
optimize: use T const& when possible
optimizate iterator: precompute realindex and then just change that realindex
implement const accessors
implement ConstIterator
implement operator>> and operator<< to serialize DimVector to/from file

Find the mode (most common element) of an array in C++

I had this on an interview question. I'd like to see how StackOverflow would do it.
What would Bjarne Stroustrop think of my way? It's a little wordy, but I don't know how to make it better, unfortunately. I know you guys are gonna laugh at my stupidity.
template <class T>
T mode(T* arr, size_t n)
// If there's a tie, return an arbitrary element tied for 1st
// If the array size is 0, throw an error
{
if (n == 0)
{
throw("Mode of array of size 0 is undefined, bro.");
}
else if (n == 1)
{
return arr[0];
}
else
{
std::pair<T, int> highest(arr[0], 1);
std::map<T, int> S;
S.insert(highest);
for (T* thisPtr(arr + 1), lastPtr(arr+n); thisPtr != lastPtr; ++thisPtr)
{
if (S.count(*thisPtr) == 0)
{
S.insert(std::pair<T, int> (*thisPtr, 1);
}
else
{
++S[*thisPtr];
if (S[*thisPtr] > highest.second)
{
highest = std::pair<T, int> (*thisPtr, S[*thisPtr]);
}
}
}
}
}
You could do this, provided that T implements std::hash:
std::unordered_multiset<T> elems;
std::for_each(arr, arr + size, [&elems](T const & elem) { elems.insert(elem); }
//Now you have elems.count() for each entry
auto max_count = /*Guaranteed minimum value*/
T mode{};
for (auto const & set_elem : elems) {
if (max(elems.count(set_elem), max_count) == max_count)) {
mode = set_elem;
}
}
I think I'd use an std::map to do the counting, then find the item with the largest count:
template <class T>
T mode(T* arr, size_t n) {
std::map<T, size_t> counts;
for (size_t i=0; i<n; i++)
++counts[arr[i]];
return max_element(counts.begin(), counts.end(),
[](std::pair<T, size_t> const &a, std::pair<T, size_t> const &b) {
return a.second < b.second;
})->first;
}
If you expect a really large number of unique items, you might want to use an std::unordered_map instead of a std::map [should reduce expected complexity from O(n log n) to O(N)].
I found the following issues with your code.
Redundant check n == 1
You can remove the block
else if (n == 1)
{
return arr[0];
}
without affecting the outcome.
Declaration of the variables in the for loop:
T* thisPtr(arr + 1), lastPtr(arr+n);`
is equivalent to
T* thisPtr(arr + 10); T lastPtr(arr+n);
That's not what your intention is. The compiler will report an error too. So, move their declaration outside the for loop. Change
for (T* thisPtr(arr + 1), lastPtr(arr+n); thisPtr != lastPtr; ++thisPtr)
to
T* thisPtr(arr + 1);
T* lastPtr(arr+n);
for ( ; thisPtr != lastPtr; ++thisPtr)
Simplify the contents of the for loop
The lines
if (S.count(*thisPtr) == 0)
{
S.insert(std::pair<T, int> (*thisPtr, 1));
}
can be replaced by
++S[*thisPtr];
which is exactly what you are doing in the following else block.
You can change the contents of the entire for loop to:
++S[*thisPtr];
if (S[*thisPtr] > highest.second)
{
highest = std::pair<T, int> (*thisPtr, S[*thisPtr]);
}
You need to return the mode
Add
return highest.first;
before the closing of the else block.

C++ STL Range Container

I'm looking for a container that maps from a double to object pointers. However, each key is simply a range of doubles that would correspond to that object.
For example, there could be a key/value pair that's <(0.0 3.0), ptr>, or <(3.5 10.0), ptr2>
container[1.0] should return ptr, container[3.0] should also return ptr, and container[-1.0] should be undefined.
Is there any object with similar behaviour by default or will I have to implement it myself?
Edit
Here's the actual code that I've written, it might be easier to debug/offer advice on it.
// Behavior: A range is defined mathematically as (min, max]
class dblRange
{
public:
double min;
double max;
dblRange(double min, double max)
{
this->min = min;
this->max = max;
};
dblRange(double val)
{
this->min = val;
this->max = val;
};
int compare(const dblRange rhs)
{
// 1 if this > rhs
// 0 if this == rhs
//-1 if this < rhs
if (rhs.min == rhs.max && min == max)
{
/*if (min > rhs.min)
return 1;
else if (min == rhs.min)
return 0;
else
return -1;*/
throw "You should not be comparing values like this. :(\n";
}
else if (rhs.max == rhs.min)
{
if (min > rhs.min)
return 1;
else if (min <= rhs.min && max > rhs.min)
return 0;
else // (max <= rhs.min)
return -1;
}
else if (min == max)
{
if (min >= rhs.max)
return 1;
else if (min < rhs.max && min >= rhs.min)
return 0;
else // if (min < rhs.min
return -1;
}
// Check if the two ranges are equal:
if (rhs.min == min && rhs.max == max)
{
return 0;
}
else if (rhs.min < min && rhs.max <= min)
{
// This is what happens if rhs is fully lower than this one.
return 1;
}
else if (rhs.min > min && rhs.min >= max)
{
return -1;
}
else
{
// This means there's an undefined case. Ranges are overlapping,
// so comparisons don't work quite nicely.
throw "Ranges are overlapping weirdly. :(\n";
}
};
int compare(const dblRange rhs) const
{
// 1 if this > rhs
// 0 if this == rhs
//-1 if this < rhs
if (rhs.min == rhs.max && min == max)
{
/*if (min > rhs.min)
return 1;
else if (min == rhs.min)
return 0;
else
return -1;*/
throw "You should not be comparing values like this. :(\n";
}
else if (rhs.max == rhs.min)
{
if (min > rhs.min)
return 1;
else if (min <= rhs.min && max > rhs.min)
return 0;
else // (max <= rhs.min)
return -1;
}
else if (min == max)
{
if (min >= rhs.max)
return 1;
else if (min < rhs.max && min >= rhs.min)
return 0;
else // if (min < rhs.min
return -1;
}
// Check if the two ranges are equal:
if (rhs.min == min && rhs.max == max)
{
return 0;
}
else if (rhs.min < min && rhs.max <= min)
{
// This is what happens if rhs is fully lower than this one.
return 1;
}
else if (rhs.min > min && rhs.min >= max)
{
return -1;
}
else
{
// This means there's an undefined case. Ranges are overlapping,
// so comparisons don't work quite nicely.
throw "Ranges are overlapping weirdly. :(\n";
}
};
bool operator== (const dblRange rhs ) {return (*this).compare(rhs)==0;};
bool operator== (const dblRange rhs ) const {return (*this).compare(rhs)==0;};
bool operator!= (const dblRange rhs ) {return (*this).compare(rhs)!=0;};
bool operator!= (const dblRange rhs ) const {return (*this).compare(rhs)!=0;};
bool operator< (const dblRange rhs ) {return (*this).compare(rhs)<0;};
bool operator< (const dblRange rhs ) const {return (*this).compare(rhs)<0;};
bool operator> (const dblRange rhs ) {return (*this).compare(rhs)>0;};
bool operator> (const dblRange rhs ) const {return (*this).compare(rhs)>0;};
bool operator<= (const dblRange rhs ) {return (*this).compare(rhs)<=0;};
bool operator<= (const dblRange rhs ) const {return (*this).compare(rhs)<=0;};
bool operator>= (const dblRange rhs ) {return (*this).compare(rhs)>=0;};
bool operator>= (const dblRange rhs ) const {return (*this).compare(rhs)>=0;};
};
Right now I'm having trouble having the map accept a double as a key, even though the comparison operators are defined.
Here's some driving code that I'm using to test if it would work:
std::map<dblRange, int> map;
map[dblRange(0,1)] = 1;
map[dblRange(1,4)] = 2;
map[dblRange(4,5)] = 3;
map[3.0] = 4;
I mostly agree with Earwicker in that you can define a range. Now, I am in favor of implementing operators with the real meaning (do what basic types do: two ranges compare equal if both ranges ARE equal). Then you can use the third map parameter to pass it a comparison functor (or function) that solves your particular problem with this map.
// Generic range, can be parametrized for any type (double, float, int...)
template< typename T >
class range
{
public:
typedef T value_type;
range( T const & center ) : min_( center ), max_( center ) {}
range( T const & min, T const & max )
: min_( min ), max_( max ) {}
T min() const { return min_; }
T max() const { return max_; }
private:
T min_;
T max_;
};
// Detection of outside of range to the left (smaller values):
//
// a range lhs is left (smaller) of another range if both lhs.min() and lhs.max()
// are smaller than rhs.min().
template <typename T>
struct left_of_range : public std::binary_function< range<T>, range<T>, bool >
{
bool operator()( range<T> const & lhs, range<T> const & rhs ) const
{
return lhs.min() < rhs.min()
&& lhs.max() <= rhs.min();
}
};
int main()
{
typedef std::map< range<double>, std::string, left_of_range<double> > map_type;
map_type integer; // integer part of a decimal number:
integer[ range<double>( 0.0, 1.0 ) ] = "zero";
integer[ range<double>( 1.0, 2.0 ) ] = "one";
integer[ range<double>( 2.0, 3.0 ) ] = "two";
// ...
std::cout << integer[ range<double>( 0.5 ) ] << std::endl; // zero
std::cout << integer[ range<double>( 1.0 ) ] << std::endl; // one
std::cout << integer[ 1.5 ] << std::endl; // one, again, implicit conversion kicks in
}
You must be careful with equality and comparisons among double values. Different ways of getting to the same value (in the real world) can yield slightly different floating point results.
Create a class DoubleRange to store the double range, and implement the comparison operators on it. That way, std::map will do the rest for you, with the DoubleRange class as the key.
It is better to use Interval tree data structure. Boost has an implementation in Interval Container Library
One approach would be to calculate the "break points" before hand:
typedef vector< tuple<double, double, foo*> > collisionlist_t;
const collisionlist_t vec;
vec.push_back(make_tuple(0.0, 3.0, ptr));
vec.push_back(make_tuple(3.5, 10.0, ptr2));
// sort
std::map<double, foo*> range_lower_bounds;
for(collisionlist_t::const_iterator curr(vec.begin()), end(vec.end()); curr!=end; ++curr)
{
/* if ranges are potentially overlapping, put some code here to handle it */
range_lower_bounds[curr->get<0>()] = curr->get<2>();
range_lower_bounds[curr->get<1>()] = NULL;
}
double x = // ...
std::map<double, foo*>::const_iterator citer = range_lower_bounds.lower_bound(x);
Another suggestion: Use a mathematical transform to map the index from REAL to INT which can be directly compared.
If these ranges are multiple and dense there's also a structure known as an "interval tree" which may help.
Are the intervals open or closed or half open?
I will assumed closed. Note that the intervals cannot overlap by the definition of a map. You will also need splitting rules for when one inserts an over lapping interval. the rules need to decide where the split takes place and must take into account floating point epsilon.
this implementation uses map::lower_bound and does NOT use a class as the domain of the map
map::lower_bound returns an iterator to the first element in a map with a key value that is equal to or greater than that of a specified key. (ie the least key greater than or equal to K. An unfortunate choice of STL method names as it is the least upper bound of K.)
template <class codomain>
class RangeMap : private std::map<double,std::pair<double,codomain>{
public:
typedef double domain;
typedef std::map<double,std::pair<double,codomain>:: super;
typename super::value_type value_type;
protected:
static domain& lower(const value_type& v){
return v.first;
}
static domain& upper(const value_type& v){
return v.second.first;
}
static codomain& v(const value_type& v){
return v.second.second;
}
public:
static const domain& lower(const value_type& v){
return v.first;
}
static const domain& upper(const value_type& v){
return v.second.first;
}
static const codomain& v(const value_type& v){
return v.second.second;
}
static bool is_point(const value_type& vf) {
return lower(v) == upper(v);
}
static bool is_in(const domain& d,const value_type& vf) {
return (lower(v) <= d) && (d <= upper(v));
}
const_iterator greatest_lower_bound(const domain& d)const {
const_iterator j = super::lower_bound(d);
if(j!=end() && j->first==d) return j;//d is the lh side of the closed interval
//remember j->first >= d because it was lower but its the first
if(j==begin()) return end();//d < all intervals
--j; //back up
return j;
}
const_iterator find(domain& d) {
const_iterator j = greatest_lower_bound(d);
if (is_in(j,d)) return j;
return end();
}
iterator greatest_lower_bound(const domain& d) {
iterator j = super::lower_bound(d);
if(j!=end() && j->first==d) return j;//d is the lh side of the closed interval
//remember j->first >= d because it was lower but its the first
if(j==begin()) return end();//d < all intervals
--j; //back up
return j;
}
const_iterator find(domain& d) const{
iterator j = greatest_lower_bound(d);
if (is_in(j,d)) return j;
return end();
} //so much for find(d)
iterator find(domain& d){
iterator j = greatest_lower_bound(d);
if (is_in(j,d)) return j;
return end();
} //so much for find(d)
struct overlap: public std::exception{
};
bool erase(const double lhep,const double rhep);
//you have a lot of work regarding splitting intervals erasing when overlapped
//but that can all be done with erase, and insert below.
//erase may need to split too
std::pair<iterator,bool>
split_and_or_erase_intervals(const double lhep,
const double rhep,
const codomain& cd);
//the insert method - note the addition of the overwrtite
std::pair<iterator,bool>
insert(const double lhep,const double rhep,const codomain& cd,bool overwrite_ok){
if( find(lhep)!=end() || find(rhep)!=end() ) {
if(overwrite_ok){
return split_and_or_erase_intervals(const double lhep,
const double rhep,
const codomain& cd);
}
throw overlap();
}
return insert(value_type(lhep,pair<double,codomain>(rhep,cd)));
}
};
If your intervals must be non-overlapping, you must add some extra code to verify this property at insertion-time. Specifically, the property you wish to assert is that your new interval lies entirely within a range that was previously empty. An easy way to do this is to allow two types of ranges: "occupied" and "empty". You should begin by creating a single "empty" entry which covers the entire usable range. Insertion of a new "occupied" range requires:
(1) lookup some value within the new range.
(2) ensure that the returned range is empty, and wholly encompasses your new range. (This was the required assertion, above)
(3) modify the returned empty range so its end lies at the start of your new range.
(4) insert a new empty range that begins at the end of your new range, and ends at the old end of the returned range.
(5) insert your new range, confident that it is surrounded by empty-ranges.
(6) There may be additional corner-cases when inserting a new occupied range which has no empty space separating it from other occupied ranges.