From bc37faae71a50c240986f2da035c5206bf0692f2 Mon Sep 17 00:00:00 2001 From: Vahagn Khachatryan Date: Tue, 23 Dec 2014 13:15:24 +0400 Subject: [PATCH] think-cell test moving to linux for debugging. --- interviews/think-cell/IntervalMap.cpp | 369 ++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 interviews/think-cell/IntervalMap.cpp diff --git a/interviews/think-cell/IntervalMap.cpp b/interviews/think-cell/IntervalMap.cpp new file mode 100644 index 0000000..2def04d --- /dev/null +++ b/interviews/think-cell/IntervalMap.cpp @@ -0,0 +1,369 @@ +/* Check .vimrc.local defs. +VIM: let g:lcppflags="-std=c++11 -O2 -pthread" +VIM: let g:wcppflags="/O2 /EHsc /DWIN32" +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. +// Your task is to implement the assign member function of this data structure, which is outlined below. + +// interval_map 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 have attached an excerpt of the C++1x draft standard at the end of this +// file for your convenience. + +// Each key-value-pair (k,v) in the m_map member means that the value v is associated to the interval from k (including) to +// the next key (excluding) in m_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::max() + +// 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. +// Initially, the whole range of K is associated with a given initial value, passed to the constructor. +template +class interval_map { + friend void IntervalMapTest(); + +private: + std::map m_map; + +public: + // 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.begin(),std::make_pair(std::numeric_limits::min(),val)); + }; + + // Assign value val to interval [keyBegin, keyEnd). + // Overwrite previous values in this interval. Do not change values outside 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, const V& val ) { + // + // If !( keyBegin < keyEnd ), this designates an empty interval, and + // assign must do nothing. + // + 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. + // + 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)); + } + // + // There should always be std::numeric_limits::min() + // + 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; + } + // + // Find the range to be removed. + // + m_map.erase( ++it, ie ); + } + + // look-up of the value associated with key + V const& operator[]( K const& key ) const { + return ( --m_map.upper_bound(key) )->second; + } + +private: + // + // Since this is a template class, I assume this method will be + // instantiated only in IntervalMapTest. Hence, key and value are pretty + // printable. + // + std::ostream& print( std::ostream& os ) const { + for( auto e: m_map ) + os << '[' << e.first << ',' << e.second << ']'; + return os; + } +}; + +// 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::min(); +// - does not implement any other operations, in particular no equality comparison or arithmetic operators. +template +class test_key { + T value; +public: + /*explicit*/ test_key( T v ) + : value(v) + {} + + bool operator < ( const test_key& op ) const { + return value < op.value; + } + + std::ostream& print( std::ostream& os ) const { + return os << value; + } +}; +namespace std { + // + // I hope this much of implementation of numeric_limits is enough for my + // tests. + // + template + struct numeric_limits<::test_key > : public numeric_limits { + typedef ::test_key value_type; + typedef numeric_limits base_type; + static value_type min() { + return value_type( base_type::min() ); + } + static value_type max() { + return value_type( base_type::max() ); + } + }; +} +template +inline std::ostream& operator << ( std::ostream& os, test_key t ) { + return t.print(os); +} +template +void test_test_key() { + + typedef test_key test_type; + test_type a(T(3.5)); + test_type b(T(5.5)); + + std::cout << "a::min() << std::endl; + std::cout << "max: " << std::numeric_limits::max() << std::endl; +} + +// Value type V +// - besides being copyable and assignable, is equality-comparable via operator== ; +// - does not implement any other operations. +template +class test_value { + T value; +public: + /*explicit*/ test_value( T v ) + : value(v) + {} + + bool operator == ( const test_value& op ) const { + return value == op.value; + } + + std::ostream& print( std::ostream& os ) const { + return os << value; + } +}; +template +void test_test_value() { + test_value a(T(3.5)); + 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); +} + +// 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() { +#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; + + interval_map im(0); + + std::cout << "constrtuction: \n\t"; + im.print(std::cout); + std::cout << std::endl; + + + im.assign( 1000, 2000, 1 ); + std::cout << "constrtuction: \n\t"; + im.print(std::cout); + std::cout << std::endl; +} + +int main(int argc, char* argv[]) { + IntervalMapTest(); + return 0; +} + +/* +The following paragraphs from the final draft of the C++1x ISO standard describe the available +operations on a std::map container, their effects and their complexity. + +23.2.1 General container requirements + +§1 Containers are objects that store other objects. They control allocation and deallocation of +these objects through constructors, destructors, insert and erase operations. + +§6 begin() returns an iterator referring to the first element in the container. end() returns +an iterator which is the past-the-end value for the container. If the container is empty, +then begin() == end(); + +24.2.1 General Iterator Requirements + +§1 Iterators are a generalization of pointers that allow a C++ program to work with different +data structures. + +§2 Since iterators are an abstraction of pointers, their semantics is a generalization of most +of the semantics of pointers in C++. This ensures that every function template that takes +iterators works as well with regular pointers. + +§5 Just as a regular pointer to an array guarantees that there is a pointer value pointing past +the last element of the array, so for any iterator type there is an iterator value that points +past the last element of a corresponding sequence. These values are called past-the-end values. +Values of an iterator i for which the expression *i is defined are called dereferenceable. +The library never assumes that past-the-end values are dereferenceable. Iterators can also have +singular values that are not associated with any sequence. [ Example: After the declaration of +an uninitialized pointer x (as with int* x;), x must always be assumed to have a singular +value of a pointer. —end example ] Results of most expressions are undefined for singular +values; the only exceptions are destroying an iterator that holds a singular value, the +assignment of a non-singular value to an iterator that holds a singular value, and, for +iterators that satisfy the DefaultConstructible requirements, using a value-initialized +iterator as the source of a copy or move operation. + +§10 An invalid iterator is an iterator that may be singular. (This definition applies to pointers, +since pointers are iterators. The effect of dereferencing an iterator that has been invalidated +is undefined.) + +23.2.4 Associative containers + +§1 Associative containers provide fast retrieval of data based on keys. The library provides four +basic kinds of associative containers: set, multiset, map and multimap. + +§4 An associative container supports unique keys if it may contain at most one element for each key. +Otherwise, it supports equivalent keys. The set and map classes support unique keys; the multiset +and multimap classes support equivalent keys. + +§5 For map and multimap the value type is equal to std::pair. Keys in an associative +container are immutable. + +§6 iterator of an associative container is of the bidirectional iterator category. +(i.e., an iterator i can be incremented and decremented: ++i; --i;) + +§9 The insert member functions (see below) shall not affect the validity of iterators and references +to the container, and the erase members shall invalidate only iterators and references to the erased +elements. + +§10 The fundamental property of iterators of associative containers is that they iterate through the +containers in the non-descending order of keys where non-descending is defined by the comparison +that was used to construct them. + +Associative container requirements (in addition to general container requirements): + +std::pair insert(std::pair const& t) +Effects: Inserts t if and only if there is no element in the container with key equivalent to the key of t. +The bool component of the returned pair is true if and only if the insertion takes place, and the iterator +component of the pair points to the element with key equivalent to the key of t. +Complexity: logarithmic + +iterator insert(const_iterator p, std::pair const& t) +Effects: Inserts t if and only if there is no element with key equivalent to the key of t in containers with +unique keys. Always returns the iterator pointing to the element with key equivalent to the key of t. +Complexity: logarithmic in general, but amortized constant if t is inserted right before p. + +size_type erase(key_type const& k) +Effects: Erases all elements in the container with key equivalent to k. Returns the number of erased elements. +Complexity: log(size of container) + number of elements with key k + +iterator erase(const_iterator q) +Effects: Erases the element pointed to by q. Returns an iterator pointing to the element immediately following +q prior to the element being erased. If no such element exists, returns end(). +Complexity: Amortized constant + +iterator erase(const_iterator q1, const_iterator q2) +Effects: Erases all the elements in the left-inclusive and right-exclusive range [q1,q2). Returns q2. +Complexity: Amortized O(N) where N has the value distance(q1, q2). + +void clear() +Effects: erase(begin(), end()) +Post-Condition: empty() returns true +Complexity: linear in size(). + +iterator find(key_type const& k); +Effects: Returns an iterator pointing to an element with the key equivalent to k, or end() if such an element is not found +Complexity: logarithmic + +size_type count(key_type const& k) +Effects: Returns the number of elements with key equivalent to k +Complexity: log(size of map) + number of elements with key equivalent to k + +iterator lower_bound(key_type const& k) +Effects: Returns an iterator pointing to the first element with key not less than k, or end() if such an element is not found. +Complexity: logarithmic + +iterator upper_bound(key_type const& k) +Effects: Returns an iterator pointing to the first element with key greater than k, or end() if such an element is not found. +Complexity: logarithmic + +23.4.1 Class template map + +§1 A map is an associative container that supports unique keys (contains at most one of each key value) and provides +for fast retrieval of values of another type T based on the keys. The map class supports bidirectional iterators. + +23.4.1.2 map element access + +T& operator[](const key_type& x); +Effects: If there is no key equivalent to x in the map, inserts value_type(x, T()) into the map. +Returns: A reference to the mapped_type corresponding to x in *this. +Complexity: logarithmic. + +T& at(const key_type& x); +const T& at(const key_type& x) const; +Returns: A reference to the element whose key is equivalent to x. +Throws: An exception object of type out_of_range if no such element is present. +Complexity: logarithmic. +*/