diff --git a/logic_programming/4_DPLL.ipynb b/logic_programming/4_DPLL.ipynb
index cc310c1069e05c28ef252e074376226e8f679ece..0e698531f720d8fbb46cacb54d3b65a028b846c0 100644
--- a/logic_programming/4_DPLL.ipynb
+++ b/logic_programming/4_DPLL.ipynb
@@ -30,12 +30,14 @@
     "One can encode a clause as a set of literals, and one can encode the CNF as a set of clauses.\n",
     "Implicitly, the literals in clause are disjoined, while all the clauses in a CNF are conjoined.\n",
     "In Prolog, we can represent sets using lists.\n",
-    "Below is an encoding of the CNF as a list of lists."
+    "Below is an encoding of the CNF as a list of lists.\n",
+    "For example, the clause ```¬A ∨ ¬B ∨ ¬C``` is represented as the list ```[neg(a),neg(b),neg(c)]```.\n",
+    "Every literal is of either the form ```neg(p)``` or ```pos(p)``` with ```p``` being a proposition."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 4,
    "id": "964ebd52",
    "metadata": {
     "vscode": {
@@ -56,6 +58,39 @@
     "negate(neg(A),pos(A))."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "a4dc903e",
+   "metadata": {},
+   "source": [
+    "We can negate literals as follows:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "26e0221b",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mNA = neg(a),\n",
+       "NB = pos(b)"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- negate(pos(a),NA), negate(neg(b),NB)."
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "fc6823ad",
@@ -65,7 +100,8 @@
     }
    },
    "source": [
-    "We can pick two clauses and resolve them as follows:"
+    "We now show to perform resolution of two clauses.\n",
+    "First we can pick two clauses simply using ```member```:"
    ]
   },
   {
@@ -95,6 +131,14 @@
     "   member(C1,Clauses), member(Lit1,C1)."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "68fd6059",
+   "metadata": {},
+   "source": [
+    "We can now pick matching literals with opposing polarity as follows:"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": 5,
@@ -126,9 +170,17 @@
     "%comment."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "07785e91",
+   "metadata": {},
+   "source": [
+    "We can now implement resolution of two particular clauses as follows:"
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 8,
    "id": "33fc6271",
    "metadata": {
     "vscode": {
@@ -144,6 +196,126 @@
     "   append(R1,R2,Resolvent)."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "1d426bb1",
+   "metadata": {},
+   "source": [
+    "The above predicate uses the predicate ```select/3``` from ```library(lists)```, which can be used to select an element from a list and returns a modified list with the element removed:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "f2a8b4a4",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mRes1 = [1,3]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- select(2,[1,2,3],Res1)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "cba18c66",
+   "metadata": {},
+   "source": [
+    "The predicate ```resolve``` works as follows:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "id": "06aa608c",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mResolvent = [neg(a),pos(c)]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- resolve([neg(a),pos(b)],[neg(b),pos(c)],Resolvent)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "id": "a76d8b5d",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mResolvent = []"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- resolve([pos(b)],[neg(b)],Resolvent)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "id": "d7a0cc62",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1;31mfalse"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- resolve([neg(a),neg(b)],[neg(b),pos(c)],Resolvent)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2b2ea192",
+   "metadata": {},
+   "source": [
+    "Let us now resolve two clauses from our puzzle:"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": 7,
@@ -173,9 +345,17 @@
     "   resolve(C1,C2,NewClause)."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "a70dbafb",
+   "metadata": {},
+   "source": [
+    "Let us compute all direct resolvents:"
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 18,
    "id": "9d3fbd1a",
    "metadata": {
     "vscode": {
@@ -187,7 +367,10 @@
      "data": {
       "text/plain": [
        "\u001b[1mClauses = [[neg(a),neg(b),neg(c)],[pos(b),pos(a)],[pos(c),pos(a)],[neg(b),pos(a)],[neg(a),pos(b)]],\n",
-       "NewClauses = [[neg(b),neg(c),pos(b)],[neg(a),neg(c),pos(a)],[neg(b),neg(c),pos(c)],[neg(a),neg(b),pos(a)],[neg(b),neg(c),neg(b)],[neg(a),neg(c),neg(a)],[pos(a),neg(a),neg(c)],[pos(b),neg(b),neg(c)],[pos(a),pos(a)],[pos(b),pos(b)],[pos(a),neg(a),neg(b)],[pos(c),neg(b),neg(c)],[pos(c),pos(b)],[neg(b),neg(b),neg(c)],[pos(a),pos(a)],[pos(a),neg(a)],[neg(b),pos(b)],[neg(a),neg(a),neg(c)],[pos(b),pos(b)],[pos(b),pos(c)],[pos(b),neg(b)],[neg(a),pos(a)]]"
+       "NewClauses = [[neg(b),neg(c),pos(b)],[neg(a),neg(c),pos(a)],[neg(b),neg(c),pos(c)],[neg(a),neg(b),pos(a)],[neg(b),neg(c),neg(b)],[neg(a),neg(c),neg(a)],[pos(a),neg(a),neg(c)],[pos(b),neg(b),neg(c)],[pos(a),pos(a)],[pos(b),pos(b)],[pos(a),neg(a),neg(b)],[pos(c),neg(b),neg(c)],[pos(c),pos(b)],[neg(b),neg(b),neg(c)],[pos(a),pos(a)],[pos(a),neg(a)],[neg(b),pos(b)],[neg(a),neg(a),neg(c)],[pos(b),pos(b)],[pos(b),pos(c)],[pos(b),neg(b)],[neg(a),pos(a)]],\n",
+       "NrNew = 22,\n",
+       "SortedC = [[neg(a),neg(a),neg(c)],[neg(a),neg(b),pos(a)],[neg(a),neg(c),neg(a)],[neg(a),neg(c),pos(a)],[neg(a),pos(a)],[neg(b),neg(b),neg(c)],[neg(b),neg(c),neg(b)],[neg(b),neg(c),pos(b)],[neg(b),neg(c),pos(c)],[neg(b),pos(b)],[pos(a),neg(a)],[pos(a),neg(a),neg(b)],[pos(a),neg(a),neg(c)],[pos(a),pos(a)],[pos(b),neg(b)],[pos(b),neg(b),neg(c)],[pos(b),pos(b)],[pos(b),pos(c)],[pos(c),neg(b),neg(c)],[pos(c),pos(b)]],\n",
+       "NrNewUnique = 20"
       ]
      },
      "metadata": {},
@@ -197,7 +380,19 @@
    "source": [
     "?- problem(1,_,Clauses),\n",
     "   findall(NewClause,(member(C1,Clauses), member(C2,Clauses), \n",
-    "                      resolve(C1,C2,NewClause)), NewClauses)."
+    "                      resolve(C1,C2,NewClause)\n",
+    "                      ), NewClauses),\n",
+    "   length(NewClauses,NrNew),\n",
+    "   sort(NewClauses,SortedC), length(SortedC,NrNewUnique)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6cdeade5",
+   "metadata": {},
+   "source": [
+    "As you can see, the number of new clauses can grow considerably when applying resolution.\n",
+    "Below we study the DPLL algorithm to check satisfiability of a formula in CNF, using a simple form of resolution (where one clause is a single literal and the other clauses comes from the CNF of the problem)."
    ]
   },
   {
@@ -212,12 +407,15 @@
     "### DPLL Algorithm\n",
     "\n",
     "We now present bit by bit the DPLL algorithm as a Prolog program\n",
-    "manipulating the above clause database."
+    "manipulating the above clause database.\n",
+    "\n",
+    "The algorithm works by selecting a literal and then making it true and checking whether this leads to a solution.\n",
+    "If not, the algorithm backtracks and makes the negated version of the literal true and tries to find a solution."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 23,
    "id": "26924712",
    "metadata": {
     "vscode": {
@@ -227,7 +425,7 @@
    "outputs": [],
    "source": [
     "becomes_true(TrueLit,Clause) :-\n",
-    "    member(TrueLit,Clause).  "
+    "    member(TrueLit,Clause)."
    ]
   },
   {
@@ -235,7 +433,7 @@
    "id": "17dd1525",
    "metadata": {},
    "source": [
-    "The above checks if making a given literal true makes the second argument, a clause, true:"
+    "The above checks if making a given literal true makes the second argument, a clause, true (i.e., the clause is satisfied):"
    ]
   },
   {
@@ -286,9 +484,18 @@
     "?- becomes_true(neg(a),[neg(a),pos(b)])."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "62e13b8d",
+   "metadata": {},
+   "source": [
+    "The code below simplifies a clause given a literal ```TrueLit``` that has just become true.\n",
+    "This is done using resolution:"
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 24,
    "id": "3b9c87e4",
    "metadata": {
     "vscode": {
@@ -299,12 +506,20 @@
    "source": [
     "simplify(TrueLit,Clause,SimplifedClause) :-\n",
     "   negate(TrueLit,FalseLit),\n",
-    "   delete(Clause,FalseLit,SimplifedClause).   % Resolution with TrueLit\n"
+    "   delete(Clause,FalseLit,SimplifedClause).   % Resolution with TrueLit if possible\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e8cee74e",
+   "metadata": {},
+   "source": [
+    "The code uses the ```delete/3``` predicate from ```library(lists)```. Unlike select it also succeeds when the element is not in the list:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 19,
    "id": "0abd90f6",
    "metadata": {
     "vscode": {
@@ -315,7 +530,7 @@
     {
      "data": {
       "text/plain": [
-       "\u001b[1mR = [neg(b),neg(c)]"
+       "\u001b[1mR = [a,b,c]"
       ]
      },
      "metadata": {},
@@ -323,7 +538,7 @@
     }
    ],
    "source": [
-    "?- simplify(pos(a),[neg(a),neg(b),neg(c)],R)."
+    "?- delete([a,b,c],d,R)."
    ]
   },
   {
@@ -382,6 +597,14 @@
     "?- simplify(pos(c),[pos(a),neg(c)],SC)."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "4c3eae55",
+   "metadata": {},
+   "source": [
+    "If no resolution is possible the clause is returned unchanged:"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": 16,
@@ -406,9 +629,19 @@
     "?- simplify(pos(b),[pos(a),neg(c)],SC)."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "441c4145",
+   "metadata": {},
+   "source": [
+    "We can now define the whole procedure to set a literal ```Lit``` to true (```Clauses|{Lit}```) by\n",
+    "- removing all clauses which have become true (and no longer need to be checked)\n",
+    "- simplifying the remaining clauses with resolution if possible."
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 25,
    "id": "95fcf64b",
    "metadata": {
     "vscode": {
@@ -427,13 +660,12 @@
    "id": "f0a8731d",
    "metadata": {},
    "source": [
-    "The above code sets a given literal ```Lit``` to true by removing\n",
-    "all now redundant clauses and simplifying the remaining ones via resolution."
+    "The predicate ```exclude/3``` from ```library(lists)``` is a higher-order predicate. It maps its first argument (a predicate) over the second argument (a list) and excludes all elements where the predicate succeeds."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 26,
    "id": "9c35b0b3",
    "metadata": {
     "vscode": {
@@ -455,6 +687,56 @@
     "?- exclude(becomes_true(pos(a)), [[neg(b)], [pos(a),pos(c)]],R)."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "1b1f2fe8",
+   "metadata": {},
+   "source": [
+    "```exclude``` will perform these two calls and hence remove the second clause and keep the first one:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "id": "56175b0d",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1;31mfalse"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mtrue"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- becomes_true(pos(a),[neg(b)]).\n",
+    "?- becomes_true(pos(a),[pos(a),pos(c)])."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "52063b72",
+   "metadata": {},
+   "source": [
+    "The predicate ```maplist/3``` from ```library(lists)``` is another higher-order predicate. It maps its first argument (a predicate) over the elements in the second and third arguments (both lists)."
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": 19,
@@ -479,6 +761,48 @@
     "?- maplist(simplify(neg(c)), [[neg(b),pos(c)], [pos(a),pos(c)]],R)."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "44d0ef2e",
+   "metadata": {},
+   "source": [
+    "```maplist``` will perform these two calls and put ```R1``` and ```R2``` into the result list ```R``` above:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "id": "9e0d83d0",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mR1 = [neg(b)],\n",
+       "R2 = [pos(a)]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- simplify(neg(c),[neg(b),pos(c)],R1),\n",
+    "   simplify(neg(c),[pos(a),pos(c)],R2)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "007b118b",
+   "metadata": {},
+   "source": [
+    "The predicate ```set_literal``` can now be called as follows:"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": 20,
@@ -503,6 +827,71 @@
     "?- set_literal(pos(a),[[neg(a),neg(b),neg(c)]],Res)."
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": 30,
+   "id": "75a52c4f",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mRes = []"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- set_literal(neg(a),[[neg(a),neg(b),neg(c)]],Res)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "04b0de71",
+   "metadata": {},
+   "source": [
+    "In the call above we have found a model: all clauses have been satisfied.\n",
+    "In the next call we have found a contradiction, as the resulting list contains the empty clause (aka the obvious contradiction)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "id": "d8876a92",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mRes = [[]]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- set_literal(neg(a),[[neg(a),neg(b),neg(c)],[pos(a)]],Res)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ee321f62",
+   "metadata": {},
+   "source": [
+    "Let us now use this code on our puzzle:"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": 21,
@@ -555,9 +944,21 @@
     "?- problem(1,_,C1), set_literal(pos(a),C1,C2), set_literal(pos(b),C2,C3)."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "800c4415",
+   "metadata": {},
+   "source": [
+    "This is now the top-level of the DPLL algorithm.\n",
+    "It first tries to find unit clauses, to deterministically find forced assignments.\n",
+    "It then checks if all clauses have now been satisfied.\n",
+    "If not, it checks for inconsistency.\n",
+    "If there is no inconsistency, then a literal is chosen and forced to one and then if required the other value."
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": 23,
+   "execution_count": 33,
    "id": "b8cabba2",
    "metadata": {
     "vscode": {
@@ -573,7 +974,7 @@
     "dpll([],Stack) :- !, Stack=[]. % SAT\n",
     "dpll(Clauses,[branch(Lit)|Stack]) :-\n",
     "    \\+ member([],Clauses), % no inconsistency\n",
-    "    choose_literal(Clauses,Lit),\n",
+    "    choose_literal(Clauses,Lit), % this selects one literal and returns it first in original then in negated form\n",
     "    set_literal(Lit,Clauses,Clauses2),\n",
     "    dpll(Clauses2,Stack).    \n",
     "\n",
@@ -584,7 +985,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": 34,
    "id": "e369355c",
    "metadata": {
     "vscode": {
@@ -609,7 +1010,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 25,
+   "execution_count": 35,
    "id": "78764141",
    "metadata": {
     "vscode": {