diff --git a/notebooks/06_STL_Sequence_Containers.ipynb b/notebooks/06_STL_Sequence_Containers.ipynb
index 42b59ef92f414acea0eac21560f8bd6c31f11ef6..f634dd6f4828e43faa2e89b00a54c739101f8964 100644
--- a/notebooks/06_STL_Sequence_Containers.ipynb
+++ b/notebooks/06_STL_Sequence_Containers.ipynb
@@ -1 +1 @@
-{"metadata":{"orig_nbformat":4,"kernelspec":{"display_name":"C++14","language":"C++14","name":"xcpp14"},"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"14"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Standard Template Library (STL)\n","metadata":{},"id":"0921615f"},{"cell_type":"markdown","source":"#### Executive Summary\n* Since its inclusion in 1994 the STL forms an integral part of the **Standard C++ Library**.\n* The STL is composed of three major components:\n    1. Definition of several **container types** for data storage\n    1. Access functions for those containers, especially so called **iterators**\n    1. **Algorithms** for working with the containers.\n* The following table lists the header files to include for different aspects of the STL\n    \n| STL containers      | Iterators, Functors, Algorithms |\n| :------------------ | :------------------------------ |\n| **```<deque>```**   | **```<iterator>```**            |\n| **```<list>```**    | **```<algorithm>```**           |\n| **```<map>```**     | **```<functional>```**          |\n| **```<queue>```**   |                                 |\n| **```<set>```**     |                                 |\n| **```<stack>```**   |                                 |\n| **```<vector>```**  |                                 |\n\n* As its name suggests the STL makes heavy use of *generic programming* based on **templates**.","metadata":{},"id":"eacfe313"},{"cell_type":"markdown","source":"#### Vector\n* Probably the STL container most often used is **```std::vector```**.\n* It provides an array data-structure that\n  1. can grow and shrink dynamically at runtime\n  2. provides classical index-based access\n  3. allows dynamic bounds checking","metadata":{},"id":"e3d3641e"},{"cell_type":"code","source":"#include <vector>\n#include <iostream>\n#include <iomanip>      // required for setw() below\n\nint main() {\n\n    const int n = 5;\n    \n    std::vector< int > vec;     // need to tell compiler what we will\n                                // store inside vec: < int > = template argument\n    \n    for( int k = 1; k <= n; ++k ) {\n        vec.push_back( k*k );   // append new entries at the end\n    }\n\n    for( int k = 1; k <= n; ++k ) {\n        std::cout << \"Square of \" << k << \" is \" << std::setw(2)\n                  << vec[k-1] << std::endl;    // this container allows standard 0-based subscript access\n    }\n}","metadata":{"trusted":true},"execution_count":1,"outputs":[],"id":"213d8360"},{"cell_type":"code","source":"main();","metadata":{"trusted":true},"execution_count":2,"outputs":[{"name":"stdout","text":"Square of 1 is  1\nSquare of 2 is  4\nSquare of 3 is  9\nSquare of 4 is 16\nSquare of 5 is 25\n","output_type":"stream"}],"id":"bd290a8a"},{"cell_type":"markdown","source":"Of course we can also store data of other types in a vector.","metadata":{},"id":"aa2f2d9d"},{"cell_type":"code","source":"#include <string>\n\nint main() {\n\n    std::vector< double > dVec;\n    dVec.push_back( 22.0 );\n    dVec.push_back( -1.0 );\n    \n    std::vector< std::string > sVec;\n    sVec.push_back( \"Helena\");\n    sVec.push_back( \"Odysseus\" );\n}","metadata":{"trusted":true},"execution_count":3,"outputs":[],"id":"0bb0723b"},{"cell_type":"markdown","source":"So let's try an example with a **home-made class**. We make the con/destructor/s of our class a little talkative, to better understand what's happening.","metadata":{},"id":"aca19e34"},{"cell_type":"code","source":"class Dummy {\n\npublic:\n\n    Dummy() = delete;\n \n    Dummy( const std::string& name ) : name_(name) {\n        std::cout << \"Object '\" << name_ << \"' was created\" << std::endl;\n    }\n    \n    Dummy( const Dummy& other ) {\n        name_ = other.getName() + \"_copy\";\n        std::cout << \"Object '\" << name_ << \"' was created\" << std::endl;\n    }\n\n    ~Dummy() {\n        std::cout << \"Object '\" << name_ << \"' was destroyed\" << std::endl;\n    }\n\n    const std::string& getName() const {\n        return name_;\n    }\n\nprivate:\n    \n  std::string name_;\n\n};","metadata":{"trusted":true},"execution_count":4,"outputs":[],"id":"e5b2a349"},{"cell_type":"code","source":"int main() {\n\n  std::cout << \"-> Creating single objects\" << std::endl;\n  Dummy obj1( \"one\" );\n  Dummy obj2( \"two\" );\n\n  std::cout << \"\\n-> Putting objects in container\" << std::endl;\n  std::vector< Dummy > vec;\n  vec.push_back( obj1 );\n  vec.push_back( obj2 );\n\n  std::cout << \"\\n-> Auto clean-up at program end\" << std::endl;\n}","metadata":{"trusted":true},"execution_count":5,"outputs":[],"id":"bd331ee4"},{"cell_type":"code","source":"main();","metadata":{"trusted":true},"execution_count":6,"outputs":[{"name":"stdout","text":"-> Creating single objects\nObject 'one' was created\nObject 'two' was created\n\n-> Putting objects in container\nObject 'one_copy' was created\nObject 'two_copy' was created\nObject 'one_copy_copy' was created\nObject 'one_copy' was destroyed\n\n-> Auto clean-up at program end\nObject 'one_copy_copy' was destroyed\nObject 'two_copy' was destroyed\nObject 'two' was destroyed\nObject 'one' was destroyed\n","output_type":"stream"}],"id":"a04654d2"},{"cell_type":"markdown","source":"**Observations:**\n1. Apparently ```std::vector``` stores **copies** of the objects we append.\n1. But why is a second copy of **one**, i.e. **one_copy_copy** created?","metadata":{},"id":"e9db8ff6"},{"cell_type":"code","source":" // Suggestions anyone?\n\n\n","metadata":{"trusted":true},"execution_count":7,"outputs":[],"id":"5d4ba36f"},{"cell_type":"markdown","source":"This has to do with the fact that an ```std::vector``` can dynamically grow and shrink. Consequently it has two different important properties\n\n* size = number of elements currently stored inside the vector\n* capacity = currently available slots to store entries inside the vector\n\nThe capacity of the vector is managed dynamically. We can see this in the following experiment:","metadata":{},"id":"e36f2bb9"},{"cell_type":"code","source":"#include <vector>\n#include <iostream>\n#include <iomanip>\n\nint main() {\n\n  std::vector< int > vec;\n\n  std::cout << \"vec: length = \" << std::setw(2) << vec.size()\n            << \", capacity = \" << std::setw(2) << vec.capacity()\n            << std::endl;\n\n  for( int k = 1; k <= 33; k++ ) {\n    vec.push_back( k );\n    std::cout << \"vec: length = \" << std::setw(2) << vec.size()\n              << \", capacity = \" << std::setw(2) << vec.capacity()\n              << std::endl;\n  }\n}\n\nmain();","metadata":{"trusted":true},"execution_count":8,"outputs":[{"name":"stdout","text":"vec: length =  0, capacity =  0\nvec: length =  1, capacity =  1\nvec: length =  2, capacity =  2\nvec: length =  3, capacity =  4\nvec: length =  4, capacity =  4\nvec: length =  5, capacity =  8\nvec: length =  6, capacity =  8\nvec: length =  7, capacity =  8\nvec: length =  8, capacity =  8\nvec: length =  9, capacity = 16\nvec: length = 10, capacity = 16\nvec: length = 11, capacity = 16\nvec: length = 12, capacity = 16\nvec: length = 13, capacity = 16\nvec: length = 14, capacity = 16\nvec: length = 15, capacity = 16\nvec: length = 16, capacity = 16\nvec: length = 17, capacity = 32\nvec: length = 18, capacity = 32\nvec: length = 19, capacity = 32\nvec: length = 20, capacity = 32\nvec: length = 21, capacity = 32\nvec: length = 22, capacity = 32\nvec: length = 23, capacity = 32\nvec: length = 24, capacity = 32\nvec: length = 25, capacity = 32\nvec: length = 26, capacity = 32\nvec: length = 27, capacity = 32\nvec: length = 28, capacity = 32\nvec: length = 29, capacity = 32\nvec: length = 30, capacity = 32\nvec: length = 31, capacity = 32\nvec: length = 32, capacity = 32\nvec: length = 33, capacity = 64\n","output_type":"stream"}],"id":"261a8327"},{"cell_type":"markdown","source":"**What happens, when the capacity is exhausted and must be enlarged?**\n\n* ```std::vector<T>``` is basically a wrapper class around a dynamic array ``T* data``  \n  *(we can actually access this via the data() member function, e.g. to interface with C or Fortran)* \n* When the capacity is exhausted, but we want to add another entry three steps need to happen\n  1. Dynamical allocation of a new internal data-array with a new larger capacity.\n  1. **Copying** of all exisisting entries from the old data-array to the new one.  \n     *[That's what gave us \"Object 'one_copy_copy' was created\" above]*\n  1. Destruction of objects in old data-array and its de-allocation.  \n     *[That's what gave us \"Object 'one_copy' was destroyed\" above]*\n* Afterwards the new entry can be appended.\n\n**Performance issue**  \nThe memory management, but especially the copying constitutes a significant overhead, especially for large arrays.\nWhen we already know what the maximal size of our vector will be, we can avoid this, by **reserving** a large enough\ncapacity.","metadata":{},"id":"b9d88e30"},{"cell_type":"code","source":"#include <vector>\n#include <iostream>\n#include <iomanip>\n\nint main() {\n\n  std::vector< int > vec;\n\n  // make the vector allocate a data-array of sufficient length\n  vec.reserve( 100 );\n    \n  std::cout << \"vec: length = \" << std::setw(2) << vec.size()\n            << \", capacity = \" << std::setw(2) << vec.capacity()\n            << std::endl;\n\n  for( int k = 1; k <= 33; k++ ) {\n    vec.push_back( k );\n    std::cout << \"vec: length = \" << std::setw(2) << vec.size()\n              << \", capacity = \" << std::setw(2) << vec.capacity()\n              << std::endl;\n  }\n}\n\nmain();","metadata":{"trusted":true},"execution_count":9,"outputs":[{"name":"stdout","text":"vec: length =  0, capacity = 100\nvec: length =  1, capacity = 100\nvec: length =  2, capacity = 100\nvec: length =  3, capacity = 100\nvec: length =  4, capacity = 100\nvec: length =  5, capacity = 100\nvec: length =  6, capacity = 100\nvec: length =  7, capacity = 100\nvec: length =  8, capacity = 100\nvec: length =  9, capacity = 100\nvec: length = 10, capacity = 100\nvec: length = 11, capacity = 100\nvec: length = 12, capacity = 100\nvec: length = 13, capacity = 100\nvec: length = 14, capacity = 100\nvec: length = 15, capacity = 100\nvec: length = 16, capacity = 100\nvec: length = 17, capacity = 100\nvec: length = 18, capacity = 100\nvec: length = 19, capacity = 100\nvec: length = 20, capacity = 100\nvec: length = 21, capacity = 100\nvec: length = 22, capacity = 100\nvec: length = 23, capacity = 100\nvec: length = 24, capacity = 100\nvec: length = 25, capacity = 100\nvec: length = 26, capacity = 100\nvec: length = 27, capacity = 100\nvec: length = 28, capacity = 100\nvec: length = 29, capacity = 100\nvec: length = 30, capacity = 100\nvec: length = 31, capacity = 100\nvec: length = 32, capacity = 100\nvec: length = 33, capacity = 100\n","output_type":"stream"}],"id":"664eec8e"},{"cell_type":"markdown","source":"***\nThis will also work for our original example with the class Dummy:","metadata":{},"id":"2352d31e"},{"cell_type":"code","source":"int main() {\n\n  std::cout << \"-> Creating single objects\" << std::endl;\n  Dummy obj1( \"one\" );\n  Dummy obj2( \"two\" );\n\n  std::cout << \"\\n-> Putting objects in container\" << std::endl;\n  std::vector< Dummy > vec;\n  vec.reserve( 2 );\n  vec.push_back( obj1 );\n  vec.push_back( obj2 );\n\n  std::cout << \"\\n-> Auto clean-up at program end\" << std::endl;\n}\n\nmain();","metadata":{"trusted":true},"execution_count":10,"outputs":[{"name":"stdout","text":"-> Creating single objects\nObject 'one' was created\nObject 'two' was created\n\n-> Putting objects in container\nObject 'one_copy' was created\nObject 'two_copy' was created\n\n-> Auto clean-up at program end\nObject 'one_copy' was destroyed\nObject 'two_copy' was destroyed\nObject 'two' was destroyed\nObject 'one' was destroyed\n","output_type":"stream"}],"id":"a5dffa46-65b8-477b-bbd8-a0ad0801d1fb"},{"cell_type":"markdown","source":"**But:** Wouldn't it be nicer/easier to have the reservation be part of the instantiation? Let's check:","metadata":{},"id":"41bf0d83"},{"cell_type":"code","source":"std::vector< Dummy > vec( 2 );","metadata":{"trusted":true},"execution_count":11,"outputs":[{"name":"stderr","text":"In file included from input_line_5:1:\nIn file included from /srv/conda/envs/notebook/include/xeus/xinterpreter.hpp:15:\nIn file included from /srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/vector:65:\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_construct.h:75:38: error: call to deleted constructor of '__cling_N55::Dummy'\n    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }\n                                     ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_uninitialized.h:545:8: note: in instantiation of function template specialization 'std::_Construct<__cling_N55::Dummy>' requested here\n                std::_Construct(std::__addressof(*__cur));\n                     ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_uninitialized.h:601:2: note: in instantiation of function template specialization 'std::__uninitialized_default_n_1<false>::__uninit_default_n<__cling_N55::Dummy *, unsigned long>' requested here\n        __uninit_default_n(__first, __n);\n        ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_uninitialized.h:663:19: note: in instantiation of function template specialization 'std::__uninitialized_default_n<__cling_N55::Dummy *, unsigned long>' requested here\n    { return std::__uninitialized_default_n(__first, __n); }\n                  ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_vector.h:1603:9: note: in instantiation of function template specialization 'std::__uninitialized_default_n_a<__cling_N55::Dummy *, unsigned long, __cling_N55::Dummy>' requested here\n          std::__uninitialized_default_n_a(this->_M_impl._M_start, __n,\n               ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_vector.h:509:9: note: in instantiation of member function 'std::vector<__cling_N55::Dummy, std::allocator<__cling_N55::Dummy> >::_M_default_initialize' requested here\n      { _M_default_initialize(__n); }\n        ^\ninput_line_21:2:23: note: in instantiation of member function 'std::vector<__cling_N55::Dummy, std::allocator<__cling_N55::Dummy> >::vector' requested here\n std::vector< Dummy > vec( 2 );\n                      ^\ninput_line_12:3:5: note: 'Dummy' has been explicitly marked deleted here\n    Dummy() = delete;\n    ^\n","output_type":"stream"},{"ename":"Interpreter Error","evalue":"","traceback":["Interpreter Error: "],"output_type":"error"}],"id":"351ace20"},{"cell_type":"markdown","source":"Problem is that this version of the ```std::vector<T>``` constructor will try to fill/initialise the vector with objects of type ```T``` using their default constructor.","metadata":{},"id":"ec2de34b"},{"cell_type":"markdown","source":"Let us take a look at other ways to construct an std::vector:","metadata":{},"id":"d1cdc134"},{"cell_type":"code","source":"int main() {\n\n  std::cout << \"-> Creating single objects\" << std::endl;\n  Dummy obj1( \"one\" );\n  Dummy obj2( \"two\" );\n\n  std::cout << \"\\n-> Putting objects in container\" << std::endl;\n  std::vector< Dummy > vec;\n\n  // Will fail, as we have no default constructor\n  // std::vector< Dummy > vec2( 2 );\n\n  vec.push_back( obj1 );\n  // std::vector< Dummy > vec2( vec );           // -> construction by copying\n  // std::vector< Dummy > vec2 = vec;            // -> construction by copying (but move semantics?)\n  // std::vector< Dummy > vec2( 5, obj1 );       // -> vector with 5 copies of object one\n  // std::vector< Dummy > vec2( {obj1, obj2} ); // -> using initialiser list (but is a temporary generated here?)\n  // std::vector< Dummy > vec2{obj1, obj2};     // -> clearer version with initialiser list\n\n  vec.emplace_back( \"three\" );     // <- what will this do?\n\n  std::cout << \"\\n-> Auto clean-up at program end\" << std::endl;\n\n}","metadata":{"trusted":true},"execution_count":12,"outputs":[],"id":"b3dfe338"},{"cell_type":"code","source":"main();","metadata":{"trusted":true},"execution_count":13,"outputs":[{"name":"stdout","text":"-> Creating single objects\nObject 'one' was created\nObject 'two' was created\n\n-> Putting objects in container\nObject 'one_copy' was created\nObject 'three' was created\nObject 'one_copy_copy' was created\nObject 'one_copy' was destroyed\n\n-> Auto clean-up at program end\nObject 'one_copy_copy' was destroyed\nObject 'three' was destroyed\nObject 'two' was destroyed\nObject 'one' was destroyed\n","output_type":"stream"}],"id":"ad928c13"},{"cell_type":"markdown","source":"* ```emplace()``` allows to **generate** a new entry at a specified place inside the vector, passing the given arguments to the entry's constructor.\n* ```emplace_back()``` does the same, but **appends at the end**.","metadata":{},"id":"eebc1269"},{"cell_type":"markdown","source":"***\nWe have already seen that we have read/write access to entries of our vector with the **```[ ]```** operator. Another alternative is the **```at()```** method.\n\nThe difference is that **```at()```** will throw an exception, when we commit an out-of-bounds access error.","metadata":{},"id":"dc64b2c9"},{"cell_type":"code","source":"%%file exception.cpp\n\n#include <iostream>\n#include <vector>\n\nint main() {\n\n  std::vector< double > vec( 10, 2.0 );\n\n#ifdef OUT_OF_BOUNDS\n  for( int k = 0; k <= 10; ++k ) {\n    std::cout << \"vec[ \" << k << \" ] = \" << vec[k] << std::endl;\n  }\n  std::cout << \"Too bad we reached this line :-(\" << std::endl;\n#else\n  for( int k = 0; k <= 10; ++k ) {\n    std::cout << \"vec[ \" << k << \" ] = \" << vec.at( k ) << std::endl;\n  }\n#endif\n\n}","metadata":{"trusted":true},"execution_count":14,"outputs":[{"name":"stdout","text":"Overwriting exception.cpp\n","output_type":"stream"}],"id":"7e537a6b"},{"cell_type":"code","source":"!g++ -Wall -DOUT_OF_BOUNDS exception.cpp","metadata":{"trusted":true},"execution_count":15,"outputs":[],"id":"0513b187"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":16,"outputs":[{"name":"stdout","text":"vec[ 0 ] = 2\nvec[ 1 ] = 2\nvec[ 2 ] = 2\nvec[ 3 ] = 2\nvec[ 4 ] = 2\nvec[ 5 ] = 2\nvec[ 6 ] = 2\nvec[ 7 ] = 2\nvec[ 8 ] = 2\nvec[ 9 ] = 2\nvec[ 10 ] = 0\nToo bad we reached this line :-(\n","output_type":"stream"}],"id":"af593caa"},{"cell_type":"code","source":"!g++ -Wall exception.cpp","metadata":{"trusted":true},"execution_count":17,"outputs":[],"id":"131dd4f5"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":18,"outputs":[{"name":"stdout","text":"vec[ 0 ] = 2\nvec[ 1 ] = 2\nvec[ 2 ] = 2\nvec[ 3 ] = 2\nvec[ 4 ] = 2\nvec[ 5 ] = 2\nvec[ 6 ] = 2\nvec[ 7 ] = 2\nvec[ 8 ] = 2\nvec[ 9 ] = 2\nterminate called after throwing an instance of 'std::out_of_range'\n  what():  vector::_M_range_check: __n (which is 10) >= this->size() (which is 10)\nAborted\n","output_type":"stream"}],"id":"e6ce23bd"},{"cell_type":"markdown","source":"### Iterators and range-based access","metadata":{},"id":"8e0987ec"},{"cell_type":"markdown","source":"Our **std::vector** belongs to the class of sequence containers, i.e. the ordering of entries $e_k$ is important\n\n$$ \\Big( e_1, e_2, \\ldots, e_n \\Big)$$\n\nand the same value $v$ can show up at multiple positions ($e_i = v = e_j$ with $i\\neq j$).\n\nAnother type of sequence container is the **linked list**. The **std::list** implements a doubly-linked list, i.e. each entry/element is composed of three parts\n\n$$ \\text{entry} = \\Big( \\text{payload}, \\text{prev}, \\text{next} \\Big)$$\n\nwhere\n * payload = value of the entry\n * prev = information on where to find the predecessor of this entry\n * next = information on where to find the successor of this entry\n \nContrary to a vector, a list does not offer index-based access to its elements. Instead we can do list-traversal.\n\nNow consider the following piece of code:","metadata":{},"id":"80b33721"},{"cell_type":"code","source":"#include <iostream>\n#include <vector>\n\nint main() {\n    using Container = std::vector< int >;\n    Container box;\n\n    const int n = 5;\n    \n    // fill the box with odd numbers\n    for( int k = 0; k < n; k++ ) {\n        box.push_back( 2*k+1 );\n    }\n    \n    // print its contents\n    for( int k = 0; k < n; k++ ) {\n        std::cout << \"box(\" << k << \") = \" << box[k] << std::endl;\n    }\n}\n\nmain();","metadata":{"trusted":true},"execution_count":19,"outputs":[{"name":"stdout","text":"box(0) = 1\nbox(1) = 3\nbox(2) = 5\nbox(3) = 7\nbox(4) = 9\n","output_type":"stream"}],"id":"eee3b34a"},{"cell_type":"markdown","source":"For what we are doing in the program the type of underlying container does not really matter.  \nHow about replacing vector with list for a change?","metadata":{},"id":"33c83653"},{"cell_type":"code","source":"#include <iostream>\n#include <list>      // okay, need to adapt the include\n\nint main() {\n    using Container = std::list< int >;  // need to alter this statement\n    Container box;\n\n    const int n = 5;\n    \n    // fill the box with odd numbers\n    for( int k = 0; k < n; k++ ) {\n        box.push_back( 2*k+1 );\n    }\n    \n    // print its contents\n    for( int k = 0; k < n; k++ ) {\n        std::cout << \"box(\" << k << \") = \" << box[k] << std::endl;\n    }\n}\n\nmain();","metadata":{"trusted":true},"execution_count":20,"outputs":[{"name":"stderr","text":"input_line_27:13:50: error: type 'Container' (aka 'list<int>') does not provide a subscript operator\n        std::cout << \"box(\" << k << \") = \" << box[k] << std::endl;\n                                              ~~~^~\n","output_type":"stream"},{"ename":"Interpreter Error","evalue":"","traceback":["Interpreter Error: "],"output_type":"error"}],"id":"d2806a4f"},{"cell_type":"markdown","source":"***\nNow that is, where **iterators** come into play. They abstract away the details of the underlying container.","metadata":{},"id":"40d19e9a"},{"cell_type":"code","source":"#include <iostream>\n#include <vector>\n\nint main() {\n    using Container = std::vector< int >;\n    Container box;\n\n    const int n = 5;\n    \n    // fill the box with odd numbers\n    for( int k = 0; k < n; k++ ) {\n        box.push_back( 2*k+1 );\n    }\n    \n    // use an iterator-based loop\n    int k = 0;\n    for( Container::iterator it = box.begin(); it != box.end(); it++ ) {\n        std::cout << \"box(\" << k << \") = \" << *it << std::endl;\n        k++;\n    }\n}","metadata":{"trusted":true},"execution_count":21,"outputs":[],"id":"4a6ba619"},{"cell_type":"code","source":"main();","metadata":{"trusted":true},"execution_count":22,"outputs":[{"name":"stdout","text":"box(0) = 1\nbox(1) = 3\nbox(2) = 5\nbox(3) = 7\nbox(4) = 9\n","output_type":"stream"}],"id":"907c51a5"},{"cell_type":"markdown","source":"**Remarks:**\n* The iterator is no pointer, but 'feels' like one, as it overloads the dereferencing operator ```*```.\n* ```box.begin()``` points to the first entry in the container.\n* ```box.end()``` points **after** the last entry in the container. Thus, dereferencing it would be illegal.\n* Do not test for ```it < box.end()```, but for (in)equality only.","metadata":{},"id":"5b9766a5"},{"cell_type":"markdown","source":"Since our loop is using an iterator now, we can with two small modifications switch from a **std::vector** to a **std::list**.","metadata":{},"id":"ba14f11e"},{"cell_type":"code","source":"#include <iostream>\n#include <list>       // mod #1\n\nint main() {\n    using Container = std::list< int >;    // mod #2\n    Container box;\n\n    const int n = 5;\n    \n    // fill the box with odd numbers\n    for( int k = 0; k < n; k++ ) {\n        box.push_back( 2*k+1 );\n    }\n    \n    // use an iterator-based loop\n    int k = 0;\n    for( Container::iterator it = box.begin(); it != box.end(); it++ ) {\n        std::cout << \"box(\" << k << \") = \" << *it << std::endl;\n        k++;\n    }\n}","metadata":{"trusted":true},"execution_count":23,"outputs":[],"id":"9f8daf26"},{"cell_type":"code","source":"main();","metadata":{"trusted":true},"execution_count":24,"outputs":[{"name":"stdout","text":"box(0) = 1\nbox(1) = 3\nbox(2) = 5\nbox(3) = 7\nbox(4) = 9\n","output_type":"stream"}],"id":"53211059"},{"cell_type":"markdown","source":"#### 'Reverse mode' \nUp above we have used a forward iterator. However, we can also ask for a reverse iterator to go through the container from end to start (if the container supports it).","metadata":{},"id":"44f9f4e2"},{"cell_type":"code","source":"#include <iostream>\n\nstd::vector< short > vec;\n\nvec.push_back( 1 );\nvec.push_back( 2 );\nvec.push_back( 3 );\n\nfor( std::vector< short >::reverse_iterator it = vec.rbegin(); it != vec.rend(); it++ ) {\n    std::cout << *it << std::endl;\n}","metadata":{"trusted":true},"execution_count":25,"outputs":[{"name":"stdout","text":"3\n2\n1\n","output_type":"stream"}],"id":"158950d1"},{"cell_type":"markdown","source":"#### 'Const mode'\nWhen we do not intend to change the element in the loop body, we can also employ a ```const_iterator```.","metadata":{},"id":"96bed53d"},{"cell_type":"code","source":"#include <iostream>\nstd::list< double > lc;\nusing cIter = std::list< double >::const_iterator;\nlc.push_back( 4.0 );\nlc.push_back( 5.0 );\nlc.push_back( 6.0 );\nfor( cIter it = lc.begin(); it != lc.end(); it++ ) {\n  std::cout << *it << std::endl;\n}","metadata":{"trusted":true},"execution_count":26,"outputs":[{"name":"stdout","text":"4\n5\n6\n","output_type":"stream"}],"id":"44a9bf28"},{"cell_type":"markdown","source":"#### 'Free Functions'\nC++ introduced free-function forms of ```begin()``` and ```end()``` which we can use alternatively to the member functions above:","metadata":{},"id":"da22b078"},{"cell_type":"code","source":"for( cIter it = begin( lc ), e = end( lc ); it != e; it++ ) {\n  std::cout << *it << std::endl;\n}","metadata":{"trusted":true},"execution_count":27,"outputs":[{"name":"stdout","text":"4\n5\n6\n","output_type":"stream"}],"id":"ceb3de09"},{"cell_type":"markdown","source":"Why did we introduce ```e```? Is it necessary?","metadata":{},"id":"14632b71"},{"cell_type":"markdown","source":"### Range-based access\nC++ nowadays also supports an even simpler way to encode a loop over a container:","metadata":{},"id":"c731df99"},{"cell_type":"code","source":"#include <iostream>\n#include <list>\n\nint main() {\n    using Container = std::list< int >;\n    Container box;\n\n    const int n = 5;\n    \n    // fill the box with odd numbers\n    for( int k = 0; k < n; k++ ) {\n        box.push_back( 2*k+1 );\n    }\n    \n    // change that to even, using a range-based loop\n    for( int& entry: box ) {\n        entry -= 1;\n    }\n    \n    // print using a range-based loop (in this case we can do w/o a ref)\n    int k = 0;\n    for( int value: box ) {\n        std::cout << \"box(\" << k << \") = \" << value << std::endl;\n        k++;\n    }\n}","metadata":{"trusted":true},"execution_count":28,"outputs":[],"id":"2497a826"},{"cell_type":"code","source":"main();","metadata":{"trusted":true},"execution_count":29,"outputs":[{"name":"stdout","text":"box(0) = 0\nbox(1) = 2\nbox(2) = 4\nbox(3) = 6\nbox(4) = 8\n","output_type":"stream"}],"id":"a8918a0e"},{"cell_type":"markdown","source":"Note that the last loop version involved a copy operation!","metadata":{},"id":"f4d8fd5e"},{"cell_type":"code","source":"std::cout << \"-> Put objects into container\" << std::endl;\nstd::vector< Dummy > vec;\nvec.reserve(2);\nvec.emplace_back( \"one\" );\nvec.emplace_back( \"two\" );\n\nstd::cout << \"\\n-> Run a loop\" << std::endl;\nfor( Dummy x: vec ) {\n    std::cout << \"* \" << x.getName() << std::endl;\n}","metadata":{"trusted":true},"execution_count":30,"outputs":[{"name":"stdout","text":"-> Put objects into container\nObject 'one' was created\nObject 'two' was created\n\n-> Run a loop\nObject 'one_copy' was created\n* one_copy\nObject 'one_copy' was destroyed\nObject 'two_copy' was created\n* two_copy\nObject 'two_copy' was destroyed\n","output_type":"stream"}],"id":"d1c64b51"},{"cell_type":"markdown","source":"Now let's try the same, but use a reference","metadata":{},"id":"eaf4c60a-ce19-410a-a81e-163d292de58d"},{"cell_type":"code","source":"std::cout << \"-> Put objects into container\" << std::endl;\nstd::vector< Dummy > vec;\nvec.reserve(2);\nvec.emplace_back( \"one\" );\nvec.emplace_back( \"two\" );\n\nstd::cout << \"\\n-> Run a loop\" << std::endl;\nfor( Dummy& x: vec ) {\n    std::cout << \"* \" << x.getName() << std::endl;\n}","metadata":{"trusted":true},"execution_count":31,"outputs":[{"name":"stdout","text":"-> Put objects into container\nObject 'one' was created\nObject 'two' was created\n\n-> Run a loop\n* one\n* two\n","output_type":"stream"}],"id":"28b2e3fb"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"c4afab4e-a800-4176-a26f-15d11ffd3964"}]}
\ No newline at end of file
+{"metadata":{"orig_nbformat":4,"kernelspec":{"display_name":"C++14","language":"C++14","name":"xcpp14"},"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"14"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Standard Template Library (STL)\n","metadata":{},"id":"0921615f"},{"cell_type":"markdown","source":"#### Executive Summary\n* Since its inclusion in 1994 the STL forms an integral part of the **Standard C++ Library**.\n* The STL is composed of three major components:\n    1. Definition of several **container types** for data storage\n    1. Access functions for those containers, especially so called **iterators**\n    1. **Algorithms** for working with the containers.\n* The following table lists the header files to include for different aspects of the STL\n    \n| STL containers      | Iterators, Functors, Algorithms |\n| :------------------ | :------------------------------ |\n| **```<deque>```**   | **```<iterator>```**            |\n| **```<list>```**    | **```<algorithm>```**           |\n| **```<map>```**     | **```<functional>```**          |\n| **```<queue>```**   |                                 |\n| **```<set>```**     |                                 |\n| **```<stack>```**   |                                 |\n| **```<vector>```**  |                                 |\n\n* As its name suggests the STL makes heavy use of *generic programming* based on **templates**.","metadata":{},"id":"eacfe313"},{"cell_type":"markdown","source":"#### Vector\n* Probably the STL container most often used is **```std::vector```**.\n* It provides an array data-structure that\n  1. can grow and shrink dynamically at runtime\n  2. provides classical index-based access\n  3. allows dynamic bounds checking","metadata":{},"id":"e3d3641e"},{"cell_type":"code","source":"#include <vector>\n#include <iostream>\n#include <iomanip>      // required for setw() below\n\nint main() {\n\n    const int n = 5;\n    \n    std::vector< int > vec;     // need to tell compiler what we will\n                                // store inside vec: < int > = template argument\n    \n    for( int k = 1; k <= n; ++k ) {\n        vec.push_back( k*k );   // append new entries at the end\n    }\n\n    for( int k = 1; k <= n; ++k ) {\n        std::cout << \"Square of \" << k << \" is \" << std::setw(2)\n                  << vec[k-1] << std::endl;    // this container allows standard 0-based subscript access\n    }\n}","metadata":{},"execution_count":1,"outputs":[],"id":"213d8360"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":2,"outputs":[{"name":"stdout","text":"Square of 1 is  1\nSquare of 2 is  4\nSquare of 3 is  9\nSquare of 4 is 16\nSquare of 5 is 25\n","output_type":"stream"}],"id":"bd290a8a"},{"cell_type":"markdown","source":"Of course we can also store data of other types in a vector.","metadata":{},"id":"aa2f2d9d"},{"cell_type":"code","source":"#include <string>\n\nint main() {\n\n    std::vector< double > dVec;\n    dVec.push_back( 22.0 );\n    dVec.push_back( -1.0 );\n    \n    std::vector< std::string > sVec;\n    sVec.push_back( \"Helena\");\n    sVec.push_back( \"Odysseus\" );\n}","metadata":{},"execution_count":3,"outputs":[],"id":"0bb0723b"},{"cell_type":"markdown","source":"So let's try an example with a **home-made class**. We make the con/destructor/s of our class a little talkative, to better understand what's happening.","metadata":{},"id":"aca19e34"},{"cell_type":"code","source":"class Dummy {\n\npublic:\n\n    Dummy() = delete;\n \n    Dummy( const std::string& name ) : name_(name) {\n        std::cout << \"Object '\" << name_ << \"' was created\" << std::endl;\n    }\n    \n    Dummy( const Dummy& other ) {\n        name_ = other.getName() + \"_copy\";\n        std::cout << \"Object '\" << name_ << \"' was created\" << std::endl;\n    }\n\n    ~Dummy() {\n        std::cout << \"Object '\" << name_ << \"' was destroyed\" << std::endl;\n    }\n\n    const std::string& getName() const {\n        return name_;\n    }\n\nprivate:\n    \n  std::string name_;\n\n};","metadata":{},"execution_count":4,"outputs":[],"id":"e5b2a349"},{"cell_type":"code","source":"int main() {\n\n  std::cout << \"-> Creating single objects\" << std::endl;\n  Dummy obj1( \"one\" );\n  Dummy obj2( \"two\" );\n\n  std::cout << \"\\n-> Putting objects in container\" << std::endl;\n  std::vector< Dummy > vec;\n  vec.push_back( obj1 );\n  vec.push_back( obj2 );\n\n  std::cout << \"\\n-> Auto clean-up at program end\" << std::endl;\n}","metadata":{},"execution_count":5,"outputs":[],"id":"bd331ee4"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":6,"outputs":[{"name":"stdout","text":"-> Creating single objects\nObject 'one' was created\nObject 'two' was created\n\n-> Putting objects in container\nObject 'one_copy' was created\nObject 'two_copy' was created\nObject 'one_copy_copy' was created\nObject 'one_copy' was destroyed\n\n-> Auto clean-up at program end\nObject 'one_copy_copy' was destroyed\nObject 'two_copy' was destroyed\nObject 'two' was destroyed\nObject 'one' was destroyed\n","output_type":"stream"}],"id":"a04654d2"},{"cell_type":"markdown","source":"**Observations:**\n1. Apparently ```std::vector``` stores **copies** of the objects we append.\n1. But why is a second copy of **one**, i.e. **one_copy_copy** created?","metadata":{},"id":"e9db8ff6"},{"cell_type":"code","source":" // Suggestions anyone?\n\n\n","metadata":{},"execution_count":7,"outputs":[],"id":"5d4ba36f"},{"cell_type":"markdown","source":"This has to do with the fact that an ```std::vector``` can dynamically grow and shrink. Consequently it has two different important properties\n\n* size = number of elements currently stored inside the vector\n* capacity = currently available slots to store entries inside the vector\n\nThe capacity of the vector is managed dynamically. We can see this in the following experiment:","metadata":{},"id":"e36f2bb9"},{"cell_type":"code","source":"#include <vector>\n#include <iostream>\n#include <iomanip>\n\nint main() {\n\n  std::vector< int > vec;\n\n  std::cout << \"vec: length = \" << std::setw(2) << vec.size()\n            << \", capacity = \" << std::setw(2) << vec.capacity()\n            << std::endl;\n\n  for( int k = 1; k <= 33; k++ ) {\n    vec.push_back( k );\n    std::cout << \"vec: length = \" << std::setw(2) << vec.size()\n              << \", capacity = \" << std::setw(2) << vec.capacity()\n              << std::endl;\n  }\n}\n\nmain();","metadata":{},"execution_count":8,"outputs":[{"name":"stdout","text":"vec: length =  0, capacity =  0\nvec: length =  1, capacity =  1\nvec: length =  2, capacity =  2\nvec: length =  3, capacity =  4\nvec: length =  4, capacity =  4\nvec: length =  5, capacity =  8\nvec: length =  6, capacity =  8\nvec: length =  7, capacity =  8\nvec: length =  8, capacity =  8\nvec: length =  9, capacity = 16\nvec: length = 10, capacity = 16\nvec: length = 11, capacity = 16\nvec: length = 12, capacity = 16\nvec: length = 13, capacity = 16\nvec: length = 14, capacity = 16\nvec: length = 15, capacity = 16\nvec: length = 16, capacity = 16\nvec: length = 17, capacity = 32\nvec: length = 18, capacity = 32\nvec: length = 19, capacity = 32\nvec: length = 20, capacity = 32\nvec: length = 21, capacity = 32\nvec: length = 22, capacity = 32\nvec: length = 23, capacity = 32\nvec: length = 24, capacity = 32\nvec: length = 25, capacity = 32\nvec: length = 26, capacity = 32\nvec: length = 27, capacity = 32\nvec: length = 28, capacity = 32\nvec: length = 29, capacity = 32\nvec: length = 30, capacity = 32\nvec: length = 31, capacity = 32\nvec: length = 32, capacity = 32\nvec: length = 33, capacity = 64\n","output_type":"stream"}],"id":"261a8327"},{"cell_type":"markdown","source":"**What happens, when the capacity is exhausted and must be enlarged?**\n\n* ```std::vector<T>``` is basically a wrapper class around a dynamic array ``T* data``  \n  *(we can actually access this via the data() member function, e.g. to interface with C or Fortran)* \n* When the capacity is exhausted, but we want to add another entry three steps need to happen\n  1. Dynamical allocation of a new internal data-array with a new larger capacity.\n  1. **Copying** of all exisisting entries from the old data-array to the new one.  \n     *[That's what gave us \"Object 'one_copy_copy' was created\" above]*\n  1. Destruction of objects in old data-array and its de-allocation.  \n     *[That's what gave us \"Object 'one_copy' was destroyed\" above]*\n* Afterwards the new entry can be appended.\n\n**Performance issue**  \nThe memory management, but especially the copying constitutes a significant overhead, especially for large arrays.\nWhen we already know what the maximal size of our vector will be, we can avoid this, by **reserving** a large enough\ncapacity.","metadata":{},"id":"b9d88e30"},{"cell_type":"code","source":"#include <vector>\n#include <iostream>\n#include <iomanip>\n\nint main() {\n\n  std::vector< int > vec;\n\n  // make the vector allocate a data-array of sufficient length\n  vec.reserve( 100 );\n    \n  std::cout << \"vec: length = \" << std::setw(2) << vec.size()\n            << \", capacity = \" << std::setw(2) << vec.capacity()\n            << std::endl;\n\n  for( int k = 1; k <= 33; k++ ) {\n    vec.push_back( k );\n    std::cout << \"vec: length = \" << std::setw(2) << vec.size()\n              << \", capacity = \" << std::setw(2) << vec.capacity()\n              << std::endl;\n  }\n}\n\nmain();","metadata":{},"execution_count":9,"outputs":[{"name":"stdout","text":"vec: length =  0, capacity = 100\nvec: length =  1, capacity = 100\nvec: length =  2, capacity = 100\nvec: length =  3, capacity = 100\nvec: length =  4, capacity = 100\nvec: length =  5, capacity = 100\nvec: length =  6, capacity = 100\nvec: length =  7, capacity = 100\nvec: length =  8, capacity = 100\nvec: length =  9, capacity = 100\nvec: length = 10, capacity = 100\nvec: length = 11, capacity = 100\nvec: length = 12, capacity = 100\nvec: length = 13, capacity = 100\nvec: length = 14, capacity = 100\nvec: length = 15, capacity = 100\nvec: length = 16, capacity = 100\nvec: length = 17, capacity = 100\nvec: length = 18, capacity = 100\nvec: length = 19, capacity = 100\nvec: length = 20, capacity = 100\nvec: length = 21, capacity = 100\nvec: length = 22, capacity = 100\nvec: length = 23, capacity = 100\nvec: length = 24, capacity = 100\nvec: length = 25, capacity = 100\nvec: length = 26, capacity = 100\nvec: length = 27, capacity = 100\nvec: length = 28, capacity = 100\nvec: length = 29, capacity = 100\nvec: length = 30, capacity = 100\nvec: length = 31, capacity = 100\nvec: length = 32, capacity = 100\nvec: length = 33, capacity = 100\n","output_type":"stream"}],"id":"664eec8e"},{"cell_type":"markdown","source":"***\nThis will also work for our original example with the class Dummy:","metadata":{},"id":"2352d31e"},{"cell_type":"code","source":"int main() {\n\n  std::cout << \"-> Creating single objects\" << std::endl;\n  Dummy obj1( \"one\" );\n  Dummy obj2( \"two\" );\n\n  std::cout << \"\\n-> Putting objects in container\" << std::endl;\n  std::vector< Dummy > vec;\n  vec.reserve( 2 );\n  vec.push_back( obj1 );\n  vec.push_back( obj2 );\n\n  std::cout << \"\\n-> Auto clean-up at program end\" << std::endl;\n}\n\nmain();","metadata":{},"execution_count":10,"outputs":[{"name":"stdout","text":"-> Creating single objects\nObject 'one' was created\nObject 'two' was created\n\n-> Putting objects in container\nObject 'one_copy' was created\nObject 'two_copy' was created\n\n-> Auto clean-up at program end\nObject 'one_copy' was destroyed\nObject 'two_copy' was destroyed\nObject 'two' was destroyed\nObject 'one' was destroyed\n","output_type":"stream"}],"id":"a5dffa46-65b8-477b-bbd8-a0ad0801d1fb"},{"cell_type":"markdown","source":"**But:** Wouldn't it be nicer/easier to have the reservation be part of the instantiation? Let's check:","metadata":{},"id":"41bf0d83"},{"cell_type":"code","source":"std::vector< Dummy > vec( 2 );","metadata":{},"execution_count":11,"outputs":[{"name":"stderr","text":"In file included from input_line_5:1:\nIn file included from /srv/conda/envs/notebook/include/xeus/xinterpreter.hpp:15:\nIn file included from /srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/vector:65:\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_construct.h:75:38: error: call to deleted constructor of '__cling_N55::Dummy'\n    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }\n                                     ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_uninitialized.h:545:8: note: in instantiation of function template specialization 'std::_Construct<__cling_N55::Dummy>' requested here\n                std::_Construct(std::__addressof(*__cur));\n                     ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_uninitialized.h:601:2: note: in instantiation of function template specialization 'std::__uninitialized_default_n_1<false>::__uninit_default_n<__cling_N55::Dummy *, unsigned long>' requested here\n        __uninit_default_n(__first, __n);\n        ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_uninitialized.h:663:19: note: in instantiation of function template specialization 'std::__uninitialized_default_n<__cling_N55::Dummy *, unsigned long>' requested here\n    { return std::__uninitialized_default_n(__first, __n); }\n                  ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_vector.h:1603:9: note: in instantiation of function template specialization 'std::__uninitialized_default_n_a<__cling_N55::Dummy *, unsigned long, __cling_N55::Dummy>' requested here\n          std::__uninitialized_default_n_a(this->_M_impl._M_start, __n,\n               ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_vector.h:509:9: note: in instantiation of member function 'std::vector<__cling_N55::Dummy, std::allocator<__cling_N55::Dummy> >::_M_default_initialize' requested here\n      { _M_default_initialize(__n); }\n        ^\ninput_line_21:2:23: note: in instantiation of member function 'std::vector<__cling_N55::Dummy, std::allocator<__cling_N55::Dummy> >::vector' requested here\n std::vector< Dummy > vec( 2 );\n                      ^\ninput_line_12:3:5: note: 'Dummy' has been explicitly marked deleted here\n    Dummy() = delete;\n    ^\n","output_type":"stream"},{"ename":"Interpreter Error","evalue":"","traceback":["Interpreter Error: "],"output_type":"error"}],"id":"351ace20"},{"cell_type":"markdown","source":"Problem is that this version of the ```std::vector<T>``` constructor will try to fill/initialise the vector with objects of type ```T``` using their default constructor.","metadata":{},"id":"ec2de34b"},{"cell_type":"markdown","source":"Let us take a look at other ways to construct an std::vector:","metadata":{},"id":"d1cdc134"},{"cell_type":"code","source":"int main() {\n\n  std::cout << \"-> Creating single objects\" << std::endl;\n  Dummy obj1( \"one\" );\n  Dummy obj2( \"two\" );\n\n  std::cout << \"\\n-> Putting objects in container\" << std::endl;\n  std::vector< Dummy > vec;\n\n  // Will fail, as we have no default constructor\n  // std::vector< Dummy > vec2( 2 );\n\n  vec.push_back( obj1 );\n  // std::vector< Dummy > vec2( vec );           // -> construction by copying\n  // std::vector< Dummy > vec2 = vec;            // -> construction by copying (but move semantics?)\n  // std::vector< Dummy > vec2( 5, obj1 );       // -> vector with 5 copies of object one\n  // std::vector< Dummy > vec2( {obj1, obj2} ); // -> using initialiser list (but is a temporary generated here?)\n  // std::vector< Dummy > vec2{obj1, obj2};     // -> clearer version with initialiser list\n\n  vec.emplace_back( \"three\" );     // <- what will this do?\n\n  std::cout << \"\\n-> Auto clean-up at program end\" << std::endl;\n\n}","metadata":{},"execution_count":12,"outputs":[],"id":"b3dfe338"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":13,"outputs":[{"name":"stdout","text":"-> Creating single objects\nObject 'one' was created\nObject 'two' was created\n\n-> Putting objects in container\nObject 'one_copy' was created\nObject 'three' was created\nObject 'one_copy_copy' was created\nObject 'one_copy' was destroyed\n\n-> Auto clean-up at program end\nObject 'one_copy_copy' was destroyed\nObject 'three' was destroyed\nObject 'two' was destroyed\nObject 'one' was destroyed\n","output_type":"stream"}],"id":"ad928c13"},{"cell_type":"markdown","source":"* ```emplace()``` allows to **generate** a new entry at a specified place inside the vector, passing the given arguments to the entry's constructor.\n* ```emplace_back()``` does the same, but **appends at the end**.","metadata":{},"id":"eebc1269"},{"cell_type":"markdown","source":"***\nWe have already seen that we have read/write access to entries of our vector with the **```[ ]```** operator. Another alternative is the **```at()```** method.\n\nThe difference is that **```at()```** will throw an exception, when we commit an out-of-bounds access error.","metadata":{},"id":"dc64b2c9"},{"cell_type":"code","source":"%%file exception.cpp\n\n#include <iostream>\n#include <vector>\n\nint main() {\n\n  std::vector< double > vec( 10, 2.0 );\n\n#ifdef OUT_OF_BOUNDS\n  for( int k = 0; k <= 10; ++k ) {\n    std::cout << \"vec[ \" << k << \" ] = \" << vec[k] << std::endl;\n  }\n  std::cout << \"Too bad we reached this line :-(\" << std::endl;\n#else\n  for( int k = 0; k <= 10; ++k ) {\n    std::cout << \"vec[ \" << k << \" ] = \" << vec.at( k ) << std::endl;\n  }\n#endif\n\n}","metadata":{},"execution_count":14,"outputs":[{"name":"stdout","text":"Overwriting exception.cpp\n","output_type":"stream"}],"id":"7e537a6b"},{"cell_type":"code","source":"!g++ -Wall -DOUT_OF_BOUNDS exception.cpp","metadata":{},"execution_count":15,"outputs":[],"id":"0513b187"},{"cell_type":"code","source":"!./a.out","metadata":{},"execution_count":16,"outputs":[{"name":"stdout","text":"vec[ 0 ] = 2\nvec[ 1 ] = 2\nvec[ 2 ] = 2\nvec[ 3 ] = 2\nvec[ 4 ] = 2\nvec[ 5 ] = 2\nvec[ 6 ] = 2\nvec[ 7 ] = 2\nvec[ 8 ] = 2\nvec[ 9 ] = 2\nvec[ 10 ] = 0\nToo bad we reached this line :-(\n","output_type":"stream"}],"id":"af593caa"},{"cell_type":"code","source":"!g++ -Wall exception.cpp","metadata":{},"execution_count":17,"outputs":[],"id":"131dd4f5"},{"cell_type":"code","source":"!./a.out","metadata":{},"execution_count":18,"outputs":[{"name":"stdout","text":"vec[ 0 ] = 2\nvec[ 1 ] = 2\nvec[ 2 ] = 2\nvec[ 3 ] = 2\nvec[ 4 ] = 2\nvec[ 5 ] = 2\nvec[ 6 ] = 2\nvec[ 7 ] = 2\nvec[ 8 ] = 2\nvec[ 9 ] = 2\nterminate called after throwing an instance of 'std::out_of_range'\n  what():  vector::_M_range_check: __n (which is 10) >= this->size() (which is 10)\nAborted\n","output_type":"stream"}],"id":"e6ce23bd"},{"cell_type":"markdown","source":"### Iterators and range-based access","metadata":{},"id":"8e0987ec"},{"cell_type":"markdown","source":"Our **std::vector** belongs to the class of sequence containers, i.e. the ordering of entries $e_k$ is important\n\n$$ \\Big( e_1, e_2, \\ldots, e_n \\Big)$$\n\nand the same value $v$ can show up at multiple positions ($e_i = v = e_j$ with $i\\neq j$).\n\nAnother type of sequence container is the **linked list**. The **std::list** implements a doubly-linked list, i.e. each entry/element is composed of three parts\n\n$$ \\text{entry} = \\Big( \\text{payload}, \\text{prev}, \\text{next} \\Big)$$\n\nwhere\n * payload = value of the entry\n * prev = information on where to find the predecessor of this entry\n * next = information on where to find the successor of this entry\n \nContrary to a vector, a list does not offer index-based access to its elements. Instead we can do list-traversal.\n\nNow consider the following piece of code:","metadata":{},"id":"80b33721"},{"cell_type":"code","source":"#include <iostream>\n#include <vector>\n\nint main() {\n    using Container = std::vector< int >;\n    Container box;\n\n    const int n = 5;\n    \n    // fill the box with odd numbers\n    for( int k = 0; k < n; k++ ) {\n        box.push_back( 2*k+1 );\n    }\n    \n    // print its contents\n    for( int k = 0; k < n; k++ ) {\n        std::cout << \"box(\" << k << \") = \" << box[k] << std::endl;\n    }\n}\n\nmain();","metadata":{},"execution_count":19,"outputs":[{"name":"stdout","text":"box(0) = 1\nbox(1) = 3\nbox(2) = 5\nbox(3) = 7\nbox(4) = 9\n","output_type":"stream"}],"id":"eee3b34a"},{"cell_type":"markdown","source":"For what we are doing in the program the type of underlying container does not really matter.  \nHow about replacing vector with list for a change?","metadata":{},"id":"33c83653"},{"cell_type":"code","source":"#include <iostream>\n#include <list>      // okay, need to adapt the include\n\nint main() {\n    using Container = std::list< int >;  // need to alter this statement\n    Container box;\n\n    const int n = 5;\n    \n    // fill the box with odd numbers\n    for( int k = 0; k < n; k++ ) {\n        box.push_back( 2*k+1 );\n    }\n    \n    // print its contents\n    for( int k = 0; k < n; k++ ) {\n        std::cout << \"box(\" << k << \") = \" << box[k] << std::endl;\n    }\n}\n\nmain();","metadata":{},"execution_count":20,"outputs":[{"name":"stderr","text":"input_line_27:13:50: error: type 'Container' (aka 'list<int>') does not provide a subscript operator\n        std::cout << \"box(\" << k << \") = \" << box[k] << std::endl;\n                                              ~~~^~\n","output_type":"stream"},{"ename":"Interpreter Error","evalue":"","traceback":["Interpreter Error: "],"output_type":"error"}],"id":"d2806a4f"},{"cell_type":"markdown","source":"***\nNow that is, where **iterators** come into play. They abstract away the details of the underlying container.","metadata":{},"id":"40d19e9a"},{"cell_type":"code","source":"#include <iostream>\n#include <vector>\n\nint main() {\n    using Container = std::vector< int >;\n    Container box;\n\n    const int n = 5;\n    \n    // fill the box with odd numbers\n    for( int k = 0; k < n; k++ ) {\n        box.push_back( 2*k+1 );\n    }\n    \n    // use an iterator-based loop\n    int k = 0;\n    for( Container::iterator it = box.begin(); it != box.end(); it++ ) {\n        std::cout << \"box(\" << k << \") = \" << *it << std::endl;\n        k++;\n    }\n}","metadata":{},"execution_count":21,"outputs":[],"id":"4a6ba619"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":22,"outputs":[{"name":"stdout","text":"box(0) = 1\nbox(1) = 3\nbox(2) = 5\nbox(3) = 7\nbox(4) = 9\n","output_type":"stream"}],"id":"907c51a5"},{"cell_type":"markdown","source":"**Remarks:**\n* The iterator is no pointer, but 'feels' like one, as it overloads the dereferencing operator ```*```.\n* ```box.begin()``` points to the first entry in the container.\n* ```box.end()``` points **after** the last entry in the container. Thus, dereferencing it would be illegal.\n* Do not test for ```it < box.end()```, but for (in)equality only.","metadata":{},"id":"5b9766a5"},{"cell_type":"markdown","source":"Since our loop is using an iterator now, we can with two small modifications switch from a **std::vector** to a **std::list**.","metadata":{},"id":"ba14f11e"},{"cell_type":"code","source":"#include <iostream>\n#include <list>       // mod #1\n\nint main() {\n    using Container = std::list< int >;    // mod #2\n    Container box;\n\n    const int n = 5;\n    \n    // fill the box with odd numbers\n    for( int k = 0; k < n; k++ ) {\n        box.push_back( 2*k+1 );\n    }\n    \n    // use an iterator-based loop\n    int k = 0;\n    for( Container::iterator it = box.begin(); it != box.end(); it++ ) {\n        std::cout << \"box(\" << k << \") = \" << *it << std::endl;\n        k++;\n    }\n}","metadata":{},"execution_count":23,"outputs":[],"id":"9f8daf26"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":24,"outputs":[{"name":"stdout","text":"box(0) = 1\nbox(1) = 3\nbox(2) = 5\nbox(3) = 7\nbox(4) = 9\n","output_type":"stream"}],"id":"53211059"},{"cell_type":"markdown","source":"#### 'Reverse mode' \nUp above we have used a forward iterator. However, we can also ask for a reverse iterator to go through the container from end to start (if the container supports it).","metadata":{},"id":"44f9f4e2"},{"cell_type":"code","source":"#include <iostream>\n\nstd::vector< short > vec;\n\nvec.push_back( 1 );\nvec.push_back( 2 );\nvec.push_back( 3 );\n\nfor( std::vector< short >::reverse_iterator it = vec.rbegin(); it != vec.rend(); it++ ) {\n    std::cout << *it << std::endl;\n}","metadata":{},"execution_count":25,"outputs":[{"name":"stdout","text":"3\n2\n1\n","output_type":"stream"}],"id":"158950d1"},{"cell_type":"markdown","source":"#### 'Const mode'\nWhen we do not intend to change the element in the loop body, we can also employ a ```const_iterator```.","metadata":{},"id":"96bed53d"},{"cell_type":"code","source":"#include <iostream>\nstd::list< double > lc;\nusing cIter = std::list< double >::const_iterator;\nlc.push_back( 4.0 );\nlc.push_back( 5.0 );\nlc.push_back( 6.0 );\nfor( cIter it = lc.begin(); it != lc.end(); it++ ) {\n  std::cout << *it << std::endl;\n}","metadata":{},"execution_count":26,"outputs":[{"name":"stdout","text":"4\n5\n6\n","output_type":"stream"}],"id":"44a9bf28"},{"cell_type":"markdown","source":"#### 'Free Functions'\nC++ introduced free-function forms of ```begin()``` and ```end()``` which we can use alternatively to the member functions above:","metadata":{},"id":"da22b078"},{"cell_type":"code","source":"for( cIter it = begin( lc ), e = end( lc ); it != e; it++ ) {\n  std::cout << *it << std::endl;\n}","metadata":{},"execution_count":27,"outputs":[{"name":"stdout","text":"4\n5\n6\n","output_type":"stream"}],"id":"ceb3de09"},{"cell_type":"markdown","source":"Why did we introduce ```e```? Is it necessary?","metadata":{},"id":"14632b71"},{"cell_type":"markdown","source":"### Range-based access\nC++ nowadays also supports an even simpler way to encode a loop over a container:","metadata":{},"id":"c731df99"},{"cell_type":"code","source":"#include <iostream>\n#include <list>\n\nint main() {\n    using Container = std::list< int >;\n    Container box;\n\n    const int n = 5;\n    \n    // fill the box with odd numbers\n    for( int k = 0; k < n; k++ ) {\n        box.push_back( 2*k+1 );\n    }\n    \n    // change that to even, using a range-based loop\n    for( int& entry: box ) {\n        entry -= 1;\n    }\n    \n    // print using a range-based loop (in this case we can do w/o a ref)\n    int k = 0;\n    for( int value: box ) {\n        std::cout << \"box(\" << k << \") = \" << value << std::endl;\n        k++;\n    }\n}","metadata":{},"execution_count":28,"outputs":[],"id":"2497a826"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":29,"outputs":[{"name":"stdout","text":"box(0) = 0\nbox(1) = 2\nbox(2) = 4\nbox(3) = 6\nbox(4) = 8\n","output_type":"stream"}],"id":"a8918a0e"},{"cell_type":"markdown","source":"Note that the last loop version involved a copy operation!","metadata":{},"id":"f4d8fd5e"},{"cell_type":"code","source":"std::cout << \"-> Put objects into container\" << std::endl;\nstd::vector< Dummy > vec;\nvec.reserve(2);\nvec.emplace_back( \"one\" );\nvec.emplace_back( \"two\" );\n\nstd::cout << \"\\n-> Run a loop\" << std::endl;\nfor( Dummy x: vec ) {\n    std::cout << \"* \" << x.getName() << std::endl;\n}","metadata":{},"execution_count":30,"outputs":[{"name":"stdout","text":"-> Put objects into container\nObject 'one' was created\nObject 'two' was created\n\n-> Run a loop\nObject 'one_copy' was created\n* one_copy\nObject 'one_copy' was destroyed\nObject 'two_copy' was created\n* two_copy\nObject 'two_copy' was destroyed\n","output_type":"stream"}],"id":"d1c64b51"},{"cell_type":"markdown","source":"Now let's try the same, but use a reference","metadata":{},"id":"eaf4c60a-ce19-410a-a81e-163d292de58d"},{"cell_type":"code","source":"std::cout << \"-> Put objects into container\" << std::endl;\nstd::vector< Dummy > vec;\nvec.reserve(2);\nvec.emplace_back( \"one\" );\nvec.emplace_back( \"two\" );\n\nstd::cout << \"\\n-> Run a loop\" << std::endl;\nfor( Dummy& x: vec ) {\n    std::cout << \"* \" << x.getName() << std::endl;\n}","metadata":{},"execution_count":31,"outputs":[{"name":"stdout","text":"-> Put objects into container\nObject 'one' was created\nObject 'two' was created\n\n-> Run a loop\n* one\n* two\n","output_type":"stream"}],"id":"28b2e3fb"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"c4afab4e-a800-4176-a26f-15d11ffd3964"}]}
\ No newline at end of file
diff --git a/notebooks/07_STL_Set+Map+Algorithms.ipynb b/notebooks/07_STL_Set+Map+Algorithms.ipynb
index a28479722953eb713c123074ea1f32008e50ad60..1de53db632957153496c8406c757ac5c773284cd 100644
--- a/notebooks/07_STL_Set+Map+Algorithms.ipynb
+++ b/notebooks/07_STL_Set+Map+Algorithms.ipynb
@@ -1,706 +1 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "bac1d62f",
-   "metadata": {},
-   "source": [
-    "# STL - Part 2 : More Containers & Algorithms"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "efdc5616",
-   "metadata": {},
-   "source": [
-    "#### Sets"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "c2848a05",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "#include <iostream>\n",
-    "#include <set>\n",
-    "\n",
-    "std::set< int > s = {1, 7, 4, 8, 2};   // create a set container and fill it\n",
-    "\n",
-    "s.insert(5);   // insert another element\n",
-    "s.insert(4);\n",
-    "\n",
-    "// check how often an element is inside the set\n",
-    "for ( int i = 0; i < 8; ++i ) {\n",
-    "    std::cout << i << \" appears \" << s.count( i ) << \" times\\n\";\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "44573775",
-   "metadata": {},
-   "source": [
-    "**Remarks:**\n",
-    "- Although we inserted **4** twice, it is only stored once in the set data structure. Just like a mathematical set a ```std::set``` can contain a specific object only once.\n",
-    "- Hence, ```count()``` will always return either 0 or 1.\n",
-    "- This shows that ```std::set``` is no sequence container.\n",
-    "- Instead it stores its elements in a **sorted** fashion. This guarantees logarithmic complexity for element access."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "a415d236",
-   "metadata": {},
-   "source": [
-    "Of course we can store various kinds of objects in a set ..."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "2213e98e",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "struct myPair {\n",
-    "  myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {};\n",
-    "  int fir_;\n",
-    "  int sec_;\n",
-    "};\n",
-    "\n",
-    "std::set< myPair > pairs; "
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "725387bc",
-   "metadata": {},
-   "source": [
-    "... but there is a caveat. Check what happens, when we try to insert an object of type ```myPair``` into the set:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "51fd5597",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "pairs.insert( myPair(1,2) );"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "2ee8f731",
-   "metadata": {},
-   "source": [
-    "**What went wrong?**\n",
-    "\n",
-    "As the set stores its elements in a **sorted** fashion it needs some way to **order** them.\n",
-    "By default that standard ```<``` relation is used.\n",
-    "\n",
-    "That does not work for our ```pair``` data type.\n",
-    "\n",
-    "We need to define our own comparison operator for the class.\n",
-    "\n",
-    "There are plenty ways of doing that. Below we will use a free function for it."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "id": "e8c29594",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Overwriting demo.cpp\n"
-     ]
-    }
-   ],
-   "source": [
-    "%%file demo.cpp\n",
-    "\n",
-    "#include <iostream>\n",
-    "#include <set>\n",
-    "\n",
-    "struct myPair {\n",
-    "  myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {};\n",
-    "  int fir_;\n",
-    "  int sec_;\n",
-    "};\n",
-    "\n",
-    "// our comparison function will return true, if the sum of the squares\n",
-    "// of the values in left is smaller than that in right\n",
-    "bool cmp( const myPair& left, const myPair& right ) {\n",
-    "  int val1 = left.fir_ * left.fir_ + left.sec_ * left.sec_;\n",
-    "  int val2 = right.fir_ * right.fir_ + right.sec_ * right.sec_;\n",
-    "  return val1 < val2;\n",
-    "}\n",
-    "\n",
-    "int main() {\n",
-    "    \n",
-    "    // create two objects\n",
-    "    myPair p1( 1, 2 );\n",
-    "    myPair p2( 3, 4 );\n",
-    "\n",
-    "    // compare them\n",
-    "    std::cout << \"p1 < p2 is \" << cmp( p1, p2 ) << std::endl;\n",
-    "    std::cout << \"p2 < p1 is \" << cmp( p2, p1 ) << std::endl;\n",
-    "\n",
-    "    // a free function in C/C++ is represented by the address of its machine code in memory;\n",
-    "    // thus, the type of argument is a function pointer;\n",
-    "    // we need to pass that info as a second template argument;\n",
-    "    // decltype returns the type of an expression\n",
-    "    std::set< myPair, decltype( cmp )* > pairs ( cmp );\n",
-    "\n",
-    "    // alternatively we could do it ourselves; note that the (*) is important!\n",
-    "    // but that way is not very C++-ish\n",
-    "    // std::set< myPair, bool (*)( const myPair&, const myPair& ) > pairs( cmp );\n",
-    "    \n",
-    "    // now insertion will work\n",
-    "    pairs.insert( p1 );\n",
-    "    pairs.insert( p2 );\n",
-    "\n",
-    "    std::cout << \"insertion successful\" << std::endl;\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "id": "0a0207dd",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!g++ demo.cpp"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "id": "68e1df4b",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "p1 < p2 is 1\n",
-      "p2 < p1 is 0\n",
-      "insertion successful\n"
-     ]
-    }
-   ],
-   "source": [
-    "!./a.out"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "f8b3109b",
-   "metadata": {},
-   "source": [
-    "**Note:**  \n",
-    "The STL offers a variant **```std::unordered_set```** that stores its elements in an unsorted fashion. However, that would not solve our problem. Instead of a **comparison** we then would need to implement a **hashing function**."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "59ab93ca",
-   "metadata": {},
-   "source": [
-    "***\n",
-    "The way we added the new element in the example, i.e. via\n",
-    "\n",
-    "```pairs.insert( myPair(1,2) );```\n",
-    "\n",
-    "again induced a copy operation. As with ```vector``` and ```list``` we could use ```emplace()``` instead.\n",
-    "\n",
-    "For demonstration purposes we make the constructor of ```myPair``` verbose."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "bdda6c33",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "%%file demo.cpp\n",
-    "\n",
-    "#include <iostream>\n",
-    "#include <set>\n",
-    "\n",
-    "struct myPair {\n",
-    "  myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {\n",
-    "      std::cout << \"(\" << fir_ << \",\" << sec_ << \") constructed\" << std::endl;\n",
-    "  };\n",
-    " \n",
-    "  int fir_;\n",
-    "  int sec_;\n",
-    "};\n",
-    "\n",
-    "// our comparison function: will return true, if the square of the values\n",
-    "// in left is smaller than that in right\n",
-    "bool cmp( const myPair& left, const myPair& right ) {\n",
-    "  int val1 = left.fir_ * left.fir_ + left.sec_ * left.sec_;\n",
-    "  int val2 = right.fir_ * right.fir_ + right.sec_ * right.sec_;\n",
-    "  return val1 < val2;\n",
-    "}\n",
-    "\n",
-    "int main() {\n",
-    "    \n",
-    "    std::set< myPair, decltype( cmp )* > pairs( cmp );\n",
-    "\n",
-    "    pairs.emplace( 1, 2 );\n",
-    "    pairs.emplace( 3, 4 );\n",
-    "    pairs.emplace( 1, 2 );\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "8099d1a4",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!g++ demo.cpp"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "5d491b27",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!./a.out"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "8e256275",
-   "metadata": {},
-   "source": [
-    "#### Associative Containers\n",
-    "\n",
-    "Quoting Wikipedia:\n",
-    "\n",
-    "> In computer science, an **associative array**, **map**, **symbol table**, or **dictionary** is an abstract data type composed of a collection of **(key, value) pairs**, such that each possible key appears at most once in the collection.\n",
-    ">\n",
-    "> Operations associated with this data type allow to:\n",
-    ">\n",
-    ">-    add a pair to the collection;\n",
-    ">-    remove a pair from the collection;\n",
-    ">-    modify an existing pair;\n",
-    ">-    lookup a value associated with a particular key.\n",
-    "\n",
-    "The STL also offers associative containers. An example is ```std::map```."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "fb3ef7bb",
-   "metadata": {},
-   "source": [
-    "Return to our example with the traffic light. Assume that we want to print the currect state of a specific traffic light. How to do that?\n",
-    "\n",
-    "Well, using an enumeration for the state we can use the classical switch-case-construct:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "59839aea",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "%%file ampel.cpp\n",
-    "\n",
-    "#include <iostream>\n",
-    "\n",
-    "typedef enum{ GREEN, YELLOW, RED, YELLOW_RED } trafficLight;\n",
-    "\n",
-    "void ampel( trafficLight tf ) {\n",
-    "\n",
-    "  switch( tf ) {\n",
-    "  case GREEN:\n",
-    "    std::cout << \"Lights are green\" << std::endl;\n",
-    "    break;\n",
-    "  case YELLOW:\n",
-    "    std::cout << \"Lights are yellow\" << std::endl;\n",
-    "    break;\n",
-    "  case RED:\n",
-    "    std::cout << \"Lights are red\" << std::endl;\n",
-    "    break;\n",
-    "  case YELLOW_RED:\n",
-    "    std::cout << \"Lights are yellow-red\" << std::endl;\n",
-    "    break;\n",
-    "  }\n",
-    "\n",
-    "}\n",
-    "\n",
-    "int main() {\n",
-    "  trafficLight tf = GREEN;\n",
-    "  ampel( tf );\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "76c8886d",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!g++ ampel.cpp"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "731e7c94",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!./a.out"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "08b68b2b",
-   "metadata": {},
-   "source": [
-    "Another (neater) way is to associate the state with a corresponding string"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "8fe484bf",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "%%file ampel.cpp\n",
-    "\n",
-    "#include <iostream>\n",
-    "#include <map>\n",
-    "\n",
-    "typedef enum{ GREEN, YELLOW, RED, YELLOW_RED } trafficLight;\n",
-    "\n",
-    "int main() {\n",
-    "\n",
-    "  std::map< trafficLight, std::string > tf2str =\n",
-    "    { { GREEN, \"green\" }, { YELLOW, \"yellow\" }, { RED, \"red\" },\n",
-    "      { YELLOW_RED, \"yellow-red\" } };\n",
-    "\n",
-    "  trafficLight tf = GREEN;\n",
-    "\n",
-    "  std::cout << \"Lights are \" << tf2str[ tf ] << std::endl;\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "74251bd8",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!g++ ampel.cpp"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "5b919ca3",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!./a.out"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b1718cdd",
-   "metadata": {},
-   "source": [
-    "Another example, demonstrating additional features of ```std::map``` (converted from Gottschling, *Forschung mit modernem C++*)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "52f631d4",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "%%file constants.cpp\n",
-    "\n",
-    "#include <iostream>\n",
-    "#include <string>\n",
-    "#include <map>\n",
-    "#include <cmath>\n",
-    "\n",
-    "int main() {\n",
-    "\n",
-    "  // create a map and fill it with some (key,value) pairs\n",
-    "  std::map< std::string, double > constants = { {\"e\", std::exp(1.0)},\n",
-    "      {\"pi\", 4.0*std::atan(1.0) }, {\"h\", 6.6e-34} };\n",
-    "\n",
-    "  // subscript operator is overloaded to allow access via a key\n",
-    "  std::cout << \"Value of Planck constant is \" << constants[ \"h\" ] << '\\n';\n",
-    "  constants[ \"c\" ] = 299792458;\n",
-    "\n",
-    "  // Hmmm, what happens here? No key \"k\" exists so far!\n",
-    "  std::cout << \"Value of Coulomb constant is \" << constants[ \"k\" ] << '\\n';\n",
-    "\n",
-    "  // find() allows to check for existance of a key; returns an iterator\n",
-    "  // to the pair, if it is found\n",
-    "  std::cout << \"Value of pi is \" << constants.find( \"pi\" )->second << '\\n';\n",
-    "\n",
-    "  // if not it returns end\n",
-    "  auto it_phi = constants.find( \"phi\" );\n",
-    "  if ( it_phi != constants.end() ) {\n",
-    "    std::cout << \"The golden ratio is \" << it_phi->second << '\\n';\n",
-    "  }\n",
-    "\n",
-    "  // can use at(), if we know the pair to exists, returns value for key\n",
-    "  // will throw an \"out_of_range\" exception, if we were wrong\n",
-    "  std::cout << \"Value of Euler constant is \" << constants.at( \"e\" ) << \"\\n\\n\";\n",
-    "\n",
-    "  // range-based loop\n",
-    "  for ( auto& c: constants ) {\n",
-    "    std::cout << \"Value of \" << c.first << \" is \" << c.second << '\\n';\n",
-    "  }\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "aaba61ba",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!g++ constants.cpp"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "3f9362c9",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!./a.out"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "a7e8490c",
-   "metadata": {},
-   "source": [
-    "**Note:**  \n",
-    "As with ```set``` there is also a variant of ```map``` that uses hashing instead of ordered storage and is called ```unordered_map```."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "2032e7f2",
-   "metadata": {},
-   "source": [
-    "#### Algorithm\n",
-    "\n",
-    "The STL supports also various algorithms that work on the entries in a container. The advantage again is that these save us implementation and will (potentially) work for various kinds of containers.\n",
-    "\n",
-    "We will briefly look at two examples."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "4c239bfa",
-   "metadata": {},
-   "source": [
-    "**Example 1:** Remove double entries from a sequence"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "aed454ce",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "%%file uniq.cpp\n",
-    "\n",
-    "#include <algorithm>\n",
-    "#include <vector>\n",
-    "#include <iostream>\n",
-    "\n",
-    "using Container = std::vector<int>;\n",
-    "\n",
-    "void print( const Container& box, const std::string& note ) {\n",
-    "  std::cout << \"( \";\n",
-    "  for( int w: box ) std::cout << w << ' ';\n",
-    "  std::cout << \") \" << note << std::endl;\n",
-    "}\n",
-    "  \n",
-    "int main() {\n",
-    "\n",
-    "  Container box{ 3, 5, 2, 4, 1, 2, 1 };\n",
-    "  print( box, \"<- unsorted\" );\n",
-    "\n",
-    "  sort( box.begin(), box.end() );\n",
-    "  print( box, \"<- sorted\" );\n",
-    "\n",
-    "  Container::iterator last = unique( begin(box), end(box) );\n",
-    "  print( box, \"<- happened in-place\" );\n",
-    "\n",
-    "  box.resize( distance( box.begin(), last ) );\n",
-    "  print( box, \"    <- truncated to new length\" );\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "57c92377",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!g++ uniq.cpp"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "b190c1c6",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!./a.out"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "e9140c15",
-   "metadata": {},
-   "source": [
-    "**Note:**\n",
-    "\n",
-    "Replacing ```vector``` by ```list``` via\n",
-    "\n",
-    "    using Container = std::vector<int>;\n",
-    "\n",
-    "will not work in this example. ```std::sort()``` is implemented to work with a **random access iterator**, which list does not provide. Instead it has its own ```list::sort()```\n",
-    "and ```list::unique()``` member functions."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "7856aa32",
-   "metadata": {},
-   "source": [
-    "***\n",
-    "**Example 2:** Performing reductions on an array\n",
-    "\n",
-    "The STL also provides some algorithms for performing numeric operations. For these we need to include ```<numeric>```."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "bed09ecd",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "%%file accumulate.cpp\n",
-    "\n",
-    "#include <numeric>\n",
-    "#include <vector>\n",
-    "#include <iostream>\n",
-    "\n",
-    "int alternate( int a, int b ) {\n",
-    "  static bool odd = false;  // static preserves value of local variable\n",
-    "  if( odd ) {\n",
-    "    odd = false;\n",
-    "    return a-b;\n",
-    "  }\n",
-    "  else {\n",
-    "    odd = true;\n",
-    "    return a+b;\n",
-    "  }\n",
-    "}\n",
-    "\n",
-    "int main() {\n",
-    "\n",
-    "  std::vector<int> v{1, 2, 3, 4, 5};\n",
-    " \n",
-    "  int gs = std::accumulate( v.begin(), v.end(), 0 );\n",
-    "  std::cout << \"sum is \" << gs << std::endl;\n",
-    "\n",
-    "  int check = std::accumulate( v.begin(), v.end(), -15 );\n",
-    "  if( check == 0 ) std::cout << \"checks out\" << std::endl;\n",
-    " \n",
-    "  int prod = std::accumulate( v.begin(), v.end(), 1, std::multiplies<int>() );\n",
-    "  std::cout << \"5! = \" << prod << std::endl;\n",
-    "\n",
-    "  int alt = std::accumulate( v.begin(), v.end(), 0, alternate );\n",
-    "  std::cout << \"Alternating sum gives \" << alt << std::endl;\n",
-    "  // 0 + 1 - 2 + 3 - 4 + 5\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "16a6d2e8",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!g++ accumulate.cpp"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "015eb80f",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!./a.out"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "1efec163",
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "C++14",
-   "language": "C++14",
-   "name": "xcpp14"
-  },
-  "language_info": {
-   "codemirror_mode": "text/x-c++src",
-   "file_extension": ".cpp",
-   "mimetype": "text/x-c++src",
-   "name": "c++",
-   "version": "14"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
+{"metadata":{"orig_nbformat":4,"kernelspec":{"display_name":"C++14","language":"C++14","name":"xcpp14"},"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"14"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# STL - Part 2 : More Containers & Algorithms","metadata":{},"id":"bac1d62f"},{"cell_type":"markdown","source":"#### Sets","metadata":{},"id":"efdc5616"},{"cell_type":"code","source":"#include <iostream>\n#include <set>\n\nstd::set< int > s = {1, 7, 4, 8, 2};   // create a set container and fill it\n\ns.insert(5);   // insert another element\ns.insert(4);\n\n// check how often an element is inside the set\nfor ( int i = 0; i < 8; ++i ) {\n    std::cout << i << \" appears \" << s.count( i ) << \" times\\n\";\n}","metadata":{"trusted":true},"execution_count":2,"outputs":[{"name":"stdout","text":"0 appears 0 times\n1 appears 1 times\n2 appears 1 times\n3 appears 0 times\n4 appears 1 times\n5 appears 1 times\n6 appears 0 times\n7 appears 1 times\n","output_type":"stream"}],"id":"c2848a05"},{"cell_type":"markdown","source":"**Remarks:**\n- Although we inserted **4** twice, it is only stored once in the set data structure. Just like a mathematical set a ```std::set``` can contain a specific object only once.\n- Hence, ```count()``` will always return either 0 or 1.\n- This shows that ```std::set``` is no sequence container.\n- Instead it stores its elements in a **sorted** fashion. This guarantees logarithmic complexity for element access.","metadata":{},"id":"44573775"},{"cell_type":"markdown","source":"Of course we can store various kinds of objects in a set ...","metadata":{},"id":"a415d236"},{"cell_type":"code","source":"struct myPair {\n  myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {};\n  int fir_;\n  int sec_;\n};\n\nstd::set< myPair > pairs; ","metadata":{"trusted":true},"execution_count":3,"outputs":[],"id":"2213e98e"},{"cell_type":"markdown","source":"... but there is a caveat. Check what happens, when we try to insert an object of type ```myPair``` into the set:","metadata":{},"id":"725387bc"},{"cell_type":"code","source":"pairs.insert( myPair(1,2) );","metadata":{"trusted":true},"execution_count":4,"outputs":[{"name":"stderr","text":"In file included from input_line_5:1:\nIn file included from /srv/conda/envs/notebook/include/xeus/xinterpreter.hpp:13:\nIn file included from /srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/functional:49:\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_function.h:386:20: error: invalid operands to binary expression ('const __cling_N54::myPair' and 'const __cling_N54::myPair')\n      { return __x < __y; }\n               ~~~ ^ ~~~\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_tree.h:2100:13: note: in instantiation of member function 'std::less<__cling_N54::myPair>::operator()' requested here\n          __comp = _M_impl._M_key_compare(__k, _S_key(__x));\n                   ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_tree.h:2153:4: note: in instantiation of member function 'std::_Rb_tree<__cling_N54::myPair, __cling_N54::myPair, std::_Identity<__cling_N54::myPair>, std::less<__cling_N54::myPair>, std::allocator<__cling_N54::myPair> >::_M_get_insert_unique_pos' requested here\n        = _M_get_insert_unique_pos(_KeyOfValue()(__v));\n          ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_set.h:521:9: note: in instantiation of function template specialization 'std::_Rb_tree<__cling_N54::myPair, __cling_N54::myPair, std::_Identity<__cling_N54::myPair>, std::less<__cling_N54::myPair>, std::allocator<__cling_N54::myPair> >::_M_insert_unique<__cling_N54::myPair>' requested here\n          _M_t._M_insert_unique(std::move(__x));\n               ^\ninput_line_12:2:8: note: in instantiation of member function 'std::set<__cling_N54::myPair, std::less<__cling_N54::myPair>, std::allocator<__cling_N54::myPair> >::insert' requested here\n pairs.insert( myPair(1,2) );\n       ^\nIn file included from input_line_5:1:\nIn file included from /srv/conda/envs/notebook/include/xeus/xinterpreter.hpp:17:\nIn file included from /srv/conda/envs/notebook/include/xeus/xcomm.hpp:15:\nIn file included from /srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/map:60:\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_tree.h:777:2: error: static_assert failed \"comparison object must be invocable with two arguments of key type\"\n        static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},\n        ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_tree.h:2100:41: note: in instantiation of member function 'std::_Rb_tree<__cling_N54::myPair, __cling_N54::myPair, std::_Identity<__cling_N54::myPair>, std::less<__cling_N54::myPair>, std::allocator<__cling_N54::myPair> >::_S_key' requested here\n          __comp = _M_impl._M_key_compare(__k, _S_key(__x));\n                                               ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_tree.h:2153:4: note: in instantiation of member function 'std::_Rb_tree<__cling_N54::myPair, __cling_N54::myPair, std::_Identity<__cling_N54::myPair>, std::less<__cling_N54::myPair>, std::allocator<__cling_N54::myPair> >::_M_get_insert_unique_pos' requested here\n        = _M_get_insert_unique_pos(_KeyOfValue()(__v));\n          ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/stl_set.h:521:9: note: in instantiation of function template specialization 'std::_Rb_tree<__cling_N54::myPair, __cling_N54::myPair, std::_Identity<__cling_N54::myPair>, std::less<__cling_N54::myPair>, std::allocator<__cling_N54::myPair> >::_M_insert_unique<__cling_N54::myPair>' requested here\n          _M_t._M_insert_unique(std::move(__x));\n               ^\ninput_line_12:2:8: note: in instantiation of member function 'std::set<__cling_N54::myPair, std::less<__cling_N54::myPair>, std::allocator<__cling_N54::myPair> >::insert' requested here\n pairs.insert( myPair(1,2) );\n       ^\n","output_type":"stream"},{"ename":"Interpreter Error","evalue":"","traceback":["Interpreter Error: "],"output_type":"error"}],"id":"51fd5597"},{"cell_type":"markdown","source":"**What went wrong?**\n\nAs the set stores its elements in a **sorted** fashion it needs some way to **order** them.\nBy default the standard ```<``` relation is used.\n\nThat does not work for our ```myPair``` data type.\n\nWe need to define our own comparison operator for the class.\n\nThere are plenty ways of doing that. Below we will use a free function for it.","metadata":{},"id":"2ee8f731"},{"cell_type":"code","source":"%%file demo.cpp\n\n#include <iostream>\n#include <set>\n\nstruct myPair {\n  myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {};\n  int fir_;\n  int sec_;\n};\n\n// our comparison function will return true, if the sum of the squares\n// of the values in left is smaller than that in right (problem with tie-breaking?)\nbool cmp( const myPair& left, const myPair& right ) {\n  int val1 = left.fir_ * left.fir_ + left.sec_ * left.sec_;\n  int val2 = right.fir_ * right.fir_ + right.sec_ * right.sec_;\n  return val1 < val2;\n}\n\nint main() {\n    \n    // create two objects\n    myPair p1( 1, 2 );\n    myPair p2( 3, 4 );\n\n    // compare them\n    std::cout << \"p1 < p2 is \" << cmp( p1, p2 ) << std::endl;\n    std::cout << \"p2 < p1 is \" << cmp( p2, p1 ) << std::endl;\n\n    // a free function in C/C++ is represented by the address of its machine code in memory;\n    // thus, the type of argument is a function pointer;\n    // we need to pass that info as a second template argument;\n    // decltype returns the type of an expression\n    std::set< myPair, decltype( cmp )* > pairs( cmp );\n\n    // alternatively we could do it ourselves; note that the (*) is important!\n    // but that way is not very C++-ish\n    // std::set< myPair, bool (*)( const myPair&, const myPair& ) > pairs( cmp );\n    \n    // now insertion will work\n    pairs.insert( p1 );\n    pairs.insert( p2 );\n\n    std::cout << \"insertion successful\" << std::endl;\n}","metadata":{"trusted":true},"execution_count":5,"outputs":[{"name":"stdout","text":"Writing demo.cpp\n","output_type":"stream"}],"id":"e8c29594"},{"cell_type":"code","source":"!g++ demo.cpp","metadata":{"trusted":true},"execution_count":6,"outputs":[],"id":"0a0207dd"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":7,"outputs":[{"name":"stdout","text":"p1 < p2 is 1\np2 < p1 is 0\ninsertion successful\n","output_type":"stream"}],"id":"68e1df4b"},{"cell_type":"markdown","source":"**Note:**  \nThe STL offers a variant **```std::unordered_set```** that stores its elements in an unsorted fashion. However, that would not solve our problem. Instead of a **comparison** we then would need to implement a **hashing function**.","metadata":{},"id":"f8b3109b"},{"cell_type":"markdown","source":"***\nThe way we added the new element in the example, i.e. via\n\n```pairs.insert( myPair(1,2) );```\n\nagain induced a copy operation. As with ```vector``` and ```list``` we could use ```emplace()``` instead.\n\nFor demonstration purposes we make the constructor of ```myPair``` verbose.","metadata":{},"id":"59ab93ca"},{"cell_type":"code","source":"%%file demo.cpp\n\n#include <iostream>\n#include <set>\n\nstruct myPair {\n  myPair( int fir, int sec ) : fir_( fir ), sec_( sec ) {\n      std::cout << \"(\" << fir_ << \",\" << sec_ << \") constructed\" << std::endl;\n  };\n \n  int fir_;\n  int sec_;\n};\n\n// our comparison function: will return true, if the square of the values\n// in left is smaller than that in right\nbool cmp( const myPair& left, const myPair& right ) {\n  int val1 = left.fir_ * left.fir_ + left.sec_ * left.sec_;\n  int val2 = right.fir_ * right.fir_ + right.sec_ * right.sec_;\n  return val1 < val2;\n}\n\nint main() {\n    \n    std::set< myPair, decltype( cmp )* > pairs( cmp );\n\n    pairs.emplace( 1, 2 );\n    pairs.emplace( 3, 4 );\n    pairs.emplace( 1, 2 );\n}","metadata":{"trusted":true},"execution_count":8,"outputs":[{"name":"stdout","text":"Overwriting demo.cpp\n","output_type":"stream"}],"id":"bdda6c33"},{"cell_type":"code","source":"!g++ demo.cpp","metadata":{"trusted":true},"execution_count":9,"outputs":[],"id":"8099d1a4"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":11,"outputs":[{"name":"stdout","text":"(1,2) constructed\n(3,4) constructed\n(1,2) constructed\n","output_type":"stream"}],"id":"5d491b27"},{"cell_type":"markdown","source":"#### Associative Containers\n\nQuoting Wikipedia:\n\n> In computer science, an **associative array**, **map**, **symbol table**, or **dictionary** is an abstract data type composed of a collection of **(key, value) pairs**, such that each possible key appears at most once in the collection.\n>\n> Operations associated with this data type allow to:\n>\n>-    add a pair to the collection;\n>-    remove a pair from the collection;\n>-    modify an existing pair;\n>-    lookup a value associated with a particular key.\n\nThe STL also offers associative containers. An example is ```std::map```.","metadata":{},"id":"8e256275"},{"cell_type":"markdown","source":"Return to our example with the traffic light. Assume that we want to print the currect state of a specific traffic light. How to do that?\n\nWell, using an enumeration for the state we can use the classical switch-case-construct:","metadata":{},"id":"fb3ef7bb"},{"cell_type":"code","source":"%%file ampel.cpp\n\n#include <iostream>\n\ntypedef enum{ GREEN, YELLOW, RED, YELLOW_RED } trafficLight;\n\nvoid ampel( trafficLight tf ) {\n\n  switch( tf ) {\n  case GREEN:\n    std::cout << \"Lights are green\" << std::endl;\n    break;\n  case YELLOW:\n    std::cout << \"Lights are yellow\" << std::endl;\n    break;\n  case RED:\n    std::cout << \"Lights are red\" << std::endl;\n    break;\n  case YELLOW_RED:\n    std::cout << \"Lights are yellow-red\" << std::endl;\n    break;\n  }\n\n}\n\nint main() {\n  trafficLight tf = GREEN;\n  ampel( tf );\n}","metadata":{"trusted":true},"execution_count":12,"outputs":[{"name":"stdout","text":"Writing ampel.cpp\n","output_type":"stream"}],"id":"59839aea"},{"cell_type":"code","source":"!g++ ampel.cpp","metadata":{"trusted":true},"execution_count":13,"outputs":[],"id":"76c8886d"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":14,"outputs":[{"name":"stdout","text":"Lights are green\n","output_type":"stream"}],"id":"731e7c94"},{"cell_type":"markdown","source":"Another (neater) way is to associate the state with a corresponding string","metadata":{},"id":"08b68b2b"},{"cell_type":"code","source":"%%file ampel.cpp\n\n#include <iostream>\n#include <map>\n\ntypedef enum{ GREEN, YELLOW, RED, YELLOW_RED } trafficLight;\n\nint main() {\n\n  std::map< trafficLight, std::string > tf2str =\n    { { GREEN, \"green\" }, { YELLOW, \"yellow\" }, { RED, \"red\" },\n      { YELLOW_RED, \"yellow-red\" } };\n\n  trafficLight tf = GREEN;\n\n  std::cout << \"Lights are \" << tf2str[ tf ] << std::endl;\n}","metadata":{"trusted":true},"execution_count":15,"outputs":[{"name":"stdout","text":"Overwriting ampel.cpp\n","output_type":"stream"}],"id":"8fe484bf"},{"cell_type":"code","source":"!g++ ampel.cpp","metadata":{"trusted":true},"execution_count":16,"outputs":[],"id":"74251bd8"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":17,"outputs":[{"name":"stdout","text":"Lights are green\n","output_type":"stream"}],"id":"5b919ca3"},{"cell_type":"markdown","source":"Another example, demonstrating additional features of ```std::map``` (converted from Gottschling, *Forschung mit modernem C++*)","metadata":{},"id":"b1718cdd"},{"cell_type":"code","source":"%%file constants.cpp\n\n#include <iostream>\n#include <string>\n#include <map>\n#include <cmath>\n\nint main() {\n\n  // create a map and fill it with some (key,value) pairs\n  std::map< std::string, double > constants = { {\"e\", std::exp(1.0)},\n      {\"pi\", 4.0*std::atan(1.0) }, {\"h\", 6.6e-34} };\n\n  // subscript operator is overloaded to allow access via a key\n  std::cout << \"Value of Planck constant is \" << constants[ \"h\" ] << '\\n';\n  constants[ \"c\" ] = 299792458;\n\n  // Hmmm, what happens here? No key \"k\" exists so far!\n  std::cout << \"Value of Coulomb constant is \" << constants[ \"k\" ] << '\\n';\n\n  // find() allows to check for existance of a key; returns an iterator\n  // to the pair, if it is found\n  std::cout << \"Value of pi is \" << constants.find( \"pi\" )->second << '\\n';\n\n  // if not it returns end\n  auto it_phi = constants.find( \"phi\" );\n  if ( it_phi != constants.end() ) {\n    std::cout << \"The golden ratio is \" << it_phi->second << '\\n';\n  }\n\n  // can use at(), if we know the pair to exists, returns value for key\n  // will throw an \"out_of_range\" exception, if we were wrong\n  std::cout << \"Value of Euler constant is \" << constants.at( \"e\" ) << \"\\n\\n\";\n\n  // range-based loop\n  for ( auto& c: constants ) {\n    std::cout << \"Value of \" << c.first << \" is \" << c.second << '\\n';\n  }\n}","metadata":{"trusted":true},"execution_count":24,"outputs":[{"name":"stdout","text":"Overwriting constants.cpp\n","output_type":"stream"}],"id":"52f631d4"},{"cell_type":"code","source":"!g++ constants.cpp","metadata":{"trusted":true},"execution_count":25,"outputs":[],"id":"aaba61ba"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":26,"outputs":[{"name":"stdout","text":"Value of Planck constant is 6.6e-34\nValue of Coulomb constant is 0\nValue of pi is 3.14159\nValue of Euler constant is 2.71828\n\nValue of c is 2.99792e+08\nValue of e is 2.71828\nValue of h is 6.6e-34\nValue of k is 0\nValue of pi is 3.14159\n","output_type":"stream"}],"id":"3f9362c9"},{"cell_type":"markdown","source":"**Note:**  \nAs with ```set``` there is also a variant of ```map``` that uses hashing instead of ordered storage and is called ```unordered_map```.","metadata":{},"id":"a7e8490c"},{"cell_type":"markdown","source":"#### Algorithm\n\nThe STL supports also various algorithms that work on the entries in a container. The advantage again is that these save us implementation and will (potentially) work for various kinds of containers.\n\nWe will briefly look at two examples.","metadata":{},"id":"2032e7f2"},{"cell_type":"markdown","source":"**Example 1:** Remove double entries from a sequence","metadata":{},"id":"4c239bfa"},{"cell_type":"code","source":"%%file uniq.cpp\n\n#include <algorithm>\n#include <vector>\n#include <iostream>\n\nusing Container = std::vector<int>;\n\nvoid print( const Container& box, const std::string& note ) {\n  std::cout << \"( \";\n  for( int w: box ) std::cout << w << ' ';\n  std::cout << \") \" << note << std::endl;\n}\n  \nint main() {\n\n  Container box{ 3, 5, 2, 4, 1, 2, 1 };\n  print( box, \"<- unsorted\" );\n\n  sort( box.begin(), box.end() );\n  print( box, \"<- sorted\" );\n\n  Container::iterator last = unique( begin(box), end(box) );\n  print( box, \"<- happened in-place\" );\n\n  box.resize( distance( box.begin(), last ) );\n  print( box, \"    <- truncated to new length\" );\n}","metadata":{"trusted":true},"execution_count":27,"outputs":[{"name":"stdout","text":"Writing uniq.cpp\n","output_type":"stream"}],"id":"aed454ce"},{"cell_type":"code","source":"!g++ uniq.cpp","metadata":{"trusted":true},"execution_count":28,"outputs":[],"id":"57c92377"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":29,"outputs":[{"name":"stdout","text":"( 3 5 2 4 1 2 1 ) <- unsorted\n( 1 1 2 2 3 4 5 ) <- sorted\n( 1 2 3 4 5 4 5 ) <- happened in-place\n( 1 2 3 4 5 )     <- truncated to new length\n","output_type":"stream"}],"id":"b190c1c6"},{"cell_type":"markdown","source":"**Note:**\n\nReplacing ```vector``` by ```list``` via\n\n    using Container = std::list<int>;\n\nwill not work in this example. ```std::sort()``` is implemented to work with a **random access iterator**, which list does not provide. Instead it has its own ```list::sort()```\nand ```list::unique()``` member functions.","metadata":{},"id":"e9140c15"},{"cell_type":"markdown","source":"***\n**Example 2:** Performing reductions on an array\n\nThe STL also provides some algorithms for performing numeric operations. For these we need to include ```<numeric>```.","metadata":{},"id":"7856aa32"},{"cell_type":"code","source":"%%file accumulate.cpp\n\n#include <numeric>\n#include <vector>\n#include <iostream>\n\nint alternate( int a, int b ) {\n  static bool odd = false;  // static preserves value of local variable\n  if( odd ) {\n    odd = false;\n    return a-b;\n  }\n  else {\n    odd = true;\n    return a+b;\n  }\n}\n\nint main() {\n\n  std::vector<int> v{1, 2, 3, 4, 5};\n \n  int gs = std::accumulate( v.begin(), v.end(), 0 );\n  std::cout << \"sum is \" << gs << std::endl;\n\n  int check = std::accumulate( v.begin(), v.end(), -15 );\n  if( check == 0 ) std::cout << \"checks out\" << std::endl;\n \n  int prod = std::accumulate( v.begin(), v.end(), 1, std::multiplies<int>() );\n  std::cout << \"5! = \" << prod << std::endl;\n\n  int alt = std::accumulate( v.begin(), v.end(), 0, alternate );\n  std::cout << \"Alternating sum gives \" << alt << std::endl;\n  // 0 + 1 - 2 + 3 - 4 + 5\n}","metadata":{"trusted":true},"execution_count":30,"outputs":[{"name":"stdout","text":"Writing accumulate.cpp\n","output_type":"stream"}],"id":"bed09ecd"},{"cell_type":"code","source":"!g++ accumulate.cpp","metadata":{"trusted":true},"execution_count":31,"outputs":[],"id":"16a6d2e8"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":32,"outputs":[{"name":"stdout","text":"sum is 15\nchecks out\n5! = 120\nAlternating sum gives 3\n","output_type":"stream"}],"id":"015eb80f"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"1efec163"}]}
\ No newline at end of file