diff --git a/interviews/think-cell/IntervalMap.cpp b/interviews/think-cell/IntervalMap.cpp index 2def04d..44b17b3 100644 --- a/interviews/think-cell/IntervalMap.cpp +++ b/interviews/think-cell/IntervalMap.cpp @@ -7,6 +7,7 @@ VIM: let g:argv="" #include #include #include +#include // interval_map is a data structure that efficiently associates intervals of keys of type K with values of type V. @@ -57,30 +58,54 @@ public: if ( !(keyBegin < keyEnd ) ) return; // - // If there is a need to break the last interval with keyEnd do it. - // Everything prior itEnd till itBegin will be removed. + // - is bounded below, with the lowest value being std::numeric_limits::min(); + // std::numeric_limits::min() actually is not the smalles number. // - auto ie = m_map.lower_bound( keyEnd ); - if ( ie != m_map.end() - && keyEnd < ie->first ) { - auto hint = ie--; - ie = m_map.insert(hint,std::make_pair(keyEnd,ie->second)); + if (keyBegin < std::numeric_limits::min()) + throw std::runtime_error("K should be bounded below, with the lowest value being std::numeric_limits::min()."); + // + // Find the interval pointer before which the new interval ends. + // + auto ie = m_map.lower_bound(keyEnd); + // + // If there is a need to break the last interval with keyEnd do it now. + // Everything prior 'ie' till 'ib' will be removed later. + // + if ( ie == m_map.end() && keyEnd < std::numeric_limits::max() + || ie != m_map.end() && keyEnd < ie->first ) { + if (!( std::prev(ie)->second == val )) + ie = m_map.insert(ie, std::make_pair(keyEnd,std::prev(ie)->second)); } // - // There should always be std::numeric_limits::min() + // If the interval next to the inserting one should be joined? // - auto it = --m_map.upper_bound( keyBegin ); - if ( !(it->second == val ) ) - { - if ( it->first < keyBegin ) - it = m_map.insert(++it,std::make_pair(keyBegin,val)); - else - it->second = val; - } + else if (ie != m_map.end() && ie->second == val) + ++ie; // - // Find the range to be removed. + // Find the interval pointer before which the new interval begins. // - m_map.erase( ++it, ie ); + auto ib = m_map.lower_bound( keyBegin ); + // + // If the interval before should be joined then ... + // Note: there should always be std::numeric_limits::min() + // + if (ib != m_map.begin() && std::prev(ib)->second == val) + --ib; + // + // If keyBegin is less than 'ib' then insert an new interval. + // + else if ( ib == m_map.end() || keyBegin < ib->first ) + ib = m_map.insert(ib, std::make_pair(keyBegin, val)); + // + // Otherwise just change the value since keyBegin matches with an + // existing interval. + // + else + ib->second = val; + // + // Removed the range between ib and ie. + // + m_map.erase( ++ib, ie ); } // look-up of the value associated with key @@ -150,12 +175,8 @@ void test_test_key() { test_type b(T(5.5)); std::cout << "a::min() << std::endl; std::cout << "max: " << std::numeric_limits::max() << std::endl; } @@ -185,55 +206,158 @@ void test_test_value() { test_value b(T(5.5)); std::cout << "a==b before a=b: " << (a == b) << std::endl; - a = b; std::cout << "a==b after a=b: " << (a == b) << std::endl; - -// (a < b); } template inline std::ostream& operator << ( std::ostream& os, test_value t ) { return t.print(os); } +// +// Test throws this exception if fails. +// +struct test_failed : public std::logic_error { + test_failed() + : logic_error("Test failed.") + {} +}; +// +// The representation in m_map must be canonical, that is, consecutive map entries must not have the same value: +// ..., (0,'A'), (3,'A'), ... is not allowed. +// +template +void test_if_canonical(const C& c) { + for (auto it = std::next(c.begin()); it != c.end(); ++it) { + if (it->second == std::prev(it)->second) + throw test_failed(); + } +} // Provide a function IntervalMapTest() here that tests the functionality of the interval_map, // for example using a map of unsigned int intervals to char. // Many solutions we receive are incorrect. Consider using a randomized test to discover // the cases that your implementation does not handle correctly. void IntervalMapTest() { +try { #if 0 std::cout << "=============================================" << std::endl; std::cout << "Test test_key" << std::endl; test_test_key(); - + std::cout << "=============================================" << std::endl; std::cout << "Test test_key" << std::endl; test_test_key(); - + std::cout << "=============================================" << std::endl; std::cout << "Test test_value" << std::endl; test_test_value(); - + std::cout << "=============================================" << std::endl; std::cout << "Test test_value" << std::endl; test_test_value(); #endif - typedef test_key key; - typedef test_value val; + // Check that key value is not less the std::numeric_limits::min(). + try { + interval_map, test_value> im(0); + im.assign(0, 500, 3); + throw test_failed(); + } + catch (const std::runtime_error&) { + } - interval_map im(0); + // + // Check map for some interesting values. + // + typedef test_key key; + typedef test_value val; + interval_map im(0); - std::cout << "constrtuction: \n\t"; - im.print(std::cout); - std::cout << std::endl; - + std::stringstream log; + auto TEST = [&log,&im](const std::string& golden ) { + test_if_canonical(im.m_map); + std::stringstream ss; + im.print(ss); + log << ss.str() << std::endl; + if (ss.str() != golden) + throw test_failed(); + }; + // check initial state. + TEST("[-2147483648,0]"); - im.assign( 1000, 2000, 1 ); - std::cout << "constrtuction: \n\t"; - im.print(std::cout); - std::cout << std::endl; + im.assign(std::numeric_limits::min(), std::numeric_limits::min(), -1); + TEST("[-2147483648,0]"); + + im.assign(std::numeric_limits::max(), std::numeric_limits::max(), -1); + TEST("[-2147483648,0]"); + + im.assign(0, 0, -1); + TEST("[-2147483648,0]"); + + im.assign(std::numeric_limits::min(), std::numeric_limits::max(), -1); + TEST("[-2147483648,-1]"); + + im.assign(1000, 2000, 1); + TEST("[-2147483648,-1][1000,1][2000,-1]"); + + im.assign(500, 1500, 1); + TEST("[-2147483648,-1][500,1][2000,-1]"); + + im.assign(1000, 1500, 2); + TEST("[-2147483648,-1][500,1][1000,2][1500,1][2000,-1]"); + + im.assign(0, 500, 3); + TEST("[-2147483648,-1][0,3][500,1][1000,2][1500,1][2000,-1]"); + + im.assign(-1000, -500, 3); + TEST("[-2147483648,-1][-1000,3][-500,-1][0,3][500,1][1000,2][1500,1][2000,-1]"); + + im.assign(-500, 0, 3); + TEST("[-2147483648,-1][-1000,3][500,1][1000,2][1500,1][2000,-1]"); + + im.assign(-500, 0, 4); + TEST("[-2147483648,-1][-1000,3][-500,4][0,3][500,1][1000,2][1500,1][2000,-1]"); + + im.assign(2000, std::numeric_limits::max(), 4); + TEST("[-2147483648,-1][-1000,3][-500,4][0,3][500,1][1000,2][1500,1][2000,4]"); + + im.assign(3000, std::numeric_limits::max(), 5); + TEST("[-2147483648,-1][-1000,3][-500,4][0,3][500,1][1000,2][1500,1][2000,4][3000,5]"); + + im.assign(2000, 3000, 5); + TEST("[-2147483648,-1][-1000,3][-500,4][0,3][500,1][1000,2][1500,1][2000,5]"); + + im.assign(std::numeric_limits::min(), -1000, 4); + TEST("[-2147483648,4][-1000,3][-500,4][0,3][500,1][1000,2][1500,1][2000,5]"); + + im.assign(std::numeric_limits::min(), -2000, 5); + TEST("[-2147483648,5][-2000,4][-1000,3][-500,4][0,3][500,1][1000,2][1500,1][2000,5]"); + + im.assign(-2000, -1000, 5); + TEST("[-2147483648,5][-1000,3][-500,4][0,3][500,1][1000,2][1500,1][2000,5]"); + + im.assign(-750, 1750, 4); + TEST("[-2147483648,5][-1000,3][-750,4][1750,1][2000,5]"); + + im.assign(-1000, 2000, 5); + TEST("[-2147483648,5]"); + + std::cout << log.str() << std::endl; + std::cout << "PASS" << std::endl; } +catch (const test_failed&) { + std::cerr << "FAIL: test_failed" << std::endl; +} +catch (const std::exception& e) +{ + std::cerr << std::endl + << "FAIL: std::exception(\"" << e.what() << "\")" << std::endl; +} +catch (...) +{ + std::cerr << std::endl + << "FAIL: unknown exception." << std::endl; +}} int main(int argc, char* argv[]) { IntervalMapTest();