diff --git a/notebooks/06_STL.ipynb b/notebooks/06_STL.ipynb
index 0aed4c7d4d047249e0c7760c6e3db7108c1e32e7..644d31f6cc3d289880d75d72d619316bf8285d15 100644
--- a/notebooks/06_STL.ipynb
+++ b/notebooks/06_STL.ipynb
@@ -1 +1,1249 @@
-{"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":{},"execution_count":null,"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":7,"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":8,"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":9,"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":null,"outputs":[],"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  vec.reserve( 10 );\n\n  // Will fail, as we have no default constructor\n  // std::vector< Dummy > vec( 2 );\n\n  vec.push_back( obj1 );\n  vec.push_back( obj2 );\n\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":null,"outputs":[],"id":"b3dfe338"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":null,"outputs":[],"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 )\n      << std::endl;\n  }\n#endif\n\n}","metadata":{},"execution_count":null,"outputs":[],"id":"7e537a6b"},{"cell_type":"code","source":"!g++ -Wall -DOUT_OF_BOUNDS exception.cpp","metadata":{},"execution_count":null,"outputs":[],"id":"0513b187"},{"cell_type":"code","source":"!./a.out","metadata":{},"execution_count":null,"outputs":[],"id":"af593caa"},{"cell_type":"code","source":"!g++ -Wall exception.cpp","metadata":{},"execution_count":null,"outputs":[],"id":"131dd4f5"},{"cell_type":"code","source":"!./a.out","metadata":{},"execution_count":null,"outputs":[],"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 containers 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":null,"outputs":[],"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":null,"outputs":[],"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":"// %%file fubar.cpp\n\n#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":null,"outputs":[],"id":"4a6ba619"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":null,"outputs":[],"id":"907c51a5"},{"cell_type":"markdown","source":"**Remarks:**\n* The iterator is no pointer, but 'feels' like once, 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 modification switch from a **std::vector** to a **std::list**.","metadata":{},"id":"ba14f11e"},{"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    // 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":null,"outputs":[],"id":"9f8daf26"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":null,"outputs":[],"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.","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":null,"outputs":[],"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":"std::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":null,"outputs":[],"id":"44a9bf28"},{"cell_type":"markdown","source":"### Range-based access\nC++ nowadays also supports and 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\n    int k = 0;\n    for( int value: box ) {\n        std::cout << \"box(\" << k << \") = \" << value << std::endl;\n        k++;\n    }\n}","metadata":{},"execution_count":null,"outputs":[],"id":"2497a826"},{"cell_type":"code","source":"main();","metadata":{},"execution_count":null,"outputs":[],"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":null,"outputs":[],"id":"d1c64b51"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"28b2e3fb"}]}
\ No newline at end of file
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "0921615f",
+   "metadata": {},
+   "source": [
+    "# Standard Template Library (STL)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "eacfe313",
+   "metadata": {},
+   "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**."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e3d3641e",
+   "metadata": {},
+   "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"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "213d8360",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <vector>\n",
+    "#include <iostream>\n",
+    "#include <iomanip>      // required for setw() below\n",
+    "\n",
+    "int 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",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "bd290a8a",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Square of 1 is  1\n",
+      "Square of 2 is  4\n",
+      "Square of 3 is  9\n",
+      "Square of 4 is 16\n",
+      "Square of 5 is 25\n"
+     ]
+    }
+   ],
+   "source": [
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "aa2f2d9d",
+   "metadata": {},
+   "source": [
+    "Of course we can also store data of other types in a vector."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "0bb0723b",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <string>\n",
+    "\n",
+    "int 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",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "aca19e34",
+   "metadata": {},
+   "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."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "e5b2a349",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class Dummy {\n",
+    "\n",
+    "public:\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",
+    "\n",
+    "private:\n",
+    "    \n",
+    "  std::string name_;\n",
+    "\n",
+    "};"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "bd331ee4",
+   "metadata": {},
+   "outputs": [],
+   "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",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "a04654d2",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "-> Creating single objects\n",
+      "Object 'one' was created\n",
+      "Object 'two' was created\n",
+      "\n",
+      "-> Putting objects in container\n",
+      "Object 'one_copy' was created\n",
+      "Object 'two_copy' was created\n",
+      "Object 'one_copy_copy' was created\n",
+      "Object 'one_copy' was destroyed\n",
+      "\n",
+      "-> Auto clean-up at program end\n",
+      "Object 'one_copy_copy' was destroyed\n",
+      "Object 'two_copy' was destroyed\n",
+      "Object 'two' was destroyed\n",
+      "Object 'one' was destroyed\n"
+     ]
+    }
+   ],
+   "source": [
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e9db8ff6",
+   "metadata": {},
+   "source": [
+    "**Observations:**\n",
+    "1. Apparently ```std::vector``` stores **copies** of the objects we append.\n",
+    "1. But why is a second copy of **one**, i.e. **one_copy_copy** created?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "id": "5d4ba36f",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    " // Suggestions anyone?\n",
+    "\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e36f2bb9",
+   "metadata": {},
+   "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",
+    "\n",
+    "The capacity of the vector is managed dynamically. We can see this in the following experiment:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "id": "261a8327",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "vec: length =  0, capacity =  0\n",
+      "vec: length =  1, capacity =  1\n",
+      "vec: length =  2, capacity =  2\n",
+      "vec: length =  3, capacity =  4\n",
+      "vec: length =  4, capacity =  4\n",
+      "vec: length =  5, capacity =  8\n",
+      "vec: length =  6, capacity =  8\n",
+      "vec: length =  7, capacity =  8\n",
+      "vec: length =  8, capacity =  8\n",
+      "vec: length =  9, capacity = 16\n",
+      "vec: length = 10, capacity = 16\n",
+      "vec: length = 11, capacity = 16\n",
+      "vec: length = 12, capacity = 16\n",
+      "vec: length = 13, capacity = 16\n",
+      "vec: length = 14, capacity = 16\n",
+      "vec: length = 15, capacity = 16\n",
+      "vec: length = 16, capacity = 16\n",
+      "vec: length = 17, capacity = 32\n",
+      "vec: length = 18, capacity = 32\n",
+      "vec: length = 19, capacity = 32\n",
+      "vec: length = 20, capacity = 32\n",
+      "vec: length = 21, capacity = 32\n",
+      "vec: length = 22, capacity = 32\n",
+      "vec: length = 23, capacity = 32\n",
+      "vec: length = 24, capacity = 32\n",
+      "vec: length = 25, capacity = 32\n",
+      "vec: length = 26, capacity = 32\n",
+      "vec: length = 27, capacity = 32\n",
+      "vec: length = 28, capacity = 32\n",
+      "vec: length = 29, capacity = 32\n",
+      "vec: length = 30, capacity = 32\n",
+      "vec: length = 31, capacity = 32\n",
+      "vec: length = 32, capacity = 32\n",
+      "vec: length = 33, capacity = 64\n"
+     ]
+    }
+   ],
+   "source": [
+    "#include <vector>\n",
+    "#include <iostream>\n",
+    "#include <iomanip>\n",
+    "\n",
+    "int 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",
+    "\n",
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b9d88e30",
+   "metadata": {},
+   "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**  \n",
+    "The memory management, but especially the copying constitutes a significant overhead, especially for large arrays.\n",
+    "When we already know what the maximal size of our vector will be, we can avoid this, by **reserving** a large enough\n",
+    "capacity."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "id": "664eec8e",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "vec: length =  0, capacity = 100\n",
+      "vec: length =  1, capacity = 100\n",
+      "vec: length =  2, capacity = 100\n",
+      "vec: length =  3, capacity = 100\n",
+      "vec: length =  4, capacity = 100\n",
+      "vec: length =  5, capacity = 100\n",
+      "vec: length =  6, capacity = 100\n",
+      "vec: length =  7, capacity = 100\n",
+      "vec: length =  8, capacity = 100\n",
+      "vec: length =  9, capacity = 100\n",
+      "vec: length = 10, capacity = 100\n",
+      "vec: length = 11, capacity = 100\n",
+      "vec: length = 12, capacity = 100\n",
+      "vec: length = 13, capacity = 100\n",
+      "vec: length = 14, capacity = 100\n",
+      "vec: length = 15, capacity = 100\n",
+      "vec: length = 16, capacity = 100\n",
+      "vec: length = 17, capacity = 100\n",
+      "vec: length = 18, capacity = 100\n",
+      "vec: length = 19, capacity = 100\n",
+      "vec: length = 20, capacity = 100\n",
+      "vec: length = 21, capacity = 100\n",
+      "vec: length = 22, capacity = 100\n",
+      "vec: length = 23, capacity = 100\n",
+      "vec: length = 24, capacity = 100\n",
+      "vec: length = 25, capacity = 100\n",
+      "vec: length = 26, capacity = 100\n",
+      "vec: length = 27, capacity = 100\n",
+      "vec: length = 28, capacity = 100\n",
+      "vec: length = 29, capacity = 100\n",
+      "vec: length = 30, capacity = 100\n",
+      "vec: length = 31, capacity = 100\n",
+      "vec: length = 32, capacity = 100\n",
+      "vec: length = 33, capacity = 100\n"
+     ]
+    }
+   ],
+   "source": [
+    "#include <vector>\n",
+    "#include <iostream>\n",
+    "#include <iomanip>\n",
+    "\n",
+    "int 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",
+    "\n",
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2352d31e",
+   "metadata": {},
+   "source": [
+    "***\n",
+    "This will also work for our original example with the class Dummy:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "id": "a5dffa46-65b8-477b-bbd8-a0ad0801d1fb",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "-> Creating single objects\n",
+      "Object 'one' was created\n",
+      "Object 'two' was created\n",
+      "\n",
+      "-> Putting objects in container\n",
+      "Object 'one_copy' was created\n",
+      "Object 'two_copy' was created\n",
+      "\n",
+      "-> Auto clean-up at program end\n",
+      "Object 'one_copy' was destroyed\n",
+      "Object 'two_copy' was destroyed\n",
+      "Object 'two' was destroyed\n",
+      "Object 'one' was destroyed\n"
+     ]
+    }
+   ],
+   "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",
+    "\n",
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "41bf0d83",
+   "metadata": {},
+   "source": [
+    "**But:** Wouldn't it be nicer/easier to have the reservation be part of the instantiation? Let's check:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "id": "351ace20",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "In file included from input_line_5:1:\n",
+      "In file included from /home/mohr/local/miniconda3/envs/cling/include/xeus/xinterpreter.hpp:15:\n",
+      "In file included from /home/mohr/local/miniconda3/envs/cling/bin/../lib/gcc/x86_64-conda-linux-gnu/9.3.0/../../../../x86_64-conda-linux-gnu/include/c++/9.3.0/vector:65:\n",
+      "\u001b[1m/home/mohr/local/miniconda3/envs/cling/bin/../lib/gcc/x86_64-conda-linux-gnu/9.3.0/../../../../x86_64-conda-linux-gnu/include/c++/9.3.0/bits/stl_construct.h:75:38: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1mcall to deleted constructor of '__cling_N56::Dummy'\u001b[0m\n",
+      "    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }\n",
+      "\u001b[0;1;32m                                     ^\n",
+      "\u001b[0m\u001b[1m/home/mohr/local/miniconda3/envs/cling/bin/../lib/gcc/x86_64-conda-linux-gnu/9.3.0/../../../../x86_64-conda-linux-gnu/include/c++/9.3.0/bits/stl_uninitialized.h:545:8: \u001b[0m\u001b[0;1;30mnote: \u001b[0min instantiation of function template specialization\n",
+      "      'std::_Construct<__cling_N56::Dummy>' requested here\u001b[0m\n",
+      "                std::_Construct(std::__addressof(*__cur));\n",
+      "\u001b[0;1;32m                     ^\n",
+      "\u001b[0m\u001b[1m/home/mohr/local/miniconda3/envs/cling/bin/../lib/gcc/x86_64-conda-linux-gnu/9.3.0/../../../../x86_64-conda-linux-gnu/include/c++/9.3.0/bits/stl_uninitialized.h:601:2: \u001b[0m\u001b[0;1;30mnote: \u001b[0min instantiation of function template specialization\n",
+      "      'std::__uninitialized_default_n_1<false>::__uninit_default_n<__cling_N56::Dummy\n",
+      "      *, unsigned long>' requested here\u001b[0m\n",
+      "        __uninit_default_n(__first, __n);\n",
+      "\u001b[0;1;32m        ^\n",
+      "\u001b[0m\u001b[1m/home/mohr/local/miniconda3/envs/cling/bin/../lib/gcc/x86_64-conda-linux-gnu/9.3.0/../../../../x86_64-conda-linux-gnu/include/c++/9.3.0/bits/stl_uninitialized.h:663:19: \u001b[0m\u001b[0;1;30mnote: \u001b[0min instantiation of function template specialization\n",
+      "      'std::__uninitialized_default_n<__cling_N56::Dummy *, unsigned long>'\n",
+      "      requested here\u001b[0m\n",
+      "    { return std::__uninitialized_default_n(__first, __n); }\n",
+      "\u001b[0;1;32m                  ^\n",
+      "\u001b[0m\u001b[1m/home/mohr/local/miniconda3/envs/cling/bin/../lib/gcc/x86_64-conda-linux-gnu/9.3.0/../../../../x86_64-conda-linux-gnu/include/c++/9.3.0/bits/stl_vector.h:1603:9: \u001b[0m\u001b[0;1;30mnote: \u001b[0min instantiation of function template specialization\n",
+      "      'std::__uninitialized_default_n_a<__cling_N56::Dummy *, unsigned long,\n",
+      "      __cling_N56::Dummy>' requested here\u001b[0m\n",
+      "          std::__uninitialized_default_n_a(this->_M_impl._M_start, __n,\n",
+      "\u001b[0;1;32m               ^\n",
+      "\u001b[0m\u001b[1m/home/mohr/local/miniconda3/envs/cling/bin/../lib/gcc/x86_64-conda-linux-gnu/9.3.0/../../../../x86_64-conda-linux-gnu/include/c++/9.3.0/bits/stl_vector.h:509:9: \u001b[0m\u001b[0;1;30mnote: \u001b[0min instantiation of member function 'std::vector<__cling_N56::Dummy,\n",
+      "      std::allocator<__cling_N56::Dummy> >::_M_default_initialize' requested\n",
+      "      here\u001b[0m\n",
+      "      { _M_default_initialize(__n); }\n",
+      "\u001b[0;1;32m        ^\n",
+      "\u001b[0m\u001b[1minput_line_22:2:23: \u001b[0m\u001b[0;1;30mnote: \u001b[0min instantiation of member function 'std::vector<__cling_N56::Dummy,\n",
+      "      std::allocator<__cling_N56::Dummy> >::vector' requested here\u001b[0m\n",
+      " std::vector< Dummy > vec( 2 );\n",
+      "\u001b[0;1;32m                      ^\n",
+      "\u001b[0m\u001b[1minput_line_13:3:5: \u001b[0m\u001b[0;1;30mnote: \u001b[0m'Dummy' has been explicitly marked deleted here\u001b[0m\n",
+      "    Dummy() = delete;\n",
+      "\u001b[0;1;32m    ^\n",
+      "\u001b[0m"
+     ]
+    },
+    {
+     "ename": "Interpreter Error",
+     "evalue": "",
+     "output_type": "error",
+     "traceback": [
+      "Interpreter Error: "
+     ]
+    }
+   ],
+   "source": [
+    "std::vector< Dummy > vec( 2 );"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ec2de34b",
+   "metadata": {},
+   "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."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d1cdc134",
+   "metadata": {},
+   "source": [
+    "Let us take a look at other ways to construct an std::vector:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "id": "b3dfe338",
+   "metadata": {},
+   "outputs": [],
+   "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",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "id": "ad928c13",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "-> Creating single objects\n",
+      "Object 'one' was created\n",
+      "Object 'two' was created\n",
+      "\n",
+      "-> Putting objects in container\n",
+      "Object 'three' was created\n",
+      "\n",
+      "-> Auto clean-up at program end\n",
+      "Object 'three' was destroyed\n",
+      "Object 'two' was destroyed\n",
+      "Object 'one' was destroyed\n"
+     ]
+    }
+   ],
+   "source": [
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "eebc1269",
+   "metadata": {},
+   "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**."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "dc64b2c9",
+   "metadata": {},
+   "source": [
+    "***\n",
+    "We have already seen that we have read/write access to entries of our vector with the **```[ ]```** operator. Another alternative is the **```at()```** method.\n",
+    "\n",
+    "The difference is that **```at()```** will throw an exception, when we commit an out-of-bounds access error."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "id": "7e537a6b",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Overwriting exception.cpp\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%file exception.cpp\n",
+    "\n",
+    "#include <iostream>\n",
+    "#include <vector>\n",
+    "\n",
+    "int 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 )\n",
+    "      << std::endl;\n",
+    "  }\n",
+    "#endif\n",
+    "\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "id": "0513b187",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!g++ -Wall -DOUT_OF_BOUNDS exception.cpp"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 25,
+   "id": "af593caa",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "vec[ 0 ] = 2\n",
+      "vec[ 1 ] = 2\n",
+      "vec[ 2 ] = 2\n",
+      "vec[ 3 ] = 2\n",
+      "vec[ 4 ] = 2\n",
+      "vec[ 5 ] = 2\n",
+      "vec[ 6 ] = 2\n",
+      "vec[ 7 ] = 2\n",
+      "vec[ 8 ] = 2\n",
+      "vec[ 9 ] = 2\n",
+      "vec[ 10 ] = 0\n",
+      "Too bad we reached this line :-(\n"
+     ]
+    }
+   ],
+   "source": [
+    "!./a.out"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 26,
+   "id": "131dd4f5",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!g++ -Wall exception.cpp"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "id": "e6ce23bd",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "vec[ 0 ] = 2\n",
+      "vec[ 1 ] = 2\n",
+      "vec[ 2 ] = 2\n",
+      "vec[ 3 ] = 2\n",
+      "vec[ 4 ] = 2\n",
+      "vec[ 5 ] = 2\n",
+      "vec[ 6 ] = 2\n",
+      "vec[ 7 ] = 2\n",
+      "vec[ 8 ] = 2\n",
+      "vec[ 9 ] = 2\n",
+      "terminate 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)\n",
+      "Aborted (core dumped)\n"
+     ]
+    }
+   ],
+   "source": [
+    "!./a.out"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8e0987ec",
+   "metadata": {},
+   "source": [
+    "### Iterators and range-based access"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "80b33721",
+   "metadata": {},
+   "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",
+    "\n",
+    "and the same value $v$ can show up at multiple positions ($e_i = v = e_j$ with $i\\neq j$).\n",
+    "\n",
+    "Another 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",
+    "\n",
+    "where\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",
+    " \n",
+    "Contrary to a vector, a list does not offer index-based access to its elements. Instead we can do list-traversal.\n",
+    "\n",
+    "Now consider the following piece of code:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 28,
+   "id": "eee3b34a",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "box(0) = 1\n",
+      "box(1) = 3\n",
+      "box(2) = 5\n",
+      "box(3) = 7\n",
+      "box(4) = 9\n"
+     ]
+    }
+   ],
+   "source": [
+    "#include <iostream>\n",
+    "#include <vector>\n",
+    "\n",
+    "int 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",
+    "\n",
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "33c83653",
+   "metadata": {},
+   "source": [
+    "For what we are doing in the program the type of underlying container does not really matter.  \n",
+    "How about replacing vector with list for a change?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "id": "d2806a4f",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "\u001b[1minput_line_33:13:50: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1mtype 'Container' (aka 'list<int>') does not provide a subscript operator\u001b[0m\n",
+      "        std::cout << \"box(\" << k << \") = \" << box[k] << std::endl;\n",
+      "\u001b[0;1;32m                                              ~~~^~\n",
+      "\u001b[0m"
+     ]
+    },
+    {
+     "ename": "Interpreter Error",
+     "evalue": "",
+     "output_type": "error",
+     "traceback": [
+      "Interpreter Error: "
+     ]
+    }
+   ],
+   "source": [
+    "#include <iostream>\n",
+    "#include <list>      // okay, need to adapt the include\n",
+    "\n",
+    "int 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",
+    "\n",
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "40d19e9a",
+   "metadata": {},
+   "source": [
+    "***\n",
+    "Now that is, where **iterators** come into play. They abstract away the details of the underlying container."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "id": "4a6ba619",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "#include <vector>\n",
+    "\n",
+    "int 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",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 32,
+   "id": "907c51a5",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "box(0) = 1\n",
+      "box(1) = 3\n",
+      "box(2) = 5\n",
+      "box(3) = 7\n",
+      "box(4) = 9\n"
+     ]
+    }
+   ],
+   "source": [
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5b9766a5",
+   "metadata": {},
+   "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."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ba14f11e",
+   "metadata": {},
+   "source": [
+    "Since our loop is using an iterator now, we can with two small modifications switch from a **std::vector** to a **std::list**."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 33,
+   "id": "9f8daf26",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "#include <list>       // mod #1\n",
+    "\n",
+    "int 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",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 34,
+   "id": "53211059",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "box(0) = 1\n",
+      "box(1) = 3\n",
+      "box(2) = 5\n",
+      "box(3) = 7\n",
+      "box(4) = 9\n"
+     ]
+    }
+   ],
+   "source": [
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "44f9f4e2",
+   "metadata": {},
+   "source": [
+    "#### 'Reverse mode' \n",
+    "Up 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)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 35,
+   "id": "158950d1",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "3\n",
+      "2\n",
+      "1\n"
+     ]
+    }
+   ],
+   "source": [
+    "#include <iostream>\n",
+    "\n",
+    "std::vector< short > vec;\n",
+    "\n",
+    "vec.push_back( 1 );\n",
+    "vec.push_back( 2 );\n",
+    "vec.push_back( 3 );\n",
+    "\n",
+    "for( std::vector< short >::reverse_iterator it = vec.rbegin(); it != vec.rend(); it++ ) {\n",
+    "    std::cout << *it << std::endl;\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "96bed53d",
+   "metadata": {},
+   "source": [
+    "#### 'Const mode'\n",
+    "When we do not intend to change the element in the loop body, we can also employ a ```const_iterator```."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 36,
+   "id": "44a9bf28",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "4\n",
+      "5\n",
+      "6\n"
+     ]
+    }
+   ],
+   "source": [
+    "std::list< double > lc;\n",
+    "using cIter = std::list< double >::const_iterator;\n",
+    "lc.push_back( 4.0 );\n",
+    "lc.push_back( 5.0 );\n",
+    "lc.push_back( 6.0 );\n",
+    "for( cIter it = lc.begin(); it != lc.end(); it++ ) {\n",
+    "  std::cout << *it << std::endl;\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c731df99",
+   "metadata": {},
+   "source": [
+    "### Range-based access\n",
+    "C++ nowadays also supports an even simpler way to encode a loop over a container:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 41,
+   "id": "2497a826",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "#include <list>\n",
+    "\n",
+    "int 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",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 42,
+   "id": "a8918a0e",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "box(0) = 0\n",
+      "box(1) = 2\n",
+      "box(2) = 4\n",
+      "box(3) = 6\n",
+      "box(4) = 8\n"
+     ]
+    }
+   ],
+   "source": [
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f4d8fd5e",
+   "metadata": {},
+   "source": [
+    "Note that the last loop version involved a copy operation!"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 43,
+   "id": "d1c64b51",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "-> Put objects into container\n",
+      "Object 'one' was created\n",
+      "Object 'two' was created\n",
+      "\n",
+      "-> Run a loop\n",
+      "Object 'one_copy' was created\n",
+      "* one_copy\n",
+      "Object 'one_copy' was destroyed\n",
+      "Object 'two_copy' was created\n",
+      "* two_copy\n",
+      "Object 'two_copy' was destroyed\n"
+     ]
+    }
+   ],
+   "source": [
+    "std::cout << \"-> Put objects into container\" << std::endl;\n",
+    "std::vector< Dummy > vec;\n",
+    "vec.reserve(2);\n",
+    "vec.emplace_back( \"one\" );\n",
+    "vec.emplace_back( \"two\" );\n",
+    "\n",
+    "std::cout << \"\\n-> Run a loop\" << std::endl;\n",
+    "for( Dummy x: vec ) {\n",
+    "    std::cout << \"* \" << x.getName() << std::endl;\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "28b2e3fb",
+   "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
+}