diff --git a/notebooks/05_Friendship.ipynb b/notebooks/05_Friendship.ipynb
index 6b9f3a07f073b7368f58a391b115eb82f7bf6095..bfae721a174df54a4f0d83105a93d847a1aecfc2 100644
--- a/notebooks/05_Friendship.ipynb
+++ b/notebooks/05_Friendship.ipynb
@@ -1 +1 @@
-{"metadata":{"orig_nbformat":4,"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"11"},"kernelspec":{"name":"xcpp11","display_name":"C++11","language":"C++11"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Friendship","metadata":{},"id":"e04e0b83-753f-4a58-bce3-3746e7c69650"},{"cell_type":"markdown","source":"* In many situations we tend to make the data members of our classes **private** (or protected in the case of inheritance). \n* If we do so, then no objects of other classes or free-functions have access to them. Not even other objects of the same class.\n* Sometimes this is too restrictive, though. We might need to provide direct access to our members for selected **friends**.\n\n* A typical example is, when we want to overload the ```<<``` operator, to be able to place our object into an output stream.","metadata":{},"id":"6c224ee8-f4c0-495c-8261-6d2c17f845a6"},{"cell_type":"markdown","source":"***\nLet us create a simpe demo class","metadata":{},"id":"ff1a3263-e349-4840-af2f-ad90fde352ac"},{"cell_type":"code","source":"class myTuple {\n\n  int fir;\n  int sec;\n\npublic:\n  myTuple( int fir, int sec ) : fir(fir), sec(sec) {};\n\n};","metadata":{"trusted":true},"execution_count":1,"outputs":[],"id":"cebe299d-3bcc-4a57-af69-8aed41401244"},{"cell_type":"markdown","source":"and try to test-run it","metadata":{},"id":"97622c4c-2eba-47cd-8dbb-c2fe9963dbc3"},{"cell_type":"code","source":"#include <iostream>\n\nint main() {\n\n  myTuple obj( 3, 5 );\n\n  std::cout << \"Pair is: \" << obj << std::endl;\n}\n\nmain();","metadata":{"trusted":true},"execution_count":2,"outputs":[{"name":"stderr","text":"input_line_9:3:28: error: invalid operands to binary expression ('basic_ostream<char, std::char_traits<char> >' and '__cling_N52::myTuple')\n  std::cout << \"Pair is: \" << obj << std::endl;\n  ~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:245:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const void *' for 1st argument; take the address of the argument with &\n      operator<<(const void* __p)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/system_error:217:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const std::error_code' for 2nd argument\n    operator<<(basic_ostream<_CharT, _Traits>& __os, const error_code& __e)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:108:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'std::basic_ostream<char, std::char_traits<char> >::__ostream_type &(*)(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &)' (aka 'basic_ostream<char, std::char_traits<char> > &(*)(basic_ostream<char, std::char_traits<char> > &)') for 1st argument\n      operator<<(__ostream_type& (*__pf)(__ostream_type&))\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:117:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'std::basic_ostream<char, std::char_traits<char> >::__ios_type &(*)(std::basic_ostream<char, std::char_traits<char> >::__ios_type &)' (aka 'basic_ios<char, std::char_traits<char> > &(*)(basic_ios<char, std::char_traits<char> > &)') for 1st argument\n      operator<<(__ios_type& (*__pf)(__ios_type&))\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:127:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'std::ios_base &(*)(std::ios_base &)' for 1st argument\n      operator<<(ios_base& (*__pf) (ios_base&))\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:166:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'long' for 1st argument\n      operator<<(long __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:170:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'unsigned long' for 1st argument\n      operator<<(unsigned long __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:174:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'bool' for 1st argument\n      operator<<(bool __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:178:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'short' for 1st argument\n      operator<<(short __n);\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:181:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'unsigned short' for 1st argument\n      operator<<(unsigned short __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:189:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'int' for 1st argument\n      operator<<(int __n);\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:192:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'unsigned int' for 1st argument\n      operator<<(unsigned int __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:201:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'long long' for 1st argument\n      operator<<(long long __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:205:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'unsigned long long' for 1st argument\n      operator<<(unsigned long long __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:220:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'double' for 1st argument\n      operator<<(double __f)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:224:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'float' for 1st argument\n      operator<<(float __f)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:232:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'long double' for 1st argument\n      operator<<(long double __f)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:276:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'std::basic_ostream<char, std::char_traits<char> >::__streambuf_type *' (aka 'basic_streambuf<char, std::char_traits<char> > *') for 1st argument\n      operator<<(__streambuf_type* __sb);\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:511:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'char' for 2nd argument\n    operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:517:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'char' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, char __c)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:523:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'signed char' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, signed char __c)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:528:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'unsigned char' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:565:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const char *' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, const char* __s)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:578:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const signed char *' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, const signed char* __s)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:583:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const unsigned char *' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, const unsigned char* __s)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/ostream.tcc:321:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const char *' for 2nd argument\n    operator<<(basic_ostream<_CharT, _Traits>& __out, const char* __s)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:506:5: note: candidate template ignored: deduced conflicting types for parameter '_CharT' ('char' vs. '__cling_N52::myTuple')\n    operator<<(basic_ostream<_CharT, _Traits>& __out, _CharT __c)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/basic_string.h:6419:5: note: candidate template ignored: could not match 'basic_string<type-parameter-0-0, type-parameter-0-1, type-parameter-0-2>' against '__cling_N52::myTuple'\n    operator<<(basic_ostream<_CharT, _Traits>& __os,\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:548:5: note: candidate template ignored: could not match 'const _CharT *' against '__cling_N52::myTuple'\n    operator<<(basic_ostream<_CharT, _Traits>& __out, const _CharT* __s)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:691:5: note: candidate template ignored: requirement '__and_<__not_<is_lvalue_reference<basic_ostream<char> &> >, __is_convertible_to_basic_ostream<basic_ostream<char> &>, __is_insertable<__rvalue_ostream_type<basic_ostream<char> &>, const myTuple &> >::value' was not satisfied [with _Ostream = std::basic_ostream<char> &, _Tp = __cling_N52::myTuple]\n    operator<<(_Ostream&& __os, const _Tp& __x)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/shared_ptr.h:66:5: note: candidate template ignored: could not match '__shared_ptr<type-parameter-0-2, _Lp>' against '__cling_N52::myTuple'\n    operator<<(std::basic_ostream<_Ch, _Tr>& __os,\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:413:5: note: candidate template ignored: could not match '_Expr' against 'basic_ostream'\n    _DEFINE_EXPR_BINARY_OPERATOR(<<, __shift_left)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:344:5: note: expanded from macro '_DEFINE_EXPR_BINARY_OPERATOR'\n    operator _Op(const _Expr<_Dom1, typename _Dom1::value_type>& __v,   \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:413:5: note: candidate template ignored: could not match '_Expr' against 'basic_ostream'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:357:5: note: expanded from macro '_DEFINE_EXPR_BINARY_OPERATOR'\n    operator _Op(const _Expr<_Dom, typename _Dom::value_type>& __v,     \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:413:5: note: candidate template ignored: could not match '_Expr<type-parameter-0-0, typename type-parameter-0-0::value_type>' against '__cling_N52::myTuple'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:370:5: note: expanded from macro '_DEFINE_EXPR_BINARY_OPERATOR'\n    operator _Op(const typename _Dom::value_type& __t,                  \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:413:5: note: candidate template ignored: could not match '_Expr' against 'basic_ostream'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:383:5: note: expanded from macro '_DEFINE_EXPR_BINARY_OPERATOR'\n    operator _Op(const _Expr<_Dom,typename _Dom::value_type>& __e,      \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:413:5: note: candidate template ignored: could not match '_Expr<type-parameter-0-0, typename type-parameter-0-0::value_type>' against '__cling_N52::myTuple'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:396:5: note: expanded from macro '_DEFINE_EXPR_BINARY_OPERATOR'\n    operator _Op(const valarray<typename _Dom::value_type>& __v,        \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1193:1: note: candidate template ignored: could not match 'valarray' against 'basic_ostream'\n_DEFINE_BINARY_OPERATOR(<<, __shift_left)\n^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1155:5: note: expanded from macro '_DEFINE_BINARY_OPERATOR'\n    operator _Op(const valarray<_Tp>& __v, const valarray<_Tp>& __w)    \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1193:1: note: candidate template ignored: could not match 'valarray' against 'basic_ostream'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1166:5: note: expanded from macro '_DEFINE_BINARY_OPERATOR'\n    operator _Op(const valarray<_Tp>& __v,                              \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1193:1: note: candidate template ignored: could not match 'valarray<type-parameter-0-0>' against '__cling_N52::myTuple'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1177:5: note: expanded from macro '_DEFINE_BINARY_OPERATOR'\n    operator _Op(const typename valarray<_Tp>::value_type& __t,         \\\n    ^\n","output_type":"stream"},{"ename":"Interpreter Error","evalue":"","traceback":["Interpreter Error: "],"output_type":"error"}],"id":"7b80f338-bd48-4699-81aa-fbae245a3a04"},{"cell_type":"markdown","source":"Okay that obviously did not work. Problem is that ```<<``` does not know how to work with objects of type ```myTuple```.\n\n\n* We can fix this by implementing a version of ```<<``` that understands it. \n* Different from our overloading of ```[ ]``` for our ```VectorClass```, we do not do this on the object. Instead we need to overload the free-function that is ```<<```.","metadata":{},"id":"8b28c2a6-533e-478b-91e1-b63270121d6c"},{"cell_type":"code","source":"%%file myTuple.cpp\n\n#include <iostream>\n\nclass myTuple {\n\n  int fir;\n  int sec;\n\npublic:\n  myTuple( int fir, int sec ) : fir(fir), sec(sec) {};\n\n};\n\nstd::ostream& operator << ( std::ostream &os, const myTuple& tup )\n{] for\n    os << \"( \" << tup.fir << \" , \" << tup.sec << \" )\" << std::endl;\n  \n    // necessary for chaining:\n    return os;\n}","metadata":{"trusted":true},"execution_count":2,"outputs":[{"name":"stdout","text":"Overwriting myTuple.cpp\n","output_type":"stream"}],"id":"8dfa7a1c-28d8-4de1-ae19-bdb2a6d3e48e"},{"cell_type":"code","source":"!g++ -Wall -Wextra -c myTuple.cpp","metadata":{"trusted":true},"execution_count":3,"outputs":[{"name":"stdout","text":"myTuple.cpp: In function ‘std::ostream& operator<<(std::ostream&, const myTuple&)’:\nmyTuple.cpp:15:2: error: expected primary-expression before ‘]’ token\n {] for\n  ^\nmyTuple.cpp:14:62: warning: unused parameter ‘tup’ [-Wunused-parameter]\n std::ostream& operator << ( std::ostream &os, const myTuple& tup )\n                                                              ^~~\n","output_type":"stream"}],"id":"25e56a5e-6c64-4d77-9a81-5d808f2b63b3"},{"cell_type":"markdown","source":"* That was to be expected. The attributes ```fir``` and ```sec``` of ```myTuple``` are private.\n* However, we do not want to make them accessible to everyone, just for the sake of one special function/operator.\n* But, we can do so **selectively**, with the friend statement.","metadata":{},"id":"092b284c-9b78-4ea9-9b9a-f43cc5f32612"},{"cell_type":"code","source":"%%file myTuple.cpp\n\n#include <iostream>\n\nclass myTuple {\n\n  int fir;\n  int sec;\n\n  friend std::ostream& operator << ( std::ostream&, const myTuple& );\n\npublic:\n  myTuple( int fir, int sec ) : fir(fir), sec(sec) {};\n\n};\n\nstd::ostream& operator << ( std::ostream &os, const myTuple& tup )\n{\n    os << \"( \" << tup.fir << \" , \" << tup.sec << \" )\" << std::endl;\n  \n    // necessary for chaining:\n    return os;\n}\n\nint main() {\n\n  myTuple obj( 3, 5 );\n\n  std::cout << \"Pair is: \" << obj << std::endl;\n}","metadata":{"trusted":true},"execution_count":10,"outputs":[{"name":"stdout","text":"Overwriting myTuple.cpp\n","output_type":"stream"}],"id":"ff341d41-7aa2-4f87-92f2-e189dd448d68"},{"cell_type":"code","source":"!g++ -Wall -Wextra myTuple.cpp","metadata":{"trusted":true},"execution_count":11,"outputs":[],"id":"c8560f81-10ef-4203-b3f3-8046255be25a"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":12,"outputs":[{"name":"stdout","text":"Pair is: ( 3 , 5 )\n\n","output_type":"stream"}],"id":"6a5f1cb3-7891-4cb8-b8ce-d3f6aa996ecc"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"94e6f340-7a06-4c59-8889-5088e41712f2"}]}
\ No newline at end of file
+{"metadata":{"orig_nbformat":4,"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"11"},"kernelspec":{"name":"xcpp11","display_name":"C++11","language":"C++11"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Friendship","metadata":{},"id":"e04e0b83-753f-4a58-bce3-3746e7c69650"},{"cell_type":"markdown","source":"* In many situations we tend to make the data members of our classes **private** (or protected in the case of inheritance). \n* If we do so, then no objects of other classes or free-functions have access to them. Not even other objects of the same class.\n* Sometimes this is too restrictive, though. We might need to provide direct access to our members for selected **friends**.\n\n* A typical example is, when we want to overload the ```<<``` operator, to be able to place our object into an output stream.","metadata":{},"id":"6c224ee8-f4c0-495c-8261-6d2c17f845a6"},{"cell_type":"markdown","source":"***\nLet us create a simpe demo class","metadata":{},"id":"ff1a3263-e349-4840-af2f-ad90fde352ac"},{"cell_type":"code","source":"class myTuple {\n\n  int fir;\n  int sec;\n\npublic:\n  myTuple( int fir, int sec ) : fir(fir), sec(sec) {};\n\n};","metadata":{"trusted":true},"execution_count":1,"outputs":[],"id":"cebe299d-3bcc-4a57-af69-8aed41401244"},{"cell_type":"markdown","source":"and try to test-run it","metadata":{},"id":"97622c4c-2eba-47cd-8dbb-c2fe9963dbc3"},{"cell_type":"code","source":"#include <iostream>\n\nint main() {\n\n  myTuple obj( 3, 5 );\n\n  std::cout << \"Pair is: \" << obj << std::endl;\n}\n\nmain();","metadata":{"trusted":true},"execution_count":2,"outputs":[{"name":"stderr","text":"input_line_9:3:28: error: invalid operands to binary expression ('basic_ostream<char, std::char_traits<char> >' and '__cling_N52::myTuple')\n  std::cout << \"Pair is: \" << obj << std::endl;\n  ~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:245:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const void *' for 1st argument; take the address of the argument with &\n      operator<<(const void* __p)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/system_error:217:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const std::error_code' for 2nd argument\n    operator<<(basic_ostream<_CharT, _Traits>& __os, const error_code& __e)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:108:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'std::basic_ostream<char, std::char_traits<char> >::__ostream_type &(*)(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &)' (aka 'basic_ostream<char, std::char_traits<char> > &(*)(basic_ostream<char, std::char_traits<char> > &)') for 1st argument\n      operator<<(__ostream_type& (*__pf)(__ostream_type&))\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:117:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'std::basic_ostream<char, std::char_traits<char> >::__ios_type &(*)(std::basic_ostream<char, std::char_traits<char> >::__ios_type &)' (aka 'basic_ios<char, std::char_traits<char> > &(*)(basic_ios<char, std::char_traits<char> > &)') for 1st argument\n      operator<<(__ios_type& (*__pf)(__ios_type&))\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:127:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'std::ios_base &(*)(std::ios_base &)' for 1st argument\n      operator<<(ios_base& (*__pf) (ios_base&))\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:166:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'long' for 1st argument\n      operator<<(long __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:170:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'unsigned long' for 1st argument\n      operator<<(unsigned long __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:174:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'bool' for 1st argument\n      operator<<(bool __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:178:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'short' for 1st argument\n      operator<<(short __n);\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:181:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'unsigned short' for 1st argument\n      operator<<(unsigned short __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:189:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'int' for 1st argument\n      operator<<(int __n);\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:192:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'unsigned int' for 1st argument\n      operator<<(unsigned int __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:201:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'long long' for 1st argument\n      operator<<(long long __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:205:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'unsigned long long' for 1st argument\n      operator<<(unsigned long long __n)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:220:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'double' for 1st argument\n      operator<<(double __f)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:224:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'float' for 1st argument\n      operator<<(float __f)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:232:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'long double' for 1st argument\n      operator<<(long double __f)\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:276:7: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'std::basic_ostream<char, std::char_traits<char> >::__streambuf_type *' (aka 'basic_streambuf<char, std::char_traits<char> > *') for 1st argument\n      operator<<(__streambuf_type* __sb);\n      ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:511:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'char' for 2nd argument\n    operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:517:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'char' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, char __c)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:523:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'signed char' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, signed char __c)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:528:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'unsigned char' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:565:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const char *' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, const char* __s)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:578:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const signed char *' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, const signed char* __s)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:583:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const unsigned char *' for 2nd argument\n    operator<<(basic_ostream<char, _Traits>& __out, const unsigned char* __s)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/ostream.tcc:321:5: note: candidate function not viable: no known conversion from '__cling_N52::myTuple' to 'const char *' for 2nd argument\n    operator<<(basic_ostream<_CharT, _Traits>& __out, const char* __s)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:506:5: note: candidate template ignored: deduced conflicting types for parameter '_CharT' ('char' vs. '__cling_N52::myTuple')\n    operator<<(basic_ostream<_CharT, _Traits>& __out, _CharT __c)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/basic_string.h:6419:5: note: candidate template ignored: could not match 'basic_string<type-parameter-0-0, type-parameter-0-1, type-parameter-0-2>' against '__cling_N52::myTuple'\n    operator<<(basic_ostream<_CharT, _Traits>& __os,\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:548:5: note: candidate template ignored: could not match 'const _CharT *' against '__cling_N52::myTuple'\n    operator<<(basic_ostream<_CharT, _Traits>& __out, const _CharT* __s)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/ostream:691:5: note: candidate template ignored: requirement '__and_<__not_<is_lvalue_reference<basic_ostream<char> &> >, __is_convertible_to_basic_ostream<basic_ostream<char> &>, __is_insertable<__rvalue_ostream_type<basic_ostream<char> &>, const myTuple &> >::value' was not satisfied [with _Ostream = std::basic_ostream<char> &, _Tp = __cling_N52::myTuple]\n    operator<<(_Ostream&& __os, const _Tp& __x)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/shared_ptr.h:66:5: note: candidate template ignored: could not match '__shared_ptr<type-parameter-0-2, _Lp>' against '__cling_N52::myTuple'\n    operator<<(std::basic_ostream<_Ch, _Tr>& __os,\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:413:5: note: candidate template ignored: could not match '_Expr' against 'basic_ostream'\n    _DEFINE_EXPR_BINARY_OPERATOR(<<, __shift_left)\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:344:5: note: expanded from macro '_DEFINE_EXPR_BINARY_OPERATOR'\n    operator _Op(const _Expr<_Dom1, typename _Dom1::value_type>& __v,   \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:413:5: note: candidate template ignored: could not match '_Expr' against 'basic_ostream'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:357:5: note: expanded from macro '_DEFINE_EXPR_BINARY_OPERATOR'\n    operator _Op(const _Expr<_Dom, typename _Dom::value_type>& __v,     \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:413:5: note: candidate template ignored: could not match '_Expr<type-parameter-0-0, typename type-parameter-0-0::value_type>' against '__cling_N52::myTuple'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:370:5: note: expanded from macro '_DEFINE_EXPR_BINARY_OPERATOR'\n    operator _Op(const typename _Dom::value_type& __t,                  \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:413:5: note: candidate template ignored: could not match '_Expr' against 'basic_ostream'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:383:5: note: expanded from macro '_DEFINE_EXPR_BINARY_OPERATOR'\n    operator _Op(const _Expr<_Dom,typename _Dom::value_type>& __e,      \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:413:5: note: candidate template ignored: could not match '_Expr<type-parameter-0-0, typename type-parameter-0-0::value_type>' against '__cling_N52::myTuple'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/bits/valarray_after.h:396:5: note: expanded from macro '_DEFINE_EXPR_BINARY_OPERATOR'\n    operator _Op(const valarray<typename _Dom::value_type>& __v,        \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1193:1: note: candidate template ignored: could not match 'valarray' against 'basic_ostream'\n_DEFINE_BINARY_OPERATOR(<<, __shift_left)\n^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1155:5: note: expanded from macro '_DEFINE_BINARY_OPERATOR'\n    operator _Op(const valarray<_Tp>& __v, const valarray<_Tp>& __w)    \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1193:1: note: candidate template ignored: could not match 'valarray' against 'basic_ostream'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1166:5: note: expanded from macro '_DEFINE_BINARY_OPERATOR'\n    operator _Op(const valarray<_Tp>& __v,                              \\\n    ^\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1193:1: note: candidate template ignored: could not match 'valarray<type-parameter-0-0>' against '__cling_N52::myTuple'\n/srv/conda/envs/notebook/bin/../lib/gcc/../../x86_64-conda-linux-gnu/include/c++/9.4.0/valarray:1177:5: note: expanded from macro '_DEFINE_BINARY_OPERATOR'\n    operator _Op(const typename valarray<_Tp>::value_type& __t,         \\\n    ^\n","output_type":"stream"},{"ename":"Interpreter Error","evalue":"","traceback":["Interpreter Error: "],"output_type":"error"}],"id":"7b80f338-bd48-4699-81aa-fbae245a3a04"},{"cell_type":"markdown","source":"Okay that obviously did not work. Problem is that ```<<``` does not know how to work with objects of type ```myTuple```.\n\n\n* We can fix this by implementing a version of ```<<``` that understands it. \n* Different from our overloading of ```[ ]``` for our ```VectorClass```, we do not do this on the object. Instead we need to overload the free-function that is ```<<```.","metadata":{},"id":"8b28c2a6-533e-478b-91e1-b63270121d6c"},{"cell_type":"code","source":"%%file myTuple.cpp\n\n#include <iostream>\n\nclass myTuple {\n\n  int fir;\n  int sec;\n\npublic:\n  myTuple( int fir, int sec ) : fir(fir), sec(sec) {};\n\n};\n\nstd::ostream& operator << ( std::ostream &os, const myTuple& tup )\n{\n    os << \"( \" << tup.fir << \" , \" << tup.sec << \" )\";\n  \n    // necessary for chaining:\n    return os;\n}","metadata":{"trusted":true},"execution_count":3,"outputs":[{"name":"stdout","text":"Writing myTuple.cpp\n","output_type":"stream"}],"id":"8dfa7a1c-28d8-4de1-ae19-bdb2a6d3e48e"},{"cell_type":"code","source":"!g++ -Wall -Wextra -c myTuple.cpp","metadata":{"trusted":true},"execution_count":4,"outputs":[{"name":"stdout","text":"myTuple.cpp: In function ‘std::ostream& operator<<(std::ostream&, const myTuple&)’:\nmyTuple.cpp:16:23: error: ‘int myTuple::fir’ is private within this context\n     os << \"( \" << tup.fir << \" , \" << tup.sec << \" )\";\n                       ^~~\nmyTuple.cpp:6:7: note: declared private here\n   int fir;\n       ^~~\nmyTuple.cpp:16:43: error: ‘int myTuple::sec’ is private within this context\n     os << \"( \" << tup.fir << \" , \" << tup.sec << \" )\";\n                                           ^~~\nmyTuple.cpp:7:7: note: declared private here\n   int sec;\n       ^~~\n","output_type":"stream"}],"id":"25e56a5e-6c64-4d77-9a81-5d808f2b63b3"},{"cell_type":"markdown","source":"* That was to be expected. The attributes ```fir``` and ```sec``` of ```myTuple``` are private.\n* However, we do not want to make them accessible to everyone, just for the sake of one special function/operator.\n* But, we can do so **selectively**, with the friend statement.","metadata":{},"id":"092b284c-9b78-4ea9-9b9a-f43cc5f32612"},{"cell_type":"code","source":"%%file myTuple.cpp\n\n#include <iostream>\n\nclass myTuple {\n\n  int fir;\n  int sec;\n\n  friend std::ostream& operator << ( std::ostream&, const myTuple& );\n\npublic:\n  myTuple( int fir, int sec ) : fir(fir), sec(sec) {};\n\n};\n\nstd::ostream& operator << ( std::ostream &os, const myTuple& tup )\n{\n    os << \"( \" << tup.fir << \" , \" << tup.sec << \" )\" << std::endl;\n  \n    // necessary for chaining:\n    return os;\n}\n\nint main() {\n\n  myTuple obj( 3, 5 );\n\n  std::cout << \"Pair is: \" << obj << std::endl;\n}","metadata":{"trusted":true},"execution_count":5,"outputs":[{"name":"stdout","text":"Overwriting myTuple.cpp\n","output_type":"stream"}],"id":"ff341d41-7aa2-4f87-92f2-e189dd448d68"},{"cell_type":"code","source":"!g++ -Wall -Wextra myTuple.cpp","metadata":{"trusted":true},"execution_count":6,"outputs":[],"id":"c8560f81-10ef-4203-b3f3-8046255be25a"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":7,"outputs":[{"name":"stdout","text":"Pair is: ( 3 , 5 )\n\n","output_type":"stream"}],"id":"6a5f1cb3-7891-4cb8-b8ce-d3f6aa996ecc"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"94e6f340-7a06-4c59-8889-5088e41712f2"}]}
\ No newline at end of file
diff --git a/notebooks/06_STL.ipynb b/notebooks/06_STL.ipynb
index a2821c3701dfcaf1d03eb15b40ad22b18ed77070..0aed4c7d4d047249e0c7760c6e3db7108c1e32e7 100644
--- a/notebooks/06_STL.ipynb
+++ b/notebooks/06_STL.ipynb
@@ -1,1236 +1 @@
-{
- "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>\n",
-    "\n",
-    "int main() {\n",
-    "\n",
-    "    const int n = 5;\n",
-    "    \n",
-    "    std::vector< int > vec;     // need to tell compiler what we will 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 index 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": 5,
-   "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": null,
-   "id": "5d4ba36f",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "\n",
-    "\n",
-    "\n",
-    "\n",
-    "\n",
-    "\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": 8,
-   "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",
-    "  // vec.reserve( 100 );\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",
-    "  1. De-allocation of the old data-array.\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": [
-    "This will also work for our original example with the class Dummy:"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "8a69af05",
-   "metadata": {},
-   "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": 13,
-   "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_24: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": 16,
-   "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",
-    "  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",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 17,
-   "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 'one_copy' was created\n",
-      "Object 'two_copy' was created\n",
-      "Object 'three' was created\n",
-      "\n",
-      "-> Auto clean-up at program end\n",
-      "Object 'one_copy' was destroyed\n",
-      "Object 'two_copy' was destroyed\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": 38,
-   "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": 39,
-   "id": "0513b187",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!g++ -Wall -DOUT_OF_BOUNDS exception.cpp"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 40,
-   "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": 41,
-   "id": "131dd4f5",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "!g++ -Wall exception.cpp"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 42,
-   "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 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",
-    "\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": 47,
-   "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": 48,
-   "id": "d2806a4f",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "\u001b[1minput_line_59: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": 9,
-   "id": "4a6ba619",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "// %%file fubar.cpp\n",
-    "\n",
-    "#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": 10,
-   "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 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."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "ba14f11e",
-   "metadata": {},
-   "source": [
-    "Since our loop is using an iterator now, we can with two small modification switch from a **std::vector** to a **std::list**."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "id": "9f8daf26",
-   "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",
-    "    // 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": 12,
-   "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."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "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": 3,
-   "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 and even simpler way to encode a loop over a container:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "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\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": 7,
-   "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": 9,
-   "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
-}
+{"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
diff --git a/notebooks/Task#1.ipynb b/notebooks/Task#1.ipynb
index 910151f71bd0bec332f2880e17fbff40a7b203d7..8b90ba59df9a2b7df6502b58e5bafc0f937832c6 100644
--- a/notebooks/Task#1.ipynb
+++ b/notebooks/Task#1.ipynb
@@ -1 +1 @@
-{"metadata":{"orig_nbformat":4,"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"17"},"kernelspec":{"name":"xcpp17","display_name":"C++17","language":"C++17"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Task #1\n\nIn this task we want to further extend our ```VectorClass``` class. Specifically we would like add two methods and answer a question concerning ```VectorClass::print()```: \n 1. Implement missing body of VectorClass::scale()\n 1. Implement VectorClass::innerProduct(), how should the header look like?\n 1. What happens, when we call myVec.print() without arguments in main?","metadata":{},"id":"5b54f8c4-59a0-4a5a-a15c-021d20ae3d22"},{"cell_type":"markdown","source":"Let us start with (2). How should the interface for ```innerProduct()``` look like?","metadata":{},"id":"45df3877-fe76-43fd-afeb-dd4de9d969c3"},{"cell_type":"code","source":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{},"execution_count":null,"outputs":[],"id":"a217056c-feec-46b7-aaac-5d673e57c53f"},{"cell_type":"markdown","source":"- Our vectors store entries of type ```double```. Thus, that would be a fitting return type for the value of the computed Euclidean inner product. *We could hand the result back via a parameter, too. But that is probably less convenient here.*\n- We need two vectors for the operation, so the second one should be an input argument. But:\n  - Use a reference to avoid copy generation.\n  - Not going to change the vector, so that could be const.\n- Method will not change the VectorClass object itself either, so the method could be marked const, too.\n\nSo we end up with:\n\n**``` double innerProduct( const VectorClass& second ) const;```**","metadata":{},"id":"0c933e9f-554b-455c-b023-7f8e533e75dd"},{"cell_type":"markdown","source":"***\nOkay, so below is our extended ```VectorClass``` minus the actual implementation of the two methods:","metadata":{},"id":"274868db-03d2-4c69-99e2-cbedc0f31960"},{"cell_type":"code","source":"#include <iostream>\n#include <cassert>\n\nclass VectorClass{\n    \npublic:\n\n  // ------------------------------------------------\n  // User should specify dimension at object creation\n  // ------------------------------------------------\n  VectorClass( unsigned int dim ) : dim_(dim) {\n            \n    // don't allow vectors with zero dimension\n    assert( dim_ > 0 );\n\n    // allocate memory (will throw an exception, if it fails)\n    vec_ = new double[ dim_ ];\n            \n    // be talkative ;-)\n    std::cout << \"An instance of VectorClass was generated. dim_ = \" << dim_ << std::endl;\n  }\n\n  // ----------------------------------------------------------\n  // Don't allow the default constructor\n  // [prior to C++11 you would solve this by making it private]\n  // ----------------------------------------------------------\n  VectorClass() = delete;\n\n  // ----------------------------------------------------------\n  // Don't allow the following default copy constructor either    \n  // ----------------------------------------------------------\n  VectorClass( VectorClass &other ) = delete;\n \n  // ----------------------------------------------------------\n  // We need to implement a destructor to free the dynamic memory again,\n  // otherwise we easily produce memory leaks\n  // ----------------------------------------------------------\n  ~VectorClass() {\n    std::cout << \"Going to delete memory starting at address \" << vec_ << std::endl;\n          \n    delete[] vec_;\n    std::cout << \"An instance of VectorClass was destroyed and \"\n              << dim_ * sizeof(double)\n              << \" bytes freed.\" << std::endl;\n  }\n\n  // ----------------------------------------------------------\n  // provide access to the vector's dimension\n  // ----------------------------------------------------------\n  unsigned int getDim() const { return dim_; }\n    \n  // ----------------------------------------------------------\n  // overload the [] for accessing individual entries\n  // ----------------------------------------------------------\n  double& operator[] ( unsigned int index ) {\n    assert( index != 0 && index <= dim_ );\n    return vec_[index-1];\n  }\n\n  const double& operator[] ( unsigned int index ) const {\n    assert( index != 0 && index <= dim_ );\n    return vec_[index-1];\n  }\n\n  // ----------------------------------------------------------------------\n  // pretty print vector to given output stream (default will be std::cout)\n  // ----------------------------------------------------------------------\n  void print( std::ostream &os = std::cout ) const {\n    os << \"(\";\n    for( unsigned int k = 0; k < dim_-1; k++ ) {\n      os << vec_[k] << \", \";\n    }\n    os << vec_[dim_-1] << \")\" << std::endl;\n  }\n    \n  // ----------------------------\n  // scale vector with a constant\n  // ----------------------------\n  void scale( double factor );\n    \n  // ----------------------------------------------\n  // compute Euclidean inner product of two vectors\n  // ----------------------------------------------\n  double innerProduct( const VectorClass& second ) const;\n    \n  // ----------------------------------------------------------------------\n  // add to vectors together (that's actually a little bit tricky,\n  // due to the question \"where to put the result?\"\n  // for the moment leave it with adding another vector to the one on which\n  // the method is called.\n  // ----------------------------------------------------------------------\n  void add( const VectorClass& other ) {\n\n    // make sure that input vector has correct length\n    assert( other.getDim() == dim_ );\n    // for( unsigned int k = 0; k < dim_; k++ ) {\n    //     vec_[k] += other[k+1];\n    // }\n    for( unsigned int k = 1; k <= dim_; k++ ) {\n      this->operator[](k) += other[k];\n    }\n  }\n    \nprivate:\n  unsigned int dim_;   // dimension of vector\n  double* vec_;        // entries of vector\n\n};","metadata":{"trusted":true},"execution_count":1,"outputs":[],"id":"2bcf89a4-e232-47f8-afe2-a88897f13a52"},{"cell_type":"markdown","source":"***\nNow let us implement ```scale()```","metadata":{},"id":"7ab16ac5-7f91-43cc-9a68-2e8a52dd3058"},{"cell_type":"code","source":"void VectorClass::scale( double factor ) {\n \n    for( unsigned int k = 0; k < dim_; k++ ) {\n      vec_[k] *= factor;\n    }\n    \n}","metadata":{"trusted":true},"execution_count":2,"outputs":[],"id":"816a0c45-6ff9-4d67-97e7-b076bd23526d"},{"cell_type":"markdown","source":"and ```innerProduct()```","metadata":{},"id":"c84995ae-c03e-41e7-a060-f5d7b4382c55"},{"cell_type":"code","source":"double VectorClass::innerProduct( const VectorClass& second ) const {\n\n    double prod = 0.0;\n\n    for( unsigned int k = 0; k < dim_; k++ ) {\n      prod += vec_[k] * second[k+1];               // remember our overloading of [], vec_[k] = this->operator[k+1]\n    }\n\n    return prod;\n\n}   ","metadata":{"trusted":true},"execution_count":3,"outputs":[],"id":"139b925e-5b80-4ea8-80e9-ffb1ace29ffc"},{"cell_type":"markdown","source":"A question on ```scale()```: Why did we not make it **```void scale( const double factor ) const```** ?","metadata":{},"id":"0b36c822-eae1-4b20-b7ad-1ff1938b723c"},{"cell_type":"markdown","source":"\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{},"id":"80b43239-51ec-4b63-af94-ce4e59fc6cc2"},{"cell_type":"markdown","source":"There are two aspects to this:\n1. The method itself cannot be marked ```const``` as it changes the state of our object, by changing values inside ```vec_```.\n1. We could mark ```factor``` as ```const```, but\n   - It is a local variable, so changes to factor would have no effect outside the body of scale() anyhow.\n   - For a basic datatype like double we are not sure if it would allow the compiler to perform some extra optimisations.\n   - Finally it is more a question of whether you think it is worth the extra effort of marking it this way.","metadata":{},"id":"9e17a14b-096c-4abb-b806-8fedc733c9fc"},{"cell_type":"markdown","source":"***\nBelow follows our test-driver that should run through now the extension was completed","metadata":{},"id":"b44bb8cd-35c0-45d5-8c0e-ee80fed8d018"},{"cell_type":"code","source":"#include <cmath>    // for using std::sqrt() below\n\nint main() {\n\n  const unsigned int dim = 10;\n  \n  // set up 1st vector\n  VectorClass myVec( dim );\n  for( unsigned int idx = 1; idx <= dim; idx ++ ) {\n    myVec[ idx ] = static_cast<double>( dim - idx + 1 );\n  }\n  myVec.print( std::cout );\n    \n  // set up 2nd vector\n  VectorClass other( dim );\n  for( unsigned int idx = 1; idx <= dim; idx ++ ) {\n    other[ idx ] = static_cast<double>( idx );\n  }\n  other.print( std::cout );\n\n  // add the 2nd to the 1st\n  std::cout << \"\\nAdding both vectors gives: \";\n  myVec.add( other );\n  myVec.print();\n\n  // now scale second vector by 0.5\n  std::cout << \"\\nScaling the vector: \";\n  other.print();\n  std::cout << \"by 0.5 gives: \";\n  other.scale( 0.5 );\n  other.print();\n  std::cout << std::endl;\n\n  // compute the norm of the following vector\n  VectorClass vec( 2 );\n  vec[1] = 3.0;\n  vec[2] = -4.0;\n  double norm = vec.innerProduct( vec );\n  norm = std::sqrt( norm );\n  std::cout << \"Norm of vector \";\n  vec.print();\n  std::cout << \"is \" << norm << std::endl;\n    \n  std::cout << std::endl;\n  // now destructors will be called\n}","metadata":{"trusted":true},"execution_count":8,"outputs":[],"id":"6fd51fae-99a1-476c-ad9f-59529b88811e"},{"cell_type":"code","source":"main();","metadata":{"trusted":true},"execution_count":7,"outputs":[{"name":"stdout","text":"An instance of VectorClass was generated. dim_ = 10\n(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)\nAn instance of VectorClass was generated. dim_ = 10\n(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)\n\nAdding both vectors gives: (11, 11, 11, 11, 11, 11, 11, 11, 11, 11)\n\nScaling the vector: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)\nby 0.5 gives: (0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5)\n\nAn instance of VectorClass was generated. dim_ = 2\nNorm of vector (3, -4)\nis 5\n\nGoing to delete memory starting at address 0x556e84d85490\nAn instance of VectorClass was destroyed and 16 bytes freed.\nGoing to delete memory starting at address 0x556e84b3ee40\nAn instance of VectorClass was destroyed and 80 bytes freed.\nGoing to delete memory starting at address 0x556e84b446c0\nAn instance of VectorClass was destroyed and 80 bytes freed.\n","output_type":"stream"}],"id":"9c827691-afe9-4cfb-b71d-4f01bccd09c2"},{"cell_type":"markdown","source":"***\nFinally, we need to answer question (3).\n\n * In our driver we just called ```vec.print()``` without providing an output stream as argument. How did that work?\n\n * At some point we change the implementation of VectorClass::print() to have a **default value** for its os argument:  \n     **```void print( std::ostream &os = std::cout ) const```**\n     \n * Thus, if no argument is given, the compiler will automatically insert ```std::cout```.","metadata":{},"id":"067df258-3921-4328-bd14-cec60a4a02d8"},{"cell_type":"markdown","source":"#### Language Overview \n| language | optional arguments (w/ default values) | passing via keyword |\n|:---------| :-------------------------------------:|:-------------------:|\n| C        | no (only via workarounds)              | no                  |\n| C++      | yes                                    | no                  |\n| Fortran  | yes                                    | yes                 |\n| Python   | yes                                    | yes                 |\n\nPassing via keyword means, that we can set some arguments explicitely by a **name=value** pair.  \nPython example: ```print ('comma', 'separated', 'words', sep=', ')```  \n\n#### Optional Arguments in C++\n* In C++ there are some restrictions on the use of optional arguments.\n* Some of these are related to the fact that *passing via keyword* is not supported.\n* Rules are:\n  - optional arguments must follow the 'regular' ones\n  - if you do not supply a value for one optional argument, then you must also skip all arguments after that one","metadata":{},"id":"a59f1813-b205-439d-bb91-986e51df9336"},{"cell_type":"code","source":"#include <iostream>\n#include <string>\n\nvoid showVals( int a, int b = 2, double x = 1.0, std::string m = \"go\" ) {\n  std::cout << \"(\"\n            << a << \", \"\n            << b << \", \"\n            << x << \", \"\n            << m << \")\" << std::endl;\n}","metadata":{"trusted":true},"execution_count":1,"outputs":[],"id":"aa9570b3-2a56-4b23-acaa-48c28a781870"},{"cell_type":"code","source":"int main() {\n\n  showVals( 3 );\n  showVals( 5, 3 );\n  showVals( 1, 2, 3 );\n  showVals( 4, 5, 6, \"Alte Gags\" );\n\n  // showVals( 3, \"Polizei\" ); will not compile\n\n}\n\nmain();","metadata":{"trusted":true},"execution_count":4,"outputs":[{"name":"stdout","text":"(3, 2, 1, go)\n(5, 3, 1, go)\n(1, 2, 3, go)\n(4, 5, 6, Alte Gags)\n","output_type":"stream"}],"id":"ae9ceb03-fe74-4774-9d9c-b3d6d63d6710"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"c5eb79ba-2885-4de6-9f14-692c810222d5"}]}
\ No newline at end of file
+{"metadata":{"orig_nbformat":4,"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"17"},"kernelspec":{"name":"xcpp17","display_name":"C++17","language":"C++17"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Task #1\n\nIn this task we want to further extend our ```VectorClass``` class. Specifically we would like add two methods and answer a question concerning ```VectorClass::print()```: \n 1. Implement missing body of VectorClass::scale()\n 1. Implement VectorClass::innerProduct(), how should the header look like?\n 1. What happens, when we call myVec.print() without arguments in main?","metadata":{},"id":"5b54f8c4-59a0-4a5a-a15c-021d20ae3d22"},{"cell_type":"markdown","source":"Let us start with (2). How should the interface for ```innerProduct()``` look like?","metadata":{},"id":"45df3877-fe76-43fd-afeb-dd4de9d969c3"},{"cell_type":"code","source":"// Your suggestions?\n\n","metadata":{},"execution_count":null,"outputs":[],"id":"a217056c-feec-46b7-aaac-5d673e57c53f"},{"cell_type":"markdown","source":"- Our vectors store entries of type ```double```. Thus, that would be a fitting return type for the value of the computed Euclidean inner product. *We could hand the result back via a parameter, too. But that is probably less convenient here.*\n- We need two vectors for the operation, so the second one should be an input argument. But:\n  - Use a reference to avoid copy generation.\n  - Not going to change the vector, so that could be const.\n- Method will not change the VectorClass object itself either, so the method could be marked const, too.\n\nSo we end up with:\n\n**``` double innerProduct( const VectorClass& second ) const;```**","metadata":{},"id":"0c933e9f-554b-455c-b023-7f8e533e75dd"},{"cell_type":"markdown","source":"***\nOkay, so below is our extended ```VectorClass``` minus the actual implementation of the two methods:","metadata":{},"id":"274868db-03d2-4c69-99e2-cbedc0f31960"},{"cell_type":"code","source":"#include <iostream>\n#include <cassert>\n\nclass VectorClass{\n    \npublic:\n\n  // ------------------------------------------------\n  // User should specify dimension at object creation\n  // ------------------------------------------------\n  VectorClass( unsigned int dim ) : dim_(dim) {\n            \n    // don't allow vectors with zero dimension\n    assert( dim_ > 0 );\n\n    // allocate memory (will throw an exception, if it fails)\n    vec_ = new double[ dim_ ];\n            \n    // be talkative ;-)\n    std::cout << \"An instance of VectorClass was generated. dim_ = \" << dim_ << std::endl;\n  }\n\n  // ----------------------------------------------------------\n  // Don't allow the default constructor\n  // [prior to C++11 you would solve this by making it private]\n  // ----------------------------------------------------------\n  VectorClass() = delete;\n\n  // ----------------------------------------------------------\n  // Don't allow the following default copy constructor either    \n  // ----------------------------------------------------------\n  VectorClass( VectorClass &other ) = delete;\n \n  // ----------------------------------------------------------\n  // We need to implement a destructor to free the dynamic memory again,\n  // otherwise we easily produce memory leaks\n  // ----------------------------------------------------------\n  ~VectorClass() {\n    std::cout << \"Going to delete memory starting at address \" << vec_ << std::endl;\n          \n    delete[] vec_;\n    std::cout << \"An instance of VectorClass was destroyed and \"\n              << dim_ * sizeof(double)\n              << \" bytes freed.\" << std::endl;\n  }\n\n  // ----------------------------------------------------------\n  // provide access to the vector's dimension\n  // ----------------------------------------------------------\n  unsigned int getDim() const { return dim_; }\n    \n  // ----------------------------------------------------------\n  // overload the [] for accessing individual entries\n  // ----------------------------------------------------------\n  double& operator[] ( unsigned int index ) {\n    assert( index != 0 && index <= dim_ );\n    return vec_[index-1];\n  }\n\n  const double& operator[] ( unsigned int index ) const {\n    assert( index != 0 && index <= dim_ );\n    return vec_[index-1];\n  }\n\n  // ----------------------------------------------------------------------\n  // pretty print vector to given output stream (default will be std::cout)\n  // ----------------------------------------------------------------------\n  void print( std::ostream &os = std::cout ) const {\n    os << \"(\";\n    for( unsigned int k = 0; k < dim_-1; k++ ) {\n      os << vec_[k] << \", \";\n    }\n    os << vec_[dim_-1] << \")\" << std::endl;\n  }\n    \n  // ----------------------------\n  // scale vector with a constant\n  // ----------------------------\n  void scale( double factor );\n    \n  // ----------------------------------------------\n  // compute Euclidean inner product of two vectors\n  // ----------------------------------------------\n  double innerProduct( const VectorClass& second ) const;\n    \n  // ----------------------------------------------------------------------\n  // add to vectors together (that's actually a little bit tricky,\n  // due to the question \"where to put the result?\"\n  // for the moment leave it with adding another vector to the one on which\n  // the method is called.\n  // ----------------------------------------------------------------------\n  void add( const VectorClass& other ) {\n\n    // make sure that input vector has correct length\n    assert( other.getDim() == dim_ );\n    // for( unsigned int k = 0; k < dim_; k++ ) {\n    //     vec_[k] += other[k+1];\n    // }\n    for( unsigned int k = 1; k <= dim_; k++ ) {\n      this->operator[](k) += other[k];\n    }\n  }\n    \nprivate:\n  unsigned int dim_;   // dimension of vector\n  double* vec_;        // entries of vector\n\n};","metadata":{"trusted":true},"execution_count":1,"outputs":[],"id":"2bcf89a4-e232-47f8-afe2-a88897f13a52"},{"cell_type":"markdown","source":"***\nNow let us implement ```scale()```","metadata":{},"id":"7ab16ac5-7f91-43cc-9a68-2e8a52dd3058"},{"cell_type":"code","source":"void VectorClass::scale( const double factor ) {\n \n    for( unsigned int k = 0; k < dim_; k++ ) {\n      vec_[k] *= factor;\n    }\n    \n}","metadata":{"trusted":true},"execution_count":2,"outputs":[],"id":"816a0c45-6ff9-4d67-97e7-b076bd23526d"},{"cell_type":"markdown","source":"and ```innerProduct()```","metadata":{},"id":"c84995ae-c03e-41e7-a060-f5d7b4382c55"},{"cell_type":"code","source":"double VectorClass::innerProduct( const VectorClass& second ) const {\n\n    double prod = 0.0;\n\n    for( unsigned int k = 0; k < dim_; k++ ) {\n      prod += vec_[k] * second[k+1];   // remember our overloading of [], vec_[k] = this->operator[k+1]\n    }\n\n    return prod;\n\n}   ","metadata":{"trusted":true},"execution_count":3,"outputs":[],"id":"139b925e-5b80-4ea8-80e9-ffb1ace29ffc"},{"cell_type":"markdown","source":"A question on ```scale()```: Why did we not make it **```void scale( const double factor ) const```** ?","metadata":{},"id":"0b36c822-eae1-4b20-b7ad-1ff1938b723c"},{"cell_type":"markdown","source":"\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{},"id":"80b43239-51ec-4b63-af94-ce4e59fc6cc2"},{"cell_type":"markdown","source":"There are two aspects to this:\n1. The method itself cannot be marked ```const``` as it changes the state of our object, by changing values inside ```vec_```.\n1. We could mark ```factor``` as ```const```, but\n   - It is a local variable, so changes to factor would have no effect outside the body of scale() anyhow.\n   - For a basic datatype like double we are not sure if it would allow the compiler to perform some extra optimisations.\n   - Finally it is more a question of whether you think it is worth the extra effort of marking it this way.","metadata":{},"id":"9e17a14b-096c-4abb-b806-8fedc733c9fc"},{"cell_type":"markdown","source":"***\nBelow follows our test-driver that should run through now the extension was completed","metadata":{},"id":"b44bb8cd-35c0-45d5-8c0e-ee80fed8d018"},{"cell_type":"code","source":"#include <cmath>    // for using std::sqrt() below\n\nint main() {\n\n  const unsigned int dim = 10;\n  \n  // set up 1st vector\n  VectorClass myVec( dim );\n  for( unsigned int idx = 1u; idx <= dim; idx++ ) {\n    myVec[ idx ] = static_cast<double>( dim - idx + 1u );\n  }\n  myVec.print( std::cout );\n    \n  // set up 2nd vector\n  VectorClass other( dim );\n  for( unsigned int idx = 1u; idx <= dim; idx ++ ) {\n    other[ idx ] = static_cast<double>( idx );\n  }\n  other.print( std::cout );\n\n  // add the 2nd to the 1st\n  std::cout << \"\\nAdding both vectors gives: \";\n  myVec.add( other );\n  myVec.print();\n\n  // now scale second vector by 0.5\n  std::cout << \"\\nScaling the vector: \";\n  other.print();\n  std::cout << \"by 0.5 gives: \";\n  other.scale( 0.5 );\n  other.print();\n  std::cout << std::endl;\n\n  // compute the norm of the following vector\n  VectorClass vec( 2 );\n  vec[1] = 3.0;\n  vec[2] = -4.0;\n  double norm = vec.innerProduct( vec );\n  norm = std::sqrt( norm );\n  std::cout << \"Norm of vector \" << vec << \"is \" << norm << std::endl;\n  vec.print();\n  std::cout << \"is \" << norm << std::endl;\n    \n  std::cout << std::endl;\n  // now destructors will be called\n}","metadata":{"trusted":true},"execution_count":4,"outputs":[],"id":"6fd51fae-99a1-476c-ad9f-59529b88811e"},{"cell_type":"code","source":"main();","metadata":{"trusted":true},"execution_count":5,"outputs":[{"name":"stdout","text":"An instance of VectorClass was generated. dim_ = 10\n(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)\nAn instance of VectorClass was generated. dim_ = 10\n(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)\n\nAdding both vectors gives: (11, 11, 11, 11, 11, 11, 11, 11, 11, 11)\n\nScaling the vector: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)\nby 0.5 gives: (0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5)\n\nAn instance of VectorClass was generated. dim_ = 2\nNorm of vector (3, -4)\nis 5\n\nGoing to delete memory starting at address 0x55e6cfd4e580\nAn instance of VectorClass was destroyed and 16 bytes freed.\nGoing to delete memory starting at address 0x55e6d09bdf70\nAn instance of VectorClass was destroyed and 80 bytes freed.\nGoing to delete memory starting at address 0x55e6d07d8a60\nAn instance of VectorClass was destroyed and 80 bytes freed.\n","output_type":"stream"}],"id":"9c827691-afe9-4cfb-b71d-4f01bccd09c2"},{"cell_type":"markdown","source":"***\nFinally, we need to answer question (3).\n\n * In our driver we just called ```vec.print()``` without providing an output stream as argument. How did that work?\n\n * At some point we change the implementation of VectorClass::print() to have a **default value** for its os argument:  \n     **```void print( std::ostream &os = std::cout ) const```**\n     \n * Thus, if no argument is given, the compiler will automatically insert ```std::cout```.","metadata":{},"id":"067df258-3921-4328-bd14-cec60a4a02d8"},{"cell_type":"markdown","source":"#### Language Overview \n| language | optional arguments (w/ default values) | passing via keyword |\n|:---------| :-------------------------------------:|:-------------------:|\n| C        | no (only via workarounds)              | no                  |\n| C++      | yes                                    | no                  |\n| Fortran  | yes                                    | yes                 |\n| Python   | yes                                    | yes                 |\n\nPassing via keyword means, that we can set some arguments explicitely by a **name=value** pair.  \nPython example: ```print ('comma', 'separated', 'words', sep=', ')```  \n\n#### Optional Arguments in C++\n* In C++ there are some restrictions on the use of optional arguments.\n* Some of these are related to the fact that *passing via keyword* is not supported.\n* Rules are:\n  - optional arguments must follow the 'regular' ones\n  - if you do not supply a value for one optional argument, then you must also skip all arguments after that one","metadata":{},"id":"a59f1813-b205-439d-bb91-986e51df9336"},{"cell_type":"code","source":"#include <iostream>\n#include <string>\n\nvoid showVals( int a, int b = 2, double x = 1.0, std::string m = \"go\" ) {\n  std::cout << \"(\"\n            << a << \", \"\n            << b << \", \"\n            << x << \", \"\n            << m << \")\" << std::endl;\n}","metadata":{"trusted":true},"execution_count":7,"outputs":[],"id":"aa9570b3-2a56-4b23-acaa-48c28a781870"},{"cell_type":"code","source":"int main() {\n\n  showVals( 3 );\n  showVals( 5, 3 );\n  showVals( 1, 2, 3.0 );\n  showVals( 4, 5, 6, \"Alte Gags\" );\n\n  // showVals( 3, \"Polizei\" ); will not compile\n\n}\n\nmain();","metadata":{"trusted":true},"execution_count":11,"outputs":[{"name":"stdout","text":"(3, 2, 1, go)\n(5, 3, 1, go)\n(1, 2, 3, go)\n(4, 5, 6, Alte Gags)\n","output_type":"stream"}],"id":"ae9ceb03-fe74-4774-9d9c-b3d6d63d6710"},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[],"id":"c5eb79ba-2885-4de6-9f14-692c810222d5"}]}
\ No newline at end of file