diff --git a/1-ProceduralProgramming/3-Types.ipynb b/1-ProceduralProgramming/3-Types.ipynb
index 7534c528867631c352b38cde16260a6c43622faa..5253a6ca21fff3a69b5c70e06be33f7f2160d78d 100644
--- a/1-ProceduralProgramming/3-Types.ipynb
+++ b/1-ProceduralProgramming/3-Types.ipynb
@@ -422,7 +422,7 @@
    "source": [
     "#### Conversions between digital types\n",
     "\n",
-    "[Earlier](/notebooks/1-ProceduralProgramming/1-Variables.ipynb#Initialisation) I indicated there were small differences between the three initialization methods, that could be ignored most of the time. \n",
+    "[Earlier](/notebooks/1-ProceduralProgramming/1-Variables.ipynb#Initialisation) we indicated there were small differences between the three initialization methods, that could be ignored most of the time. \n",
     "\n",
     "The difference is related to implicit conversion: both historical initialization methods are ok with implicit conversion __with accuracy loss__:"
    ]
@@ -436,15 +436,14 @@
     "#include <iostream>\n",
     "#include <iomanip>\n",
     "{\n",
-    "    float f = 1.1234567890123;\n",
-    "    double d = 2.1234567890123;\n",
-    "    float f_d(d);\n",
-    "    float f_dd = d;\n",
+    "    float f = 1.123456789;\n",
+    "    double d = 2.123456789;\n",
+    "    float f_d(d); // Could also have been written: float f_d = d;\n",
     "\n",
     "    std::cout << \"A double may print around 15 significant digits, d = \" << std::setprecision(16) << d << std::endl;\n",
     "    std::cout << \"A float may print around 7 significant digits, f_d = \" << std::setprecision(16) << f_d << \" so we see here the value was altered from the initial double value.\" <<  std::endl;\n",
     "\n",
-    "    std::cout << \"Even without conversion involved there is an accuracy loss: f = \" << std::setprecision(16) << f << std::endl;\n",
+    "    std::cout << \"Even without conversion involved there is an accuracy loss if we require too high a precision: f = \" << std::setprecision(16) << f << std::endl;\n",
     "    \n",
     "}"
    ]
@@ -453,7 +452,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "whereas C++ 11 introduced initialization with braces isn't:"
+    "whereas C++ 11 introduced initialization with braces isn't, even if the initial number could fit in the target conversion type:"
    ]
   },
   {
@@ -463,7 +462,7 @@
    "outputs": [],
    "source": [
     "{\n",
-    "    double d = 2.12345678901234567890;\n",
+    "    double d = 2.12345;\n",
     "    float f_d{d}; // COMPILATION ERROR\n",
     "}"
    ]
@@ -472,7 +471,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "This is really related to **accuracy loss**: initialization with braces is ok if there are none:"
+    "This is really related to **accuracy loss**: initialization with braces is ok only if there is no such loss:"
    ]
   },
   {
@@ -482,7 +481,7 @@
    "outputs": [],
    "source": [
     "{\n",
-    "    float f = 1.12345678901234567890;\n",
+    "    float f = 1.123456;\n",
     "    double d_f { f }; // OK\n",
     "}"
    ]
@@ -626,7 +625,66 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Please note that the best would be to explicitly cast both values, to avoid useless implicit conversion."
+    "Please note that the best would be to **explicitly cast both values**, to avoid useless implicit conversion."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "{\n",
+    "    double a = 2. / 3.;\n",
+    "    std::cout << a << std::endl;\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Underlying types of `enum class`\n",
+    "\n",
+    "In fact, under the hood `enum class` are storing `int` values by default:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "\n",
+    "enum class arbitrary_enum_class { yes, no }; // with scoped enum I can choose any value without fearing naming conflict!\n",
+    "\n",
+    "// Don't bother if you're lost with `std::is_same_v<int, std::underlying_type_t<int_enum>>` - we'll cover that later in part 5.\n",
+    "// You just have to know here it checks whether underlying type behind enum is an `int` or not.\n",
+    "std::cout << \"Check underlying type behind `arbitrary_enum_class` is `int`: \" << std::boolalpha << std::is_same_v<int, std::underlying_type_t<arbitrary_enum_class>> << '\\n';"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "However it is possible to choose another type by specifying it explicitly after a colon:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "enum class altitude : char // telling here explicitly to use `char` as underlying type\n",
+    "{\n",
+    "    high = 'h',\n",
+    "    low = 'l'\n",
+    "}; \n",
+    "\n",
+    "std::cout << \"Check underlying type behind `altitude` is not `int`: \" << std::boolalpha << std::is_same_v<int, std::underlying_type_t<altitude>> << '\\n';\n",
+    "std::cout << \"Check underlying type behind `altitude` is `char`: \" << std::boolalpha << std::is_same_v<char, std::underlying_type_t<altitude>> << '\\n';"
    ]
   },
   {
@@ -679,7 +737,31 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "There are several other functions related to historical strings; for more information, do not hesitate to consult [this reference page](http://www.cplusplus.com/reference/cstring/)."
+    "There are several other functions related to historical strings; for more information, do not hesitate to consult [this reference page](http://www.cplusplus.com/reference/cstring/).\n",
+    "\n",
+    "#### Drawbacks of historical strings\n",
+    "\n",
+    "- Keeping track properly and not forgetting `\\0` gets old really fast and the outcome is usually not user-friendly...\n",
+    "- Assignment doesn't work as you would expect:\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "\n",
+    "// Define hello, and copy it\n",
+    "char hello[] = {'h','e','l','l','o', '\\0'};\n",
+    "const char* copy = hello;\n",
+    "\n",
+    "// Modify hello\n",
+    "hello[0] = '!';\n",
+    "\n",
+    "// `copy` is modified too!\n",
+    "std::cout << \"Value of 'copy' is not the 'hello' you probably wanted: \" << copy << std::endl;"
    ]
   },
   {
@@ -715,7 +797,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "copy = hello; // please notice assignment is much more straightforward\n",
+    "copy = hello; // please notice assignment is much more straightforward!\n",
+    "hello = \"hi\";\n",
     "std::cout << \"String '\" << copy << \"' is \" << copy.length() << \" characters long.\" << std::endl;"
    ]
   },
@@ -725,8 +808,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "const char* copy_str = copy.data();  // Returns a classic C-string (from C++11 onward)\n",
-    "std::cout << \"String '\" << copy_str << \"' is \" << strlen(copy_str) << \" characters long.\" << std::endl;"
+    "std::string dynamic {\"dynamic\"};\n",
+    "std::cout << \"String '\" << dynamic << \"' is \" << dynamic.length() << \" characters long.\" << std::endl;\n",
+    "\n",
+    "dynamic += \" string\";\n",
+    "std::cout << \"String '\" << dynamic << \"' is \" << dynamic.length() << \" characters long.\" << std::endl;"
    ]
   },
   {
@@ -735,18 +821,15 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "const char* old_copy_str = &copy[0];  // Same before C++11... \n",
-    "std::cout << \"String '\" << old_copy_str << \"' is \" << strlen(old_copy_str) << \" characters long.\" << std::endl;"
+    "dynamic = \"std::string is dynamical and flexible\";\n",
+    "std::cout << \"String '\" << dynamic << \"' is \" << dynamic.length() << \" characters long.\" << std::endl;"
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": null,
+   "cell_type": "markdown",
    "metadata": {},
-   "outputs": [],
    "source": [
-    "std::string dynamic {\"dynamic std::string\"};\n",
-    "std::cout << \"String '\" << dynamic << \"' is \" << dynamic.length() << \" characters long.\" << std::endl;"
+    "If needed (for instance to interact with a C library) you may access to the underlying table with `c_str()` or `data()`:"
    ]
   },
   {
@@ -755,15 +838,24 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dynamic = \"std::string is dynamical and flexible\";\n",
-    "std::cout << \"String '\" << dynamic << \"' is \" << dynamic.length() << \" characters long.\" << std::endl;"
+    "#include <string>\n",
+    "{\n",
+    "    std::string cplusplus_string(\"C++ string!\");\n",
+    "    \n",
+    "    const char* c_string = cplusplus_string.c_str();\n",
+    "    const char* c_string_2 = cplusplus_string.data();\n",
+    "}"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "If needed (for instance to interact with a C library) you may access to the underlying table with `c_str()` or `data()` (both are interchangeable):"
+    "The `const` here is important: you may access the content but should not modify it; this functionality is provided for read-only access.\n",
+    "\n",
+    "Both are interchangeable; `data()` was introduced in C++ 11 but is more generic as other containers also define it - we'll cover that in part 5.\n",
+    "\n",
+    "As the principle to \"convert\" to C string is to return the address of the first character, you may also meet in some legacy codes:"
    ]
   },
   {
@@ -776,8 +868,7 @@
     "{\n",
     "    std::string cplusplus_string(\"C++ string!\");\n",
     "    \n",
-    "    const char* c_string = cplusplus_string.c_str();\n",
-    "    const char* c_string_2 = cplusplus_string.data();\n",
+    "    const char* c_string = &cplusplus_string[0];    \n",
     "}"
    ]
   },
@@ -785,14 +876,14 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "The `const` here is important: you may access the content but should not modify it; this functionality is provided for read-only access."
+    "which tells to litteraly take address of the first element."
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "FYI, C++17 introduced [std::string_view](https://en.cppreference.com/w/cpp/header/string_view) which is more efficient than `std::string` for some operations (it is presented [in appendix](../7-Appendix/StringView.ipynb) but if it's your first reading it's a bit early to tackle it now)."
+    "FYI, C++17 introduced [std::string_view](https://en.cppreference.com/w/cpp/header/string_view) which is more efficient than `std::string` for some read-only operations (it is presented [in appendix](../7-Appendix/StringView.ipynb) but if it's your first reading it's a bit early to tackle it now)."
    ]
   },
   {
diff --git a/1-ProceduralProgramming/4-Functions.ipynb b/1-ProceduralProgramming/4-Functions.ipynb
index 18386df91dff21174f3625fead51d30599a2fa2a..1e37bde9ea39d540e4beefb183adaa23a9a3b2cd 100644
--- a/1-ProceduralProgramming/4-Functions.ipynb
+++ b/1-ProceduralProgramming/4-Functions.ipynb
@@ -77,6 +77,7 @@
     "- No semicolon after the prototype\n",
     "- A block follows the prototype instead; inside this block the implementation is written.\n",
     "- Parameter may not be named, but if they are used (which should be the case most of the time hopefully...) you will need such a name in the implementation.\n",
+    "- No semicolon after the closing brace.\n",
     "\n",
     "For instance:"
    ]
@@ -99,7 +100,7 @@
     "        division = numerator / denominator ;\n",
     "        std::cout << numerator << \" / \" << denominator << \" = \" << division << std::endl ;  \n",
     "    }\n",
-    "}"
+    "} // no semicolon here as well!"
    ]
   },
   {
@@ -148,7 +149,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Functions cannot be nested in C++, contrary to some other langages such as Python:"
+    "Functions cannot be nested in C++, contrary to some other languages such as Python:"
    ]
   },
   {
@@ -192,14 +193,14 @@
    "source": [
     "#include <iostream>\n",
     "\n",
-    "void IncrementAndPrint(int i)\n",
+    "void IncrementAndPrint(int value)\n",
     "{\n",
-    "    ++i;\n",
-    "    std::cout << \"Inside the function: i = \" << i << std::endl;\n",
+    "    ++value;\n",
+    "    std::cout << \"Inside the function: value = \" << value << std::endl;\n",
     "}\n",
     "\n",
     "{\n",
-    "    int i = 5; // I could have named it differently - it doesn't matter as the scope is different!\n",
+    "    int i { 5 };\n",
     "\n",
     "    IncrementAndPrint(i);\n",
     "\n",
@@ -211,7 +212,16 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "The `i` in the block body and in the function definition are not the same: one or the other could have been named differently and the result would have been the same:"
+    "#### Naming and scope"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In the above example, the lonely parameter of the function `IncrementAndPrint` was named `value`, whereas at call site the variable was simply named `i`.\n",
+    "\n",
+    "We could as well have used the same name in both places - there are no ambiguity whatsoever in doing so."
    ]
   },
   {
@@ -220,23 +230,33 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "#include <iostream>\n",
-    "\n",
-    "void IncrementAndPrint2(int local_argument)\n",
+    "void IncrementAndPrintUseI(int i)\n",
     "{\n",
-    "    ++local_argument;\n",
-    "    std::cout << \"Inside the function: local_argument = \" << local_argument << std::endl;\n",
+    "    ++i;\n",
+    "    std::cout << \"Inside the function: i = \" << i << std::endl;\n",
     "}\n",
     "\n",
     "{\n",
-    "    int i = 5; // I could have named it differently - it doesn't matter as the scope is different!\n",
+    "    int i { 5 };\n",
     "\n",
-    "    IncrementAndPrint2(i);\n",
+    "    IncrementAndPrintUseI(i);\n",
     "\n",
     "    std::cout << \"Outside the function: i = \" << i << std::endl; \n",
     "}"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "This might seem idiotic (and sure enough giving a name as simple as a one letter character is misguided in most cases!), but on\n",
+    "the other hand when you're considering a mathematical or physical quantity (`jacobian`, `displacement`, etc... ) you do not want \n",
+    "to force yourself to find another name (naming is already hard enough as it is).\n",
+    "\n",
+    "The reason there are no ambiguity at all is that in each scope (remember [first notebook](../1-ProceduralProgramming/1-Variables.ipynb#Scope-and-blocks)) \n",
+    "there is at most one `i` defined."
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -346,7 +366,7 @@
     "}\n",
     "\n",
     "PrintDivision(8, 5);\n",
-    "PrintDivision(8, 0); // bug!\n"
+    "PrintDivision(8, 0); // prints garbage values...\n"
    ]
   },
   {
@@ -358,6 +378,62 @@
     "* This is something completely out of control: quotient and remainder do not get a default value that would help to see if something is askew. The behaviour is undefined: you have no guarantee on the values the program will print (currently I see the same values as in the previous function call, but another compiler/architecture/etc... might yield another wrong value."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### [[nodiscard]] \n",
+    "\n",
+    "C++ 17 introduced a keyword named `[[nodiscard]]` that mitigates the issue mentioned above; this keyword tells the return value must be checked and not doing so is a **compilation warning**.\n",
+    "\n",
+    "It doesn't prevent a developer to use improperly the `ComputeDivision` function, but at least they are warned that they're doing something wrong."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%%cppmagics --print_command clang\n",
+    "\n",
+    "#include <cstdlib>\n",
+    "#include <iostream>\n",
+    "\n",
+    "// Declarations\n",
+    "int ComputeDivision(int arg1, int arg2, int& quotient, int& remainder);\n",
+    "void PrintDivision(int arg1, int arg2);\n",
+    "\n",
+    "\n",
+    "// Definitions    \n",
+    "[[nodiscard]] int ComputeDivision(int arg1, int arg2, int& quotient, int& remainder)\n",
+    "{\n",
+    "    if (arg2 == 0)\n",
+    "        return -1; // error code.\n",
+    "    \n",
+    "    quotient = arg1 / arg2;\n",
+    "    remainder = arg1 % arg2;\n",
+    "    return 0; // code when everything is alright.\n",
+    "}\n",
+    "\n",
+    "void PrintDivision(int arg1, int arg2)\n",
+    "{\n",
+    "    int quotient, remainder;\n",
+    "    \n",
+    "    ComputeDivision(arg1, arg2, quotient, remainder); // the dev 'forgot' to check the error code.\n",
+    "    \n",
+    "    std::cout << \"Euclidean division of \" << arg1 << \" by \" << arg2 << \" yields a quotient of \" \n",
+    "        << quotient << \" and a remainder of \" << remainder << std::endl;    \n",
+    "}\n",
+    "\n",
+    "int main([[maybe_unused]] int argc, char** argv)\n",
+    "{\n",
+    "    PrintDivision(8, 0); // bug!\n",
+    "\n",
+    "    return EXIT_SUCCESS;\n",
+    "}"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -1400,7 +1476,7 @@
     "\n",
     "Sometimes, in old programs you may see __void main()__; this is not correct and is now refused by most modern compilers.\n",
     "\n",
-    "The return value of the main function is an integer, __EXIT_SUCCESS__ should be returned when the program succeeds and __EXIT_FAILURE__ if it fails. You will often see a numerical value instead of these: __EXIT_SUCCESS__ is just a macro which value is 0. I recommend its use as you should strive to avoid any magic number in your codes.\n",
+    "The return value of the main function is an integer, __EXIT_SUCCESS__ should be returned when the program succeeds and __EXIT_FAILURE__ if it fails. You will often see a numerical value instead of these: __EXIT_SUCCESS__ is just a macro which value is 0. I recommend its use as you should strive to avoid any magic number in your codes. These two macros are defined inside `cstdlib` header.\n",
     "\n",
     "We will deal with main functions later when we will work in a true C++ environment."
    ]
diff --git a/1-ProceduralProgramming/5-DynamicAllocation.ipynb b/1-ProceduralProgramming/5-DynamicAllocation.ipynb
index 7c51ceda37ef931309396a484d3b5b9406882766..80bbb97602100247d55b3ff0a32bb81a4bf59c77 100644
--- a/1-ProceduralProgramming/5-DynamicAllocation.ipynb
+++ b/1-ProceduralProgramming/5-DynamicAllocation.ipynb
@@ -158,12 +158,12 @@
     "\n",
     "auto Ndice = 5ul;\n",
     "\n",
-    "int* throw_5_dices = throw_dice(Ndice);\n",
+    "int* throw_5_dice = throw_dice(Ndice);\n",
     "\n",
     "for (std::size_t i = 0; i < Ndice; ++i)\n",
-    "    std::cout << throw_5_dices[i] << std::endl;\n",
+    "    std::cout << throw_5_dice[i] << std::endl;\n",
     "\n",
-    "delete[] throw_5_dices;"
+    "delete[] throw_5_dice;"
    ]
   },
   {
@@ -176,12 +176,12 @@
     "\n",
     "Ndice = 3;\n",
     "\n",
-    "int* throw_7_dices = throw_dice(Ndice);\n",
+    "int* throw_7_dice = throw_dice(Ndice);\n",
     "\n",
     "for (std::size_t i = 0; i < Ndice; ++i)\n",
-    "    std::cout << throw_7_dices[i] << std::endl;\n",
+    "    std::cout << throw_7_dice[i] << std::endl;\n",
     "\n",
-    "delete[] throw_7_dices;"
+    "delete[] throw_7_dice;"
    ]
   },
   {
diff --git a/1-ProceduralProgramming/6-Streams.ipynb b/1-ProceduralProgramming/6-Streams.ipynb
index 3ba2bc5d653229944491c3a3bd8fad92aff0c1c1..aafca4ca2b242563344fe6c44d5f605f2a5592a5 100644
--- a/1-ProceduralProgramming/6-Streams.ipynb
+++ b/1-ProceduralProgramming/6-Streams.ipynb
@@ -309,6 +309,47 @@
     "}"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Binary files\n",
+    "\n",
+    "Exactly as in C, you may want to write directly your output in binary format rather than in ascii format by specifying `std::ios::binary` as second argument of `std::ofstream` or `std::ifstream`. \n",
+    "\n",
+    "It may be combined with others with `|` operator (here `std::ios::app` which tells to append the new content at the end the file if it already exists - without this cue the default behaviour is to overwrite pre-existing `test_stream.binary` file)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <fstream>\n",
+    "\n",
+    "{\n",
+    "    std::ofstream out(\"test_stream.binary\", std::ios::binary | std::ios::app);    \n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Using binary is *much* faster than ascii; if you want to write lots of data (typically in HPC) you should probably consider it.\n",
+    "\n",
+    "Drawbacks are:\n",
+    "\n",
+    "- Files written this way are less portable.\n",
+    "- Files can't be read directly by a human.\n",
+    "- Writing and reading such files requires more handiwork by the developer.\n",
+    "\n",
+    "Still, it is an option that should be on the table as I/O might really be a bottleneck in runtime.\n",
+    "\n",
+    "For more details see [the documentation on base class for all I/O stream classes](https://en.cppreference.com/w/cpp/io/ios_base) - fmtflags are also interesting, but a bit to deep for the training."
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},