diff --git a/notebooks/intro_algorithms.ipynb b/notebooks/intro_algorithms.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..1b8b1d6e0db11f39f9325ad3da73add58a8249f0
--- /dev/null
+++ b/notebooks/intro_algorithms.ipynb
@@ -0,0 +1,279 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Operations on sequences with STL algorithms\n",
+    "\n",
+    "The following example demonstrates how to create and manipulate sequences using the algorithms in the header \"algorithm\". Try to predict the output when you execute the code in each of the cells below. Then execute the cell (Ctrl+Enter) and examine output, try to understand what the different algorithms do."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "#include <vector>\n",
+    "#include <algorithm>\n",
+    "using namespace std;"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "vector v{1, 2, 3, 4, 5, 6, 7, 8, 9}, w{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};\n",
+    "vector<int> x, y, z, m;"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "if (is_sorted(begin(v), end(v)))\n",
+    "    cout << \"The sequence is sorted in increasing order.\\n\";"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "reverse(v.begin(), v.end());\n",
+    "v"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "v"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "rotate(v.begin(), v.begin()+3, v.end());\n",
+    "v"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "sort(begin(v), end(v));\n",
+    "v"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "merge(v.begin(), v.end(), w.begin(), w.end(), back_inserter(m));\n",
+    "m"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "set_union(begin(v), end(v), begin(w), end(w), back_inserter(x));\n",
+    "x"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "set_intersection(w.begin(), w.end(), v.begin(), v.end(), back_inserter(y));\n",
+    "y"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "set_symmetric_difference(v.begin(), v.end(), w.begin(), w.end(), back_inserter(z));\n",
+    "z"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "if (is_permutation(z.begin(), z.end(), v.begin(), v.end()))\n",
+    "    cout << \"z is a permutation of v\\n\";\n",
+    "else\n",
+    "    cout << \"z is not a permutation of v\\n\";"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Part 2:\n",
+    "\n",
+    "Many algorithms accept one or more \"callable\" objects as arguments. This could be a simple function as shown here ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "void print_it(int x) { std::cout << x << \"\\n\"; }\n",
+    "for_each(begin(v), end(v), print_it);"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The callable object could also be a class with an overloaded `operator()` ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "struct show_squared {\n",
+    "    void operator()(int x) const noexcept { std::cout << x * x << \"\\n\"; }\n",
+    "};\n",
+    "for_each(begin(v), end(v), show_squared{});"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for_each(begin(v), end(v), show_squared{});"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Alternatively, one could use a lambda function ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for_each(begin(v), end(v), [](int x){ std::cout << x * x << \"\\n\"; });"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "y.clear();\n",
+    "copy(x.begin(), x.end(), back_inserter(y));\n",
+    "y"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "z.clear();\n",
+    "copy_if(x.begin(), x.end(), back_inserter(z), [](auto i){ return (i % 7) < 3; });\n",
+    "z"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "z.clear();\n",
+    "set_symmetric_difference(begin(v), end(v), begin(w), end(w), back_inserter(z));\n",
+    "z"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "auto doubledigitsstart = find_if(begin(z), end(z), [](auto i){ return i > 9 or i < -9; });\n",
+    "vector<int> filtered;\n",
+    "copy(doubledigitsstart, end(z), back_inserter(filtered));\n",
+    "filtered"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "x = v;\n",
+    "reverse(x.begin(), x.end());\n",
+    "y.clear();\n",
+    "transform(begin(x), end(x), begin(v), back_inserter(y), [](auto i, auto j){ return i+j; });\n",
+    "y"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "C++17",
+   "language": "C++",
+   "name": "cling-cpp17"
+  },
+  "language_info": {
+   "codemirror_mode": "c++",
+   "file_extension": ".c++",
+   "mimetype": "text/x-c++src",
+   "name": "c++"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/notebooks/lambda_practice_0.ipynb b/notebooks/lambda_practice_0.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..2f2ff1e64f3494eddbf753828170d4fe0f83e25e
--- /dev/null
+++ b/notebooks/lambda_practice_0.ipynb
@@ -0,0 +1,248 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "5b454048",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "#include <algorithm>\n",
+    "#include <vector>\n",
+    "#include <array>\n",
+    "#include <cmath>\n",
+    "#include <complex>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9541e5f1",
+   "metadata": {},
+   "source": [
+    "# Practice using lambdas with algorithms"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "eb5b5448",
+   "metadata": {},
+   "source": [
+    "## std::for_each"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4d3f33ef",
+   "metadata": {},
+   "source": [
+    "In the following cell, we scale the elements of a vector by $1.5$ and print them. In a separate cell, use the algorithm `std::for_each` along with an appropriate lambda expression replace the values by the $sin(value)$, and\n",
+    "show the results."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "99bbcf10",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "{\n",
+    "    std::vector X{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.8, 0.7};\n",
+    "    std::for_each(X.begin(), X.end(), [](auto& elem){ elem *= 1.5; });\n",
+    "    std::for_each(X.begin(), X.end(), [](auto&& elem){ std::cout << elem << \"\\n\"; });\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "fd0e92e9",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c2172ca4",
+   "metadata": {},
+   "source": [
+    "## std::transform\n",
+    "\n",
+    "Using the slides as a guide, fill in the necessary code, so that the second sequence consists of the values $a x^2 + b x + c$, for $x$ taken from the first sequence, $a$ and $b$ being constants."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8fb70ac5",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "{\n",
+    "    std::vector X{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.8, 0.7};\n",
+    "    std::vector<double> Y;\n",
+    "    const auto a = 2.7182818284590;\n",
+    "    const auto b = 3.1415926535897;\n",
+    "    // std::transform(what to what using what data transformation ?);\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7f0597d6",
+   "metadata": {},
+   "source": [
+    "## std::copy_if\n",
+    "\n",
+    "Using the slides as a guild, write the necessary code to \"filter\" into the vector `Y` only those values out of the input vector `X`, for which the the cylindrical Bessel function of the first kind, $J_0(x) > 0.9$. Cylindrical Bessel function of the first kind $J_0(x)$ are available in the C++ standard library as `std::cyl_bessel_j(int, double)`.  "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "07defab8",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "{\n",
+    "    std::vector X{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.8, 0.7};\n",
+    "    std::vector<double> Y;\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fc56123b",
+   "metadata": {},
+   "source": [
+    "## std::partition\n",
+    "\n",
+    "The algorithm `std::partition` takes \n",
+    "\n",
+    "    - the bounds of one sequence (as start and end iterators)\n",
+    "    - a unary predicate callable entity (something that can be called with one parameter, and returns true or false)\n",
+    "\n",
+    "and reorders the elements in the range so that all the elements which test true come before all elements which test false. The return value of the function is an iterator marking the partition point:\n",
+    "\n",
+    "```c++\n",
+    "auto p = std::partition(start, stop, criterion);\n",
+    "```\n",
+    "\n",
+    "The range `start` to `p` will then contain all elements which \"pass\" and `p` to `stop` will contain all elements which don't pass. Partition the range in the previous exercise using the $J_0(x)$ function and the selection criteria we used. Print the two sub ranges, along with the values of the Bessel function to show that `partition` did what is expected of it."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "164bd972",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "{\n",
+    "    std::vector X{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.8, 0.7};\n",
+    "    auto p = std::partition(X.begin(), X.end(), [](auto x){ return std::cyl_bessel_j(0, x) > 0.9; });\n",
+    "    std::cout << \"Passed...\\n\";\n",
+    "    std::for_each(X.begin(), p, [](auto&& x){ std::cout << x << \"\\t\" << std::cyl_bessel_j(0, x) << \"\\n\"; });\n",
+    "    std::cout << \"Did not pass.\\n\";\n",
+    "    std::for_each(p, X.end(), [](auto&& x){ std::cout << x << \"\\t\" << std::cyl_bessel_j(0, x) << \"\\n\"; });\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c9467bb9",
+   "metadata": {},
+   "source": [
+    "## std::sort\n",
+    "\n",
+    "Any container containing elements for which a \"less than\" comparison $x_i < x_j$ makes sense can be sorted. For instance, for integers, real numbers, strings etc. $a < b$ makes sense. But for complex numbers, for instance, such a comparison can not be uniquely defined. `std::sort(begin, end)` will sort any container in increasing order, so long as the elements can be compared to determine their relative ordering. Test:  "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "cfcc07df",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "{\n",
+    "    std::vector X{1,5,3,8,9,2,7,2,6,3,1,0,8,4};\n",
+    "    std::sort(X.begin(), X.end());\n",
+    "    for (auto elem : X) std::cout << elem << \"\\n\";\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "fc3ffce8",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "{\n",
+    "    std::vector<std::string> X{\"Conditionally\", \"copies\", \"elements\", \"from\", \"a\", \"source\", \"sequence\", \"to\", \n",
+    "                 \"a\", \"destination\", \"sequence\"};\n",
+    "    // Fun exercise for those who feel adventurous: Try using CTAD in defining this vector,\n",
+    "    // and explain the results. What would you have to do to correctly use CTAD in this case ?\n",
+    "    std::sort(X.begin(), X.end());\n",
+    "    for (auto elem : X) std::cout << elem << \"\\n\";\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9a391f89",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "{\n",
+    "    // But this does not work. If you run this, you may have restart the kernel!\n",
+    "    std::vector<std::complex<double>> X{{1., 3.}, {0.99, 3.14}, {1.22, 0.12}};\n",
+    "    std::sort(X.begin(), X.end(), [](auto x, auto y){ return std::abs(x) < std::abs(y); });\n",
+    "    for (auto&& elem : X) std::cout << elem << \"\\n\";\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6f7badd2",
+   "metadata": {},
+   "source": [
+    "However, `std::sort` can accept a callable object as another parameter, after the range parameters, which specifies how the contents of the range should be compared. It should be a function which takes two objects of the value type of the container, $x$ and $y$ (for instance), and return true if $x$ should be placed before $y$ for the current sorting operation. For instance, we can say that complex numbers should be sorted in such a way that the numbers with larger absolute value follow those with smaller absolute value. This can be expressed as $\\lambda(x,y) = |x| < |y|$. Write the appropriate lambda expression as the 3rd argument to sort above and verify that you can sort by this criterion. Absolute values of complex number `c` can be calculated as `std::abs(c)`. Complex numbers are known to `std::cout`."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "661c8dd9",
+   "metadata": {},
+   "source": [
+    "How would you now sort an array of complex numbers by their real parts ? (Principle of least surprises applies here: `std::real(c)`, `std::imag(c)` ...)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "881f5069",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "C++17",
+   "language": "C++",
+   "name": "cling-cpp17"
+  },
+  "language_info": {
+   "codemirror_mode": "c++",
+   "file_extension": ".c++",
+   "mimetype": "text/x-c++src",
+   "name": "c++"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/notebooks/lambda_practice_1.ipynb b/notebooks/lambda_practice_1.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..716efb4e3eb25c304a0db2342e437ea02e8a3b87
--- /dev/null
+++ b/notebooks/lambda_practice_1.ipynb
@@ -0,0 +1,342 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "e7b04ecc",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": []
+     },
+     "execution_count": 1,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "#pragma cling add_include_path(\"/p/project/training2213/local/include\")\n",
+    "#include <iostream>\n",
+    "#include <vector>\n",
+    "#include <algorithm>\n",
+    "#include <boost/type_index.hpp>\n",
+    "#include \"Vbose.hh\"\n",
+    "template <class T>\n",
+    "void typeof(T&& t) {\n",
+    "    std::cout << \"Type name: \" << boost::typeindex::type_id_with_cvr<T>().pretty_name() << \"\\n\";\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9d7b6595",
+   "metadata": {},
+   "source": [
+    "# Lambda functions: captures\n",
+    "\n",
+    "## When\n",
+    "\n",
+    "Variables which can be used inside lambda functions are those declared inside the lambda function, its formal arguments, global variables and variables captured by the capture brackets. Variables captured with `=` captures are copied into the lambda function. When does the copy take place ? Immediately when the lambda is declared or at the point when it is used ? How often does the copy happen, if we declare it once, but use it in a loop two million times ? Does `[=]` capture copy every variable defined in the context of the lambda function in to the lambda ? We will use the `Vbose` class to explore..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "1e7328f5",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Constructor of object at 140514652397128, using string \"dinosaur\"\n",
+      "Constructor of object at 140514652397040, using string \"fish\"\n",
+      "Declaring lambda function {\n",
+      "Copy constructor of object at 140514652396968. Source for copy is at 140514652397128\n",
+      "Declaring lambda function }\n",
+      "Changing internal value of object at 140514652397128 from dinosaur to bird\n",
+      "Calling lambda function {\n",
+      "5 -> \n",
+      "In lambda function, captured value of locvar is : dinosaur\n",
+      "125\n",
+      "Calling lambda function }\n",
+      "Calling lambda function {\n",
+      "5 -> \n",
+      "In lambda function, captured value of locvar is : dinosaur\n",
+      "125\n",
+      "Calling lambda function }\n",
+      "Calling lambda function {\n",
+      "5 -> \n",
+      "In lambda function, captured value of locvar is : dinosaur\n",
+      "125\n",
+      "Calling lambda function }\n",
+      "Calling lambda function {\n",
+      "5 -> \n",
+      "In lambda function, captured value of locvar is : dinosaur\n",
+      "125\n",
+      "Calling lambda function }\n",
+      "Calling lambda function {\n",
+      "5 -> \n",
+      "In lambda function, captured value of locvar is : dinosaur\n",
+      "125\n",
+      "Calling lambda function }\n",
+      "Destructor of object at 140514652396968 with data \"dinosaur\"\n",
+      "Destructor of object at 140514652397040 with data \"fish\"\n",
+      "Destructor of object at 140514652397128 with data \"bird\"\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": []
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "{\n",
+    "    Vbose locvar{ \"dinosaur\" }, anothervar{\"fish\"};\n",
+    "    std::cout << \"Declaring lambda function {\\n\";\n",
+    "    auto lambda = [=](int i) {\n",
+    "        std::cout << \"\\nIn lambda function, captured value of locvar is : \"\n",
+    "                  << locvar.getval() << \"\\n\";\n",
+    "        return i * i * i;\n",
+    "    };\n",
+    "    std::cout << \"Declaring lambda function }\\n\";\n",
+    "\n",
+    "    locvar.value(\"bird\");\n",
+    "    for (int i = 0; i < 5; ++i) {\n",
+    "        std::cout << \"Calling lambda function {\\n\";\n",
+    "        std::cout << 5 << \" -> \" << lambda(5) << \"\\n\";\n",
+    "        std::cout << \"Calling lambda function }\\n\";        \n",
+    "    }\n",
+    "}\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "04c99e3d",
+   "metadata": {},
+   "source": [
+    "How often did the copy operation for the captured variables occur inside the loop ? How did the change of the variable `locvar` after the declaration of the lambda reflect itself inside the lambda function, when it was called in the loop ? How do these results change if you use reference capture rather than value capture ?"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "79d13085",
+   "metadata": {},
+   "source": [
+    "In the following, we examine the effects of the `mutable` keyword for lambdas. The callable objects created when the lambda expression is evaluated are by default `const`. Imagine that the compiler is automatically generating a class with an overloaded function call operator, `operator()`. The captured variables are the arguments given to the constructor. By default, the `operator()` is `const` qualified, so that the callable object may not change state, i.e., the internal copies of the captured variables it creates in its constructor can not change when the lambda is used. However, declaring the lambda as `mutable` creates a non-constant function call operator. In this case, if any of the captured values are changed inside the lambda function call, those changes survive from call to call.\n",
+    "\n",
+    "Mutable lambdas are also often used in connection with init-captures. We declare a new variable in the capture bracket, using an expression to initialize it. For instance `[i = 0UL]()mutable {}` makes a variable `i` available inside the lambda, but that variable is not something that was present in the surrounding scope, but was created along with the lambda, in its constructor. It is as if the variable was being declared with an `auto i = 0UL`. We can not drop the `auto` for variable declarations anywhere in C++, except in lambda capture brackets. This is because the capture brackets define all the parameters to be given to the constructor of the lambda: so it is a context where there can only be declarations. It was therefore formulated in this way."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "5884e24d",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Outside Lambda, S= Some string\n",
+      "Inside Lambda, S = 1\n",
+      "Outside Lambda, S= Some string\n",
+      "Inside Lambda, S = 2\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": []
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "{\n",
+    "    std::string S{\"Some string\"};\n",
+    "    auto L = [S=1UL]()mutable { std::cout << \"Inside Lambda, S = \" << S << \"\\n\"; ++S; };\n",
+    "    std::cout << \"Outside Lambda, S= \" << S << \"\\n\";\n",
+    "    L();\n",
+    "    std::cout << \"Outside Lambda, S= \" << S << \"\\n\";\n",
+    "    L();\n",
+    "    // You can understand the output by noting that despite the name S appearing in the capture\n",
+    "    // bracket, the S was \"init captured\" from an unsigned long. It has no connection whatsoever\n",
+    "    // with the variable S outside.\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6135039c",
+   "metadata": {},
+   "source": [
+    "A popular application for mutable lambdas is generator functions. The algorithm `std::generate` takes the bounds of a sequence and a callable object of no input parameters (expected to be called just as `func()` with no inputs). A similar algorithm, `std::generate_n` accepts the start of the sequence, the number of elements it contains and a callable object. The generate and assign values for the sequence elements by calling the callable object once for each of them. Here is an example. We initialize a vector to the squares of positive integers. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "c9daa776",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      " v = \n",
+      "1\n",
+      "4\n",
+      "9\n",
+      "16\n",
+      "25\n",
+      "36\n",
+      "49\n",
+      "64\n",
+      "81\n",
+      "100\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": []
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "{\n",
+    "    using namespace std;\n",
+    "    vector<unsigned long> v, w;\n",
+    "    generate_n(back_inserter(v), 10, [i = 0UL]() mutable {\n",
+    "        ++i;\n",
+    "        return i * i;\n",
+    "    });\n",
+    "    // v = [1, 4, 9, 16 ... ]\n",
+    "    std::cout << \" v = \\n\";\n",
+    "    for (auto el : v)\n",
+    "        std::cout << el << \"\\n\";\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f882cb25",
+   "metadata": {},
+   "source": [
+    "And in the following we do the same, but initialize a vector with the first 50 Fibonacci numbers..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "7cec5bf6",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      " w = \n",
+      "1\n",
+      "1\n",
+      "2\n",
+      "3\n",
+      "5\n",
+      "8\n",
+      "13\n",
+      "21\n",
+      "34\n",
+      "55\n",
+      "89\n",
+      "144\n",
+      "233\n",
+      "377\n",
+      "610\n",
+      "987\n",
+      "1597\n",
+      "2584\n",
+      "4181\n",
+      "6765\n",
+      "10946\n",
+      "17711\n",
+      "28657\n",
+      "46368\n",
+      "75025\n",
+      "121393\n",
+      "196418\n",
+      "317811\n",
+      "514229\n",
+      "832040\n",
+      "1346269\n",
+      "2178309\n",
+      "3524578\n",
+      "5702887\n",
+      "9227465\n",
+      "14930352\n",
+      "24157817\n",
+      "39088169\n",
+      "63245986\n",
+      "102334155\n",
+      "165580141\n",
+      "267914296\n",
+      "433494437\n",
+      "701408733\n",
+      "1134903170\n",
+      "1836311903\n",
+      "2971215073\n",
+      "4807526976\n",
+      "7778742049\n",
+      "12586269025\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": []
+     },
+     "execution_count": 5,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "{\n",
+    "    using namespace std;\n",
+    "    vector<unsigned long> v, w;\n",
+    "    generate_n(back_inserter(w), 50, [ i = 0UL, j = 1UL ]() mutable {\n",
+    "        i = std::exchange(j, j + i);\n",
+    "        return i;\n",
+    "    });\n",
+    "    // w = [1, 1, 2, 3, 5, 8, 11 ...]\n",
+    "\n",
+    "    std::cout << \" w = \\n\";\n",
+    "    for (auto el : w)\n",
+    "        std::cout << el << \"\\n\";\n",
+    "}"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "C++17",
+   "language": "C++",
+   "name": "cling-cpp17"
+  },
+  "language_info": {
+   "codemirror_mode": "c++",
+   "file_extension": ".c++",
+   "mimetype": "text/x-c++src",
+   "name": "c++"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}