Got lured like a fool into this, too. What a heartbreaker indeed this was... Here's my fastest, smallest, and most compliant solution so far:
void assign(K const& keyBegin, K const& keyEnd, V const& val) {
if (!(keyBegin < keyEnd))
return;
if (m_map.begin() == m_map.end())
m_map.insert_or_assign(m_map.begin(), keyEnd, m_valBegin); // avoiding lowest();
const auto& l = m_map.lower_bound(keyBegin); // O(log(N))
auto R = l;
while (R != m_map.end() && !(keyEnd < R->first)) ++R; // O(D) is faster than O(log(N) for D << N.
const auto& r = std::prev(R); // r is never at the end.
m_map.erase( // (B,E) is the erase opened segment
/* auto& E = */ std::next( // E
l == m_map.begin() || !(std::prev(l)->second == val) ?
m_map.insert_or_assign(l, keyBegin, val) :
/* auto& L = */ std::prev(l)),
/* auto& B = */ r->second == val ? R : // B
!(r->first < keyEnd) && !(keyEnd < r->first) ? r :
m_map.insert_or_assign(R, keyEnd, r->second));
}
I failed the test on Monday night with the following solution which did not pass the compliance test, with the highly misleading message "Type requirements are met: You must adhere to the specification of the key and value type given above."
void assign(K const& keyBegin, K const& keyEnd, V const& val) {
if (!(keyBegin < keyEnd))
return; // trivial rejection of degenerated segments.
// Adding the Z infimum element to the map simplifies the algorithm in that any key value would be guaranteed to have a lower bound.
const auto& z = std::numeric_limits<K>::lowest();
if (m_map.begin() == m_map.end())
m_map.insert(std::make_pair(z, m_valBegin)).first;
const auto& l = m_map.lower_bound(keyBegin); // O(log(N))
#if 1
auto R = l; // lower bound is guaranteed by the Z imfimum.
while (R != m_map.end() && !(keyEnd < R->first)) ++R; // O(D) is faster than O(log(N) for D << N.
#else
const auto& R = m_map.upper_bound(keyEnd); // O(log(N)), is slower than O(D) for short [b,e) distances
#endif
const auto& r = std::prev(R); // r is never at the end.
const auto& E = r->second == val ? R :
!(r->first < keyEnd) && !(keyEnd < r->first) ? r :
m_map.insert_or_assign(R, keyEnd, r->second);
const auto& B = std::next( // Culd be made a bit faster if lowest is guaranteed not to be used as a valid key,
l == m_map.begin() || !(std::prev(l)->second == val) ? m_map.insert_or_assign(l, keyBegin, val) :
std::prev(l));
m_map.erase(B, E); // (B,E) is the erase range.
}
What we need here, is a test framework to compare our solutions, add more unit tests and performance tests. Here is mine so far:
/*
* Test framework ans attempted solution for the interval_map problem.
* Created: 2024_03_04, Modified: 2024_03_07
*/
#include <map>
/*
Task Description
interval_map<K,V> is a data structure that efficiently associates intervals of keys of type K with values of type V. Your task is to implement the assign member function of this data structure, which is outlined below.
interval_map<K, V> is implemented on top of std::map. In case you are not entirely sure which functions std::map provides, what they do and which guarantees they provide, we provide an excerpt of the C++ standard here:
Each key-value-pair (k,v) in the std::map means that the value v is associated with the interval from k (including) to the next key (excluding) in the std::map.
Example: the std::map (0,'A'), (3,'B'), (5,'A') represents the mapping
0 -> 'A'
1 -> 'A'
2 -> 'A'
3 -> 'B'
4 -> 'B'
5 -> 'A'
6 -> 'A'
7 -> 'A'
... all the way to numeric_limits<int>::max()
The representation in the std::map must be canonical, that is, consecutive map entries must not have the same value: ..., (0,'A'), (3,'A'), ... is not allowed. Initially, the whole range of K is associated with a given initial value, passed to the constructor of the interval_map<K,V> data structure.
Key type K
besides being copyable and assignable, is less-than comparable via operator<
is bounded below, with the lowest value being std::numeric_limits<K>::lowest()
does not implement any other operations, in particular no equality comparison or arithmetic operators
Value type V
besides being copyable and assignable, is equality-comparable via operator==
does not implement any other operations
*/
#define STYLE 2
enum EAalgorithms {
AMsReference,
AMsSubmission,
AMsFastest,
ALGMAX
};
const char* algorithms[] = {
"AM's Reference",
"AM's Submission",
"AM's Fastest",
};
static int algorithm{ 0 };
static int alg_min = 0;
static int alg_max = ALGMAX;
static int index_op_warnings = 0;
template<typename K, typename V>
class interval_map
{
public:
std::map<K, V> m_map;
#if STYLE==2
V m_valBegin;
// constructor associates whole range of K with val
interval_map(V const& val)
: m_valBegin(val)
{}
// look-up of the value associated with key
V const& operator[](K const& key) const {
index_op_warnings++;
auto it = m_map.upper_bound(key);
if (it == m_map.begin())
return m_valBegin;
return (--it)->second;
}
#else
// constructor associates whole range of K with val by inserting (K_min, val) into the map
interval_map(V const& val) { m_map.insert(m_map.end(), std::make_pair(std::numeric_limits<K>::lowest(), val)); }
// look-up of the value associated with key
V const& operator[](K const& key) const { return (--m_map.upper_bound(key))->second; }
#endif
// Assign value val to interval [keyBegin, keyEnd).
// Overwrite previous values in this interval.
// Conforming to the C++ Standard Library conventions, the interval
// includes keyBegin, but excludes keyEnd.
// If !( keyBegin < keyEnd ), this designates an empty interval,
// and assign must do nothing.
void assign(K const& keyBegin, K const& keyEnd, V const& val)
{
if (!(keyBegin < keyEnd))
return; // trivial rejection of degenerated segments.
switch (algorithm)
{
case AMsReference:
{
#if STYLE==2
if (m_map.begin() == m_map.end())
m_map.insert_or_assign(m_map.begin(), keyEnd, m_valBegin); // avoiding std::numeric_limits<K>::lowest();
#endif
auto l = m_map.lower_bound(keyBegin); // O(log(N))
auto R = m_map.upper_bound(keyEnd); // O(log(N))
auto r = std::prev(R);
m_map.erase( // B
std::next(l == m_map.begin() ? m_map.insert_or_assign(l, keyBegin, val) :
std::prev(l)->second == val ? std::prev(l) :
m_map.insert_or_assign(l, keyBegin, val))
,
r->second == val ? R : // E
!(r->first < keyEnd) && !(keyEnd < r->first) ? r :
m_map.insert_or_assign(R, keyEnd, r->second)
);
break;
}
case AMsSubmission:
{
// keyBegin keyEnd
// vvvvvvvvvvvv-
// [-----------) <- interval [b,e) as argument
// [-----X----X-----X-------X-----) <- map content
// Z L l r R end
// aaaaaabbbbbccccccbbbbbbbbaaaaaa-
// Adding the Z infimum element to the map simplifies the algorithm in that any key value would be guaranteed to have a lower bound.
const auto& z = std::numeric_limits<K>::lowest();
#if STYLE==2
if (m_map.begin() == m_map.end())
m_map.insert(std::make_pair(z, m_valBegin)).first;
#endif
const auto& l = m_map.lower_bound(keyBegin); // O(log(N))
#if 1
auto R = l; // lower bound is guaranteed by the Z imfimum.
while (R != m_map.end() && !(keyEnd < R->first)) ++R; // O(D) is faster than O(log(N) for D << N.
#else
const auto& R = m_map.upper_bound(keyEnd); // O(log(N)), is slower than O(D) for short [b,e) distances
#endif
const auto& r = std::prev(R); // r is never at the end.
const auto& E = r->second == val ? R :
!(r->first < keyEnd) && !(keyEnd < r->first) ? r :
m_map.insert_or_assign(R, keyEnd, r->second);
const auto& B = std::next( // Culd be made a bit faster if lowest is guaranteed not to be used as a valid key,
l == m_map.begin() || !(std::prev(l)->second == val) ? m_map.insert_or_assign(l, keyBegin, val) :
std::prev(l));
m_map.erase(B, E); // (B,E) is the erase range.
// [-----X-X-----------X----X-----) <- resulting map content
// Z L B E R end
// aaaaaabbvvvvvvvvvvvvbbbbbaaaaaa-
// 0123456789012345678901234567890-
// 0 1 2 3
break;
}
case AMsFastest:
{
#if STYLE==2
if (m_map.begin() == m_map.end())
m_map.insert_or_assign(m_map.begin(), keyEnd, m_valBegin); // avoiding std::numeric_limits<K>::lowest();
#endif
// b e
// vvvvvvvvvvvv-
// [-----------) <- interval [b,e) as argument
// [-----X----X-----X-------X-----) <- map content
// Z L l r R end
// aaaaaabbbbbccccccbbbbbbbbaaaaaa-
const auto& l = m_map.lower_bound(keyBegin); // O(log(N))
auto R = l;
while (R != m_map.end() && !(keyEnd < R->first)) ++R; // O(D) is faster than O(log(N) for D << N.
const auto& r = std::prev(R); // r is never at the end.
m_map.erase( // (B,E) is the erase segment, opened at both ends
/* auto& E = */ std::next( // E
l == m_map.begin() || !(std::prev(l)->second == val) ?
m_map.insert_or_assign(l, keyBegin, val) :
/* auto& L = */ std::prev(l)),
/* auto& B = */ r->second == val ? R : // B
!(r->first < keyEnd) && !(keyEnd < r->first) ? r :
m_map.insert_or_assign(R, keyEnd, r->second));
// [-----X-X-----------X----X-----) <- resulting map content
// Z L B E R end
// aaaaaabbvvvvvvvvvvvvbbbbbaaaaaa-
// 0123456789012345678901234567890-
// 0 1 2 3
break;
}
}
}
};
// Many solutions we receive are incorrect. Consider using a randomized test
// to discover the cases that your implementation does not handle correctly.
// We recommend to implement a test function that tests the functionality of
// the interval_map, for example using a map of unsigned int intervals to char.
#include <iostream>
#include <windows.h>
//#include <math.h>
class KT
{
friend std::ostream& operator<<(std::ostream& os, const KT& K);
unsigned int val;
public:
static int errors;
static int warnings;
KT() : val(0) { errors++; } //KT() = delete;
constexpr KT(unsigned int val) : val(val) { warnings++; }
constexpr bool operator<(const KT& other) const { return val < other.val; }
};
int KT::errors = 0;
int KT::warnings = 0;
class KF
{
friend std::ostream& operator<<(std::ostream& os, const KF& K);
float val;
public:
static int errors;
static int warnings;
KF() : val(0) { errors++; } //KF() = delete;
KF(float val) : val(val) { warnings++; }
bool operator< (KF other) const { return other.val - val > 1.e-4f; }
};
int KF::errors = 0;
int KF::warnings = 0;
class KS
{
friend std::ostream& operator<<(std::ostream& os, const KS& K);
std::string val;
public:
static int errors;
static int warnings;
KS() : val("") { errors++; } //KS() = delete;
KS(const char* cstr) : val(std::string(cstr)) { warnings++; }
bool operator< (KS other) const { return val < other.val; }
};
int KS::errors = 0;
int KS::warnings = 0;
class VT
{
friend std::ostream& operator<<(std::ostream& os, const VT& K);
char val;
public:
static int errors;
static int warnings;
VT() : val(0) { errors++; } //VT() = delete;
constexpr VT(unsigned int val) : val(val) { warnings++; }
constexpr bool operator==(const VT& other) const { return val == other.val; }
};
int VT::errors = 0;
int VT::warnings = 0;
std::ostream& operator<<(std::ostream& os, const KF& K) { os << K.val; return os; }
std::ostream& operator<<(std::ostream& os, const VT& V) { os << V.val; return os; }
std::ostream& operator<<(std::ostream& os, const KT& K) { os << K.val; return os; }
std::ostream& operator<<(std::ostream& os, const KS& K) { os << K.val; return os; }
// uncomment to include performance counters
// #include <windows.h>
#include <assert.h>
void IntervalMapTest()
{
bool error = false;
#if 1 // These defines are a bit much, but they essentialise the unit tests that follow.
#define START_TEST(name) bool error = false; printf("%30s: ", #name);
#define LENGTH(arr) (sizeof(arr) / sizeof(arr[0]))
#define START_TEST(name) bool error = false; printf("%30s: ", #name);
#define TEST(cond) if (error = !(cond)) printf(" error %s ", #cond)
#define CLEAR_COUNTERS \
index_op_warnings = 0; \
KT::errors = KF::errors = KS::errors = VT::errors = 0; \
KT::warnings = KF::warnings = KS::warnings = VT::warnings = 0;
#define TEST_COUNTERS\
index_op_warnings && printf("Use of operator [] warning, "); \
error |= KT::errors && printf("KT default ctor error, "); \
error |= KF::errors && printf("KF default ctor error, "); \
error |= KS::errors && printf("KS default ctor error, "); \
error |= VT::errors && printf("VT default ctor error, "); \
error |= KT::warnings && printf("KT-ctor warning, "); \
error |= KF::warnings && printf("KF-ctor warning, "); \
error |= KS::warnings && printf("KS-ctor warning, "); \
error |= VT::warnings && printf("VT-ctor warning, ");
#define SET_INTERVALS\
for (const auto &interval : intervals)\
m.assign(interval.L, interval.R, interval.Label);
#define TEST_EXPECTATIONS \
TEST_COUNTERS; \
for (const auto &exp : expectations)\
if (!(m[exp.pos] == exp.Label))\
{\
std::cout << std::endl << "\t\t\t m[" << exp.pos << "] = " << m[exp.pos] << "<>" << exp.Label << " ";\
error = true;\
}
#define TEST_VERIFY \
for (auto it = m.m_map.begin(); it != m.m_map.end(); ++it)\
{\
auto next = std::next(it);\
if (next != m.m_map.end() && it->second == next->second)\
{\
printf(" Map has duplicated consecutive values. ");\
error = true;\
break;\
}\
}\
std::cout << "size: " << m.m_map.size();\
for (auto entry : m.m_map) \
std::cout << ", {" << entry.first << "," << entry.second << "}"; \
printf("%s\n", error ? ", FAIL." : ", ok.");
#define END_TEST {CLEAR_COUNTERS; SET_INTERVALS; TEST_EXPECTATIONS; TEST_VERIFY;}
#endif
{
{
START_TEST(PredefinedTest);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {1,3,'B'} };
struct { int pos; char Label; } expectations[] = { {-2,'A'}, {-1, 'A'}, {0,'A'}, {1,'B'}, {2,'B'}, {3,'A'}, {4,'A'}, {5, 'A'} };
END_TEST;
}
{
START_TEST(Simple);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {1,3,'B'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'B'}, {2,'B'}, {3,'A'} };
END_TEST;
}
#if 1
{
START_TEST(DistinctSegments);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {1,3,'B'}, {6,8,'C'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'B'}, {2,'B'}, {3,'A'}, {4,'A'}, {5,'A'}, {6,'C'}, {7,'C'}, {8,'A'} };
END_TEST;
}
{
START_TEST(PyramidUp);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {1, 6, 'B'}, {3, 5, 'C'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'B'}, {2,'B'}, {3,'C'}, {4,'C'}, {5,'B'},{6, 'A'} };
END_TEST;
}
{
START_TEST(PyramidDown);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {3, 5, 'B'}, {1, 6, 'C'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'C'}, {2,'C'}, {3,'C'}, {4,'C'}, {5,'C'},{6, 'A'} };
END_TEST;
}
{
START_TEST(GrowRight);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {1, 5, 'B'}, {3, 6, 'B'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'B'}, {2,'B'}, {3,'B'}, {4,'B'}, {5,'B'},{6, 'A'} };
END_TEST;
}
{
START_TEST(GrowLeftRight);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {2, 3, 'B'}, {1, 5, 'B'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'B'}, {2,'B'}, {3,'B'}, {4,'B'}, {5,'A'} };
END_TEST;
}
{
START_TEST(OverrideLeft);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {3, 6, 'C'}, {1, 5, 'B'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'B'}, {2,'B'}, {3,'B'}, {4,'B'}, {5,'C'},{6, 'A'} };
END_TEST;
}
{
START_TEST(OverrideRight);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {1, 5, 'B'}, {3, 6, 'C'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'B'}, {2,'B'}, {3,'C'}, {4,'C'}, {5,'C'},{6, 'A'} };
END_TEST;
}
{
START_TEST(GrowLeft);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {3, 5, 'B'}, {1, 4, 'B'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'B'}, {2,'B'}, {3,'B'}, {4,'B'}, {5,'A'} };
END_TEST;
}
{
START_TEST(OverrideRight);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {2, 5, 'B'}, {5, 8, 'C'}, {4, 5, 'A'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'A'}, {2,'B'}, {3,'B'}, {4,'A'}, {5,'C'}, {6, 'C'}, {7, 'C'}, {8, 'A'} };
END_TEST;
}
{
START_TEST(InbetweenPriorLimits);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {1, 5, 'B'}, {2, 3, 'B'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'B'}, {2,'B'}, {3,'B'}, {4,'B'}, {5,'A'} };
END_TEST;
}
{
START_TEST(ResetRight);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {1, 5, 'B'}, {4, 6, 'A'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'B'}, {2,'B'}, {3,'B'}, {4,'A'}, {5,'A'} };
END_TEST;
}
{
START_TEST(SetResetSameInterval);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {1, 5, 'B'}, {1, 5, 'A'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'A'}, {2,'A'}, {3,'A'}, {4,'A'}, {5,'A'} };
END_TEST;
}
{
START_TEST(RestoreInfimum);
interval_map<unsigned int, char> m('A');
struct { unsigned int L, R; char Label; } intervals[] = { {1, 5, 'B'}, {0, 7, 'A'} };
struct { unsigned int pos; char Label; } expectations[] = { {0,'A'}, {1,'A'}, {2,'A'}, {3,'A'}, {4,'A'}, {5,'A'} };
END_TEST;
}
#endif
{
START_TEST(DevelopmentTest0);
interval_map<KT, VT> m('a');
struct { KT L, R; VT Label; } intervals[] = { {0, 6, 'a'}, {6,10,'b'}, {11, 17,'c'}, {17,25,'b'}, {8,20,'v'} };
struct { KT pos; VT Label; } expectations[] = { {0,'a'}, {6,'b'}, {7,'b'}, {8,'v'}, {19,'v'}, {20,'b'}, {24, 'b'}, {25, 'a'}, {100, 'a'} };
END_TEST;
}
{
START_TEST(DevelopmentTest1);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = { {3, 6, 'B'}, {2, 5, 'C'}, {4, 7, 'A'} };
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'A'}, {2,'C'}, {3,'C'}, {4,'A'}, {5,'A'}, {6, 'A'}, {7, 'A'} };
END_TEST;
}
{
START_TEST(DevelopmentTest2);
interval_map<KT, VT> m('A');
struct { KT L, R; VT Label; } intervals[] = {
{8, 12, 'B'},
{2, 12, 'B'},
{2, 12, 'C'},
{5, 12, 'C'},
{4, 10, 'C'},
{4, 12, 'C'},
{8, 13, 'A'},
{6, 9, 'D'},
};
struct { KT pos; VT Label; } expectations[] = { {0,'A'}, {1,'A'}, {2,'C'}, {3,'C'}, {4,'C'}, {5,'C'}, {6, 'D'}, {7, 'D'}, {8, 'D'}, {9, 'A'} };
END_TEST;
}
{
START_TEST(FloatKey);
interval_map<KF, VT> m('A');
struct { KF L, R; VT Label; } intervals[] = { {-1, 1, 'B'} };
struct { KF pos; VT Label; } expectations[] = { {-2.f,'A'}, {-1.1f,'A'}, {-1.f,'B'}, {1.f,'A'}, {1.1f,'A'} };
END_TEST;
}
{
START_TEST(StringKey);
interval_map<KS, VT> m('A');
struct { KS L, R; VT Label; } intervals[] = { {"Alpha", "Beta", 'B'}, {"Delta", "Epsilon", 'C'} };
struct { KS pos; VT Label; } expectations[] = { {"_LessThanAlpha",'A'}, {"Alpha",'B'}, {"Beta", 'A'}, {"Delta",'C'}, {"Epsilon",'A'}, {"Zeta",'A'} };
END_TEST;
}
}
const char* perf_tests[] = { "AddRight", "Pyramid", "Skew", "Remove" };
for (int type = 0; type < LENGTH(perf_tests); type++)
{
struct Counter
{
double Freq = 0.0;
__int64 Start = 0;
Counter()
{
#ifdef _WINDOWS_
LARGE_INTEGER li;
if (!QueryPerformanceFrequency(&li))
std::cout << "Cannot obtain a performance counter!\n";
Freq = double(li.QuadPart);
QueryPerformanceCounter(&li);
Start = li.QuadPart;
#endif
}
operator double() const
{
#ifdef _WINDOWS_
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
return double(li.QuadPart - Start) / Freq;
#else
return 0;
#endif
}
};
START_TEST(PerformanceTest);
int v = 0;
int factor = 100;
#ifndef _DEBUG
factor *= 60;
#endif
if (type == 0)
{
interval_map<KT, VT> m(0);
const int width = 10;
const int max_test = 1000 * factor;
const int expsz = max_test + 1;
Counter counter;
for (int i = 0; i < max_test; i++)
m.assign(i * width, (i + 1) * width, --v);
std::cout << "AddRight time: " << counter << " m.size: " << m.m_map.size()
<< " expected sz: " << expsz << (m.m_map.size() == expsz ? " okay" : " mismatch") << "\n";
}
else if (type == 1)
{
interval_map<int, unsigned int> m(0);
const int max_test = factor * 5000;
const int expsz = max_test + 1;
Counter counter;
for (int i = 0; i < max_test; i++)
m.assign(i, max_test - i, --v);
std::cout << "Pyramid time: " << counter << " m.size: " << m.m_map.size()
<< " expected sz: " << expsz << (m.m_map.size() == expsz ? " okay" : " mismatch") << "\n";
}
else if (type == 2)
{
interval_map<KT, VT> m(0);
int sqrtf = (int)sqrt(factor);
const int max_test = 100 * sqrtf;
const int max_drift = 100 * sqrtf;
const int expsz = max_test + max_drift - 1;
Counter counter;
for (int k = 0; k < max_drift; k++)
for (int i = 0; i < max_test; i++)
m.assign(k + i, k + max_test - i, --v);
std::cout << "Skew time: " << counter << " m.size: " << m.m_map.size()
<< " expected sz: " << expsz << (m.m_map.size() == expsz ? " okay" : " mismatch") << "\n";
}
else if (type == 3)
{
interval_map<KT, VT> m(0);
int sqrtf = (int)sqrt(factor);
const int width = 1;
const int stride = 10;
const int max_drift = 100 * sqrtf;
const int max_test = 100 * sqrtf;
const int expsz = 3;
Counter counter;
for (int k = 0; k < max_drift; k++)
{
for (int i = 0; i < max_test; i++)
m.assign(k * i * width, k * (i + 1) * width, --v);
int from = k * (max_test - 1) * width;
int to = k * (max_test + 1) * width;
for (int i = max_test; i > 0; i -= stride)
m.assign(i, to, --v);
}
std::cout << "Remove time: " << counter << " m.size: " << m.m_map.size()
<< " expected sz: " << expsz << (m.m_map.size() == expsz ? " okay" : " mismatch") << "\n";
}
}
}
int main(int argc, char* argv[])
{
for (algorithm = alg_min; algorithm < alg_max; algorithm++)
{
std::cout << algorithms[algorithm] << " algorithm:" << std::endl;
IntervalMapTest();
}
}
auto nextInterval = --m_map.upper_bound(keyEnd);, ifupper_boundreturnsbegin()(as for empty map), you have UB.trycatchsupposed to do ? strong guaranty exception ? there is potentially am_map.erasenot restored in that case...