I have a set of tuples of 3 integers and I don't want any duplicates. That is, I don't want 2 entries with the same 3 values.
And here is my code.
struct Key{
unsigned a;
unsigned b;
unsigned c;
public:
Key(unsigned _a, unsigned _b, unsigned _c) :
a(_a),
b(_b),
c(_c) {}
bool operator<(const Key& rhs) const
{
if (a < rhs.a) {
return true;
}
if (b < rhs.b) {
return true;
}
if (c < rhs.c) {
return true;
}
return false;
};
};
std::set<Key> myset;
But I see duplicates in myset sometimes. I can't catch exactly what sequence causes the duplicate entry to be added. It doesn't always happen.
My question is this, is there something intrinsically wrong with my operator< function?
It's nearly right! But you are cascading too soon.
bool operator<(const Key& rhs) const
{
if (a < rhs.a)
return true;
if (a > rhs.a)
return false;
if (b < rhs.b)
return true;
if (b > rhs.b)
return false;
return (c < rhs.c);
};
Otherwise the following, for example, gives the wrong result:
Key lhs{3,1,0};
Key rhs{2,2,0};
assert(lhs < rhs); // passes, wrongly, because !(3 < 2) but then (1 < 2).
// you need an immediate `return false` when !(3 < 2)
It is safer to do something like this:
bool operator<(const Key& rhs) const
{
return std::tie(a, b, c) < std::tie(rhs.a, rhs.b, rhs.c);
}
C++'s standard library already knows what to do with that, so you don't have to.
Now, how can your bug lead to duplicate keys in a set?
Set's internal algorithm relies on the ordering being a strict weak ordering — when you break that precondition, you break the algorithms managing the internal tree, which is constructed and arranged using this ordering as its bible.
All hell breaks loose, basically. You could get a crash from this. In your case the symptoms were somewhat more benign (at least for now), with a deformed/mangled data tree resulting in the appearance of duplicated data.
It's folly to try to reason about the specific chain of events that led to a specific outcome, if you started off by breaking the preconditions and causing UB.
https://stackoverflow.com/a/979768/560648
Implementing comparison operators via 'tuple' and 'tie', a good idea?
Your operator<() is not consistent, as key1<key2 and key2<key1 could both be true (example: key1={1,3,0}, key2={3,1,0}). You should give the member variables a precedence in comparison:
if (a < rhs.a) {
return true;
} else if (a == rhs.a) {
if (b < rhs.b) {
return true;
} else if (b == rhs.b) {
if (c < rhs.c) {
return true;
}
}
}
return false;
You could indeed use standard class std::tuple as the key.
Nevertheless the operator can be defined the following way
bool operator <( const Key &rhs ) const
{
return
( a < rhs.a ) ||
( !( rhs.a < a ) && ( b < rhs.b ) ) ||
( !( rhs.a < a ) && !( rhs.b < b ) && ( c < rhs.c ) );
};
That this operator would work all you need is that for the type of objects a, b, and c there would be defined operator < Of course for arithmetic types it is already defined.
In fact it is the same as
#include <tuple>
//...
bool operator <( const Key &rhs ) const
{
return std::tie( a, b, c ) < std::tie( rhs.a, rhs.b, rhs.c );
}
Related
This question already has an answer here:
Efficient operator< with multiple members
(1 answer)
Closed 1 year ago.
I would like to use a struct with three integer members as a key. How can I overload the < operator. I understand that for two members it could be overloaded as:
bool operator < (const CacheKey& a, const CacheKey& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
The generic solution is:
if (a.x != b.x) return a.x < b.x;
if (a.y != b.y) return a.y < b.y;
// ...
return false;
Or:
return std::tie(a.x, a.y) < std::tie(b.x, b.y);
(In this case, you might want to create a member function that returns the tied members, to be able to do something like a.tie() < b.tie() for all needed operators.)
Or, in C++20, you would add following to your class to automatically get all comparison operators including <:
auto operator<=>(const CacheKey &) const = default;
The most direct approach would be:
class Foo {
friend bool operator<(const Foo&, const Foo&);
int a, b, c;
}
bool operator<(const Foo& lhs, const Foo& rhs) {
return (lhs.a < rhs.a) ||
(lhs.a == rhs.a && lhs.b < rhs.b) ||
(lhs.a == rhs.a && lhs.b == rhs.b && lhs.c < rhs.c);
}
I have a type struct Type_Specifier which I want to represent an immutable tree like structure that I can compare. I have the following code to illustrate what I want:
#include <vector>
struct Parameter_Specifier;
struct Type_Specifier
{
explicit Type_Specifier(void* tag = nullptr, std::vector<Parameter_Specifier> parameters = {})
: tag(tag), parameters(parameters) { }
Type_Specifier(const Type_Specifier&) = default;
Type_Specifier(Type_Specifier&&) = default;
Type_Specifier& operator=(const Type_Specifier&) = default;
Type_Specifier& operator=(Type_Specifier&&) = default;
~Type_Specifier() = default;
private:
// Points to an arbitray memory location (whose lifetime is not managed by this type)
void* tag;
std::vector<Parameter_Specifier> parameters;
public:
static bool operator ==(const Type_Specifier& left, const Type_Specifier& right)
{
if (left.tag != right.tag)
return false;
else if (left.parameters.size() != right.parameters.size())
return false;
else for (std::size_t i = 0; i < left.parameters.size(); i++)
{
if (!(left.parameters[i] == right.parameters[i]))
return false;
}
return true;
}
static bool operator <(const Type_Specifier& left, const Type_Specifier& right)
{
if (left.tag < right.tag)
return true;
else if (left.parameters.size() < right.parameters.size())
return true;
else if (left.parameters.size() > right.parameters.size())
return false;
else for (std::size_t i = 0; i < left.parameters.size(); i++)
{
if (left.parameters[i] < right.parameters[i])
return true;
else if (right.parameters[i] < left.parameters[i])
return false;
}
return false; // left == right
}
};
struct Parameter_Specifier
{
explicit Parameter_Specifier(Type_Specifier type = Type_Specifier(), std::vector<char> value = {})
: type(type), value(value) { }
Parameter_Specifier(const Parameter_Specifier&) = default;
Parameter_Specifier(Parameter_Specifier&&) = default;
Parameter_Specifier& operator=(const Parameter_Specifier&) = default;
Parameter_Specifier& operator=(Parameter_Specifier&&) = default;
~Parameter_Specifier() = default;
private:
Type_Specifier type;
// Arbitrary data (not a 'string' or sequence of 'characters')
std::vector<char> value;
public:
static bool operator ==(const Parameter_Specifier& left, const Parameter_Specifier& right)
{
if (!(left.type == right.type))
return false;
else if (left.value.size() != right.value.size())
return false;
else for (std::size_t i = 0; i < left.value.size(); i++)
{
if (left.value[i] != right.value[i])
return false;
}
return true; // left == right
}
static bool operator <(const Parameter_Specifier& left, const Parameter_Specifier& right)
{
if (left.type < right.type)
return true;
else if (left.value.size() < right.value.size())
return true;
else if (left.value.size() > right.value.size())
return false;
else for (std::size_t i = 0; i < left.value.size(); i++)
{
if (left.value[i] < right.value[i])
return true;
else if (left.value[i] > right.value[i])
return false;
}
return false; // left == right
}
};
However it of course will not compile, probably due to struct Parameter_Specifier being incomplete, and the operators '<', and '==' being undefined.
My question is this:
How Can I modify the above types to make it compile and work properly? (and efficiently?)
Notes:
The purpose of this type is to be used in a two-way map structure to be able to map between struct Type_Specifier and std::size_t (i.e. it needs to be both a key type and a value type)
I will define the other comparison operators '!=', '>', '>=' and '<=' based on my definitions of '==' and '>', I have omitted them here for brevity
I am pretty sure that a == b is equivalent to !(a < b) && !(b < a)
The order of comparison isn't really relevant, as look as it is a strict weak ordering relation
I would like the Type_Specifier type to follow the RAII idiom
The Paramater_Specifier type is only used for children of Type_Specifier and nowhere else
I only whish to use the C++ standard library
I was considering making Type_Specifier::parameters an std::vector<Parameter_Specifier*> but then I would have to manually manage allocation, in Type_Specifier, which would cause obvious problems as Parameter_Specifier is incomplete there.
You have problems with relational operators.
1) for operator==(), you have two alternative: (a) a method of the class/struct with one parameter (the left argument, *this, is implicit), that can't be static, or (b) an external function (friend, usually) with two parameter. You have mixed the two alternative and have made the method static. I strongly suggest alternative (b): external function. So (take in count that there is an operator==() for std::vector) operator==() for Type_Specifier could be
friend bool operator== (const Type_Specifier & left,
const Type_Specifier & right)
{
return ( left.tag == right.tag )
&& ( left.parameters == right.parameters );
}
For Parameter_Specifier could be
friend bool operator== (const Parameter_Specifier & left,
const Parameter_Specifier & right)
{
return ( left.type == right.type )
&& ( left.value == right.value );
}
2) same thing for operator<(). Moreover, if "left.tag < rigth.tag" is false, I think you should verify that isn't true that left.tag > right.tag. My suggestion (but with different behaviour; caution) for Type_Specifier is
friend bool operator< (const Type_Specifier & left,
const Type_Specifier & right)
{
return ( left.tag < right.tag )
|| ( ( left.tag == right.tag )
&& ( left.parameters < right.parameters ) );
}
For Parameter_Specifier, my suggestion (with different behaviour; caution) is
friend bool operator< (const Parameter_Specifier & left,
const Parameter_Specifier & right)
{
return ( left.type < right.type )
|| ( (left.type == right.type )
&& (left.value < right.value ) );
}
3) having operator==() and operator<() others relational operator are straightforward. For Type_Specifier
friend bool operator!= (const Type_Specifier & left,
const Type_Specifier & right)
{ return ! (left == right); }
friend bool operator> (const Type_Specifier & left,
const Type_Specifier & right)
{ return (right < left); }
friend bool operator<= (const Type_Specifier & left,
const Type_Specifier & right)
{ return ! (right < left); }
friend bool operator>= (const Type_Specifier & left,
const Type_Specifier & right)
{ return ! (left < right); }
For Parameter_Specifier... well... change Type_Specifier with Parameter_Specifier.
P.s.: sorry for my bad English.
I have a struct of following type which I am planning use as a key in a map.
Hence I write a comparator like below. I would like to know if there is a more elegant yet efficient way of doing this.
May be using std::pair or something.
struct T
{
int a, b, c, d;
bool operator< (const T& r) {
if (a < r.a)
return true
else if (a == r.a)
if (b < r.b)
return true;
else if (b == r.b)
if (c < r.c)
return true;
else if (c == r.c)
if (d < r.d)
return true;
return false;
}
}
Can you use C++11? If so:
struct T {
int a, b, c, d;
bool operator<(const T& rhs) const {
return tied() < rhs.tied();
}
private:
std::tuple<int, int, int, int> tied() const {
return std::make_tuple(a, b, c, d);
}
};
Alternatively, I would prefer returning at each possible opportunity to avoid the error-prone awkward nesting approach:
bool operator<(const T& rhs) const {
if (a != rhs.a) return a < rhs.a;
if (b != rhs.b) return b < rhs.b;
if (c != rhs.c) return c < rhs.c;
return d < rhs.d;
}
You can use...
bool operator<(const T& r)
{
return a < r.a ||
a == r.a && (b < r.b ||
b == r.b && (c < r.c ||
c == r.c && d < r.d));
}
Or...
return a != r.a ? a < r.a :
b != r.b ? b < r.b :
c != r.c ? c < r.c :
d < r.d;
You've said you're not using C++11, and Barry has a good illustration of a tuple approach, but for future reference and other interested parties, with a little reusable support code...
bool less_by_pairs()
{
return false;
}
template <typename T, typename U, typename ...Args>
bool less_by_pairs(const T& v1, const U& v2, Args... args)
{
return v1 != v2 ? v1 < v2 : less_by_pairs(args...);
}
...you can implement such operators more easily...
bool operator<(const T& r)
{
return less_by_pairs(a, r.a, b, r.b, c, r.c, d, r.d);
}
You could do something similar providing a list of members to compare, but the notation for that's actually a little verbose anyway.
You could use another field to store a key. This key value could be generated through a formula, that takes as input (a,b,c,d)
Like so:
void hash()
{
key = (a ^ b ^ c ^ d);
}
As result, you would need to only compare this key to know if the contents are the same.
I get an assertion failure about my Vec2's Operator <, I have no idea what is wrong though.
bool Vec2::operator<( const Vec2& v ) const
{
if(x < v.x)
return true;
else
return y < v.y;
}
Invalid Operator < for std set insert
template<class _Pr, class _Ty1, class _Ty2> inline
bool __CLRCALL_OR_CDECL _Debug_lt_pred(_Pr _Pred, const _Ty1& _Left, const _Ty2& _Right,
const wchar_t *_Where, unsigned int _Line)
{ // test if _Pred(_Left, _Right) and _Pred is strict weak ordering
if (!_Pred(_Left, _Right))
return (false);
else if (_Pred(_Right, _Left))
_DEBUG_ERROR2("invalid operator<", _Where, _Line);
return (true);
}
Thanks
The problem is that this operator does not satisfy the weak ordering. For example consider two points
( 2, 1 ) and ( 1, 2 )
( 2, 1 ) is less ( 1, 2 ) because the second value 1 is less than 2.
At the same time ( 1, 2 ) is also less than ( 2, 1 ) because the first value 1 is less than the first value 2.
Look how thsi operator is defined for standard class std::pair and use the same operator.
The corrected way to satisfy the ordering is :
bool Vec2::operator<( const Vec2& v ) const
{
if(x < v.x)
return true;
if(x > v.x)
return false;
else
return y < v.y;
}
Or (code golf mode) :
bool Vec2::operator<( const Vec2& v ) const
{
return (x != v.x)? (x < v.x)
: (y < v.y) ;
}
Generally for a comparison like this, you want to compare the first pair of items, then if and only if those are equal, compare the second pair (and so on).
if (x < v.x)
return true;
if (x > v.x)
return false;
return y < v.y;
operator < should satisfy the Strict weak ordering.
A short way to do the job:
bool Vec2::operator< (const Vec2& v) const
{
return std::tie(x, y) < std::tie(v.x, v.y);
}
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.