diff --git a/.vitepress/config.mts b/.vitepress/config.mts
index 16f85d1..87fd9f6 100644
--- a/.vitepress/config.mts
+++ b/.vitepress/config.mts
@@ -59,10 +59,10 @@ export default defineConfig({
{ text: '4. N²-Knights', link: '/exams/n2-knights/' },
{ text: '5. Filetree', link: '/exams/filetree/' },
{ text: '6. Text Justification', link: '/exams/justify/' },
- // { text: '7. Photographing Skyscrapers', link: '/exams/filetree' },
- // { text: '8. Non-deterministic Finite Automata', link: '/exams/filetree' },
- // { text: '9. Least Common Anchestor', link: '/exams/filetree' },
- // { text: '10. Building Trees', link: '/exams/filetree' },
+ { text: '7. Photographing Skyscrapers', link: '/exams/photo-skyscraper/' },
+ { text: '8. Non-deterministic Finite Automata', link: '/exams/finite-automata/' },
+ { text: '9. Least Common Anchestor', link: '/exams/least-common-ancestor/' },
+ { text: '10. Building Trees', link: '/exams/building-trees/' },
// { text: '11. Square Code', link: '/exams/filetree' },
// { text: '12. Rock, Paper, Scissors', link: '/exams/filetree' },
// { text: '13. Sierpinski Carpet', link: '/exams/filetree' },
diff --git a/exams/building-trees/index.md b/exams/building-trees/index.md
new file mode 100644
index 0000000..e5a9a7d
--- /dev/null
+++ b/exams/building-trees/index.md
@@ -0,0 +1,253 @@
+---
+outline: deep
+title: "Exam Task: Building Trees"
+subtitle: "From: Exam 2 - 2023"
+---
+
+# Building Trees
+
+
+In this task, you will build a tree from an intial tree and a sequence of edges. Your task is to
+iterate through the edge sequence from left to right and expand successively the initial tree. Each
+edge is a pair $(s,t)$. To expand the tree by the edge $(s,t)$, you must traverse through the tree
+and locate the node $s$. Next, you add $t$ among its children. If $s$ does not occur in the tree,
+the edge is ignored.
+
+For example, suppose that the initial tree is just a single node (a leaf) $1$ and the edge sequence
+equals $(1,2),(1,3),(5,7),(2,4),(4,5),(3,6)$. By expanding the initial tree by the edges, we obtain
+the following sequence of trees:
+
+
+
+Note that the third edge $(5,7)$ was ignored because there is no node $5$ in the currently
+constructed tree. So the currently constructed tree remains unchanged.
+
+## Racket
+
+In \textbf{Racket}, implement a function `(build-tree init edges)` that takes an initial
+tree `init` and a list of edges `edges`, and returns the tree created by expanding
+the initial tree by the edges.
+
+To represent trees in Racket, use the following structures:
+```scheme
+(struct node (val kids) #:transparent)
+(struct leaf (val) #:transparent)
+```
+Thus the leaves (nodes without children) are represented as, for instance, `(leaf 6)`.
+The nodes with children are represented as, for instance, `(node 1 (list (leaf 2) (leaf 3)))`.
+
+To implement the function `build-tree`, implement first a function `(add-edge edge tree)`
+expanding a given tree `tree` by a single edge `edge`. For example,
+```scheme
+> (add-edge '(2 4) (node 1 (list (leaf 2) (leaf 3))))
+(node 1 (list (node 2 (list (leaf 4))) (leaf 3)))
+```
+
+When you add a new leaf to a list of children, prepend it at the front. For example,
+```scheme
+> (add-edge '(1 3) (node 1 (list (leaf 2))))
+(node 1 (list (leaf 3) (leaf 2)))
+```
+
+To make the output of the function `build-tree` unique, sort the children of every node
+in the resulting tree based on their values. You may assume, that the node values are numbers.
+For example,
+```scheme
+(node 1 (list (node 2 (list (leaf 4))) (leaf 3))) ; correct
+(node 1 (list (leaf 3) (node 2 (list (leaf 4))))) ; not correct
+```
+
+Your file should be called `task3.rkt` and should export the `add-edge` and
+`build-tree` functions and the structures `node` and `leaf`.
+```scheme
+#lang racket
+
+(provide add-edge
+ build-tree
+ (struct-out node)
+ (struct-out leaf))
+
+(struct node (val kids) #:transparent)
+(struct leaf (val) #:transparent)
+
+(define (add-edge edge tree)
+ ; Implement me!
+ )
+
+(define (build-tree init edges)
+ ; Implement me!
+ )
+```
+
+### Example
+
+```scheme
+> (build-tree (leaf 1) '((1 2) (1 3) (5 7) (2 4) (4 5) (3 6)))
+(node 1 (list (node 2 (list (node 4 (list (leaf 5)))))
+ (node 3 (list (leaf 6)))))
+```
+
+### Hints
+
+To sort the children, use the function `(sort lst less-than?)` sorting a list `lst`
+comparing its elements by a function `less-than?`. For example,
+```scheme
+> (sort '((1 3) (2 2) (3 1)) (lambda (p q) (< (cadr p) (cadr q))))
+'((3 1) (2 2) (1 3))
+```
+
+::: details Solution
+```scheme
+#lang racket
+
+(provide add-edge build-tree)
+
+(struct node (val kids) #:transparent)
+(struct leaf (val) #:transparent)
+
+(define (value t)
+ (match t
+ [(leaf x) x]
+ [(node x kids) x]))
+
+(define (sortnodes ns)
+ (sort ns (lambda (p q) (< (value p) (value q)))))
+
+
+(define (add-edge edge tree)
+ (define s (car edge))
+ (define t (cadr edge))
+ (match tree
+ [(leaf x)
+ (if (= x s)
+ (node x (list (leaf t)))
+ (leaf x))]
+ [(node x kids)
+ (if (= x s)
+ (node x (sortnodes (cons (leaf t) kids)))
+ (node x (map (curry add-edge edge) kids)))]
+ ))
+
+
+(define (build-tree init edges) (foldl add-edge init edges))
+
+; (define (build-tree init edges))
+; (add-edge '(2 4) (node 1 (list (le))))
+(add-edge '(2 4) (leaf 2))
+(add-edge '(2 4) (node 1 (list (leaf 2) (leaf 3))))
+
+
+(build-tree (leaf 1) '((1 2) (1 3) (2 4) (4 5) (3 6)))
+(node 1 (list (node 2 (list (node 4 (list (leaf 5))))) (node 3 (list (leaf 6)))))
+```
+:::
+
+## Haskell
+
+In Haskell, implement a function
+`buildTree :: Ord a => Tree a -> [Edge a] -> Tree a` that takes an initial
+tree and a list of edges, and returns the tree created by expanding
+the initial tree by the edges.
+
+To represent trees and edges, use the following data types:
+```haskell
+data Tree a = Leaf { val :: a }
+ | Node { val :: a,
+ kids :: [Tree a] } deriving (Eq,Show)
+
+type Edge a = (a,a)
+```
+Thus the leaves (nodes without children) are represented as, for instance, `Leaf {val = 6}`.
+The nodes with children are represented as, for instance,
+```haskell
+Node {val = 1, kids = [Leaf {val = 2,Leaf {val = 3}]}
+```
+
+To implement the function `buildTree`, implement first a function
+```haskell
+addEdge :: Eq a => Tree a -> Edge a -> Tree a
+```
+expanding a given tree by a single edge. For example,
+```haskell
+> addEdge (Node {val = 1, kids = [Leaf {val = 2}, Leaf {val = 3}]}) (2,4)
+Node {val = 1, kids = [Node {val = 2, kids = [Leaf {val = 4}]},
+ Leaf {val = 3}]}
+```
+
+When you add a new leaf to a list of children, prepend it at the front. For example,
+```haskell
+> addEdge (Node {val = 1, kids = [Leaf {val = 2}]}) (1,3)
+Node {val = 1, kids = [Leaf {val = 3}, Leaf {val = 2}]}
+```
+
+To make the output of the function `buildTree` unique, sort the children of every node
+in the resulting tree based on their values. You may assume, that the node values belongs to
+the typeclass `Ord`. For example,
+
+```haskell
+ Node {val = 1, kids = [Node {val = 2, kids = [Leaf {val = 4}]},
+ Leaf {val = 3}]} -- correct
+ Node {val = 1, kids = [Leaf {val = 3},
+ Node {val = 2, kids = [Leaf {val = 4}]}]} -- not correct
+```
+
+Your file should be called `Task4.hs` and should export the `buildTree`,
+`addEdge` functions and the type `Tree`.
+
+```haskell
+module Task4 (addEdge, buildTree, Tree(..)) where
+import Data.List
+
+data Tree a = Leaf { val :: a }
+ | Node { val :: a,
+ kids :: [Tree a] } deriving (Eq,Show)
+
+type Edge a = (a,a)
+
+addEdge :: Eq a => Tree a -> Edge a -> Tree a
+-- implement me!
+
+buildTree :: Ord a => Tree a -> [Edge a] -> Tree a
+-- implement me!
+```
+
+### Example
+
+```haskell
+> buildTree (Leaf {val=1}) [(1,2),(1,3),(5,7),(2,4),(4,5),(3,6)]
+Node {val = 1, kids = [Node {val = 2, kids = [Node {val = 4,
+ kids = [Leaf {val = 5}]}]},
+ Node {val = 3, kids = [Leaf {val = 6}]}]}
+```
+
+### Hints
+
+To sort the children, use the function `sortOn :: Ord b => (a -> b) -> [a] -> [a]`
+from the library `Data.List` sorting a list by converting them into a values of
+a type inside `Ord`. For example,
+```haskell
+> sortOn snd [(1,3),(2,2),(3,1)]
+[(3,1),(2,2),(1,3)]
+```
+
+::: details Solution
+```haskell
+import Data.List
+
+data Tree a = Leaf { val :: a }
+ | Node { val :: a,
+ kids :: [Tree a] } deriving Show
+type Edge a = (a,a)
+
+tr = Node {val = 1, kids = [Leaf {val = 2},Leaf {val = 3}]}
+
+addEdge (Leaf x) (s,t) | x == s = Node x [Leaf t]
+ | otherwise = Leaf x
+addEdge (Node x kids) (s,t)
+ | x == s = Node x (sortOn val ((Leaf t):kids))
+ | otherwise = Node x [addEdge n (s,t) | n <- kids]
+
+
+buildTree tree edges = foldl addEdge tree edges
+```
+:::
diff --git a/exams/building-trees/task.hs b/exams/building-trees/task.hs
new file mode 100644
index 0000000..9435ef5
--- /dev/null
+++ b/exams/building-trees/task.hs
@@ -0,0 +1,17 @@
+import Data.List
+
+data Tree a = Leaf { val :: a }
+ | Node { val :: a,
+ kids :: [Tree a] } deriving Show
+type Edge a = (a,a)
+
+tr = Node {val = 1, kids = [Leaf {val = 2},Leaf {val = 3}]}
+
+addEdge (Leaf x) (s,t) | x == s = Node x [Leaf t]
+ | otherwise = Leaf x
+addEdge (Node x kids) (s,t)
+ | x == s = Node x (sortOn val ((Leaf t):kids))
+ | otherwise = Node x [addEdge n (s,t) | n <- kids]
+
+
+buildTree tree edges = foldl addEdge tree edges
diff --git a/exams/building-trees/task.rkt b/exams/building-trees/task.rkt
new file mode 100644
index 0000000..21dc9f1
--- /dev/null
+++ b/exams/building-trees/task.rkt
@@ -0,0 +1,41 @@
+#lang racket
+
+(provide add-edge build-tree)
+
+(struct node (val kids) #:transparent)
+(struct leaf (val) #:transparent)
+
+(define (value t)
+ (match t
+ [(leaf x) x]
+ [(node x kids) x]))
+
+(define (sortnodes ns)
+ (sort ns (lambda (p q) (< (value p) (value q)))))
+
+
+(define (add-edge edge tree)
+ (define s (car edge))
+ (define t (cadr edge))
+ (match tree
+ [(leaf x)
+ (if (= x s)
+ (node x (list (leaf t)))
+ (leaf x))]
+ [(node x kids)
+ (if (= x s)
+ (node x (sortnodes (cons (leaf t) kids)))
+ (node x (map (curry add-edge edge) kids)))]
+ ))
+
+
+(define (build-tree init edges) (foldl add-edge init edges))
+
+; (define (build-tree init edges))
+; (add-edge '(2 4) (node 1 (list (le))))
+(add-edge '(2 4) (leaf 2))
+(add-edge '(2 4) (node 1 (list (leaf 2) (leaf 3))))
+
+
+(build-tree (leaf 1) '((1 2) (1 3) (2 4) (4 5) (3 6)))
+(node 1 (list (node 2 (list (node 4 (list (leaf 5))))) (node 3 (list (leaf 6)))))
diff --git a/exams/building-trees/trees.tex b/exams/building-trees/trees.tex
new file mode 100644
index 0000000..0d1060a
--- /dev/null
+++ b/exams/building-trees/trees.tex
@@ -0,0 +1,47 @@
+\documentclass[border=0pt]{standalone}
+\usepackage{graphicx}
+\usepackage{fancyvrb}
+\usepackage{xcolor}
+\usepackage{bm}
+\usepackage{pgf}
+\usepackage{tikz}
+\usetikzlibrary{arrows,automata,positioning}
+
+\definecolor{myGray}{rgb}{0.85,0.85,0.85}
+
+\begin{document}
+
+\begin{tikzpicture}
+ \node (1) {$1$};
+
+ \node[right=of 1] (12) {$1$}
+ child {node {$2$}};
+
+ \node[right=1.5cm of 12] (123) {$1$}
+ child {node {$2$}}
+ child {node {$3$}};
+
+ \node[right=2cm of 123] (123') {$1$}
+ child {node {$2$}}
+ child {node {$3$}};
+
+ \node[right=2cm of 123'] (1234) {$1$}
+ child {node {$2$}
+ child {node {$4$}}}
+ child {node {$3$}};
+
+ \node[right=2cm of 1234] (12345) {$1$}
+ child {node {$2$}
+ child {node {$4$}
+ child {node {$5$}}}}
+ child {node {$3$}};
+
+ \node[right=2cm of 12345] {$1$}
+ child {node {$2$}
+ child {node {$4$}
+ child {node {$5$}}}}
+ child {node {$3$}
+ child {node {$6$}}};
+
+\end{tikzpicture}
+\end{document}
diff --git a/exams/filetree/index.md b/exams/filetree/index.md
index bad1deb..4b46b18 100644
--- a/exams/filetree/index.md
+++ b/exams/filetree/index.md
@@ -9,7 +9,7 @@ subtitle: "In Racket and Haskell"
A filetree can be used to efficiently search/replace in large filesystems.
You can think of it as a tree with a variable size of nodes
-```{.tight-code}
+```
.
├── scripts
│ ├── emptydir
@@ -138,9 +138,9 @@ For splitting a string into a list of strings you can make use of the function `
## Haskell
-For the Haskell implementation you are provided with a module [`FTree.hs`](../../code/FTree.hs)
-which contains a `FTree` type including a `Data.Map` from keys to values. We quote the definition
-here:
+For the Haskell implementation you are provided with a module
+[`FTree.hs`](https://github.com/aicenter/FUP/blob/main/code/FTree.hs) which contains a `FTree` type
+including a `Data.Map` from keys to values. We quote the definition here:
```haskell
import Data.Map (Map)
import qualified Data.Map as Map
diff --git a/exams/finite-automata/dfa.tex b/exams/finite-automata/dfa.tex
new file mode 100644
index 0000000..e7031c8
--- /dev/null
+++ b/exams/finite-automata/dfa.tex
@@ -0,0 +1,27 @@
+\documentclass[border=0pt]{standalone}
+\usepackage{graphicx}
+\usepackage{fancyvrb}
+\usepackage{xcolor}
+\usepackage{bm}
+\usepackage{pgf}
+\usepackage{tikz}
+\usetikzlibrary{automata, positioning, arrows}
+
+
+\begin{document}
+
+\begin{tikzpicture}[>=stealth,auto,node distance=20mm,state/.append style={fill=black!10}]
+ \node[state, initial] (1) {$1$};
+ \node[state, accepting, above right of=1] (2) {$2$};
+ % \node[state, accepting, right of=2] (3) {$3$};
+ \node[state, accepting, below right of=1] (3) {$3$};
+ \node[state, above right of=3] (4) {$4$};
+
+ \draw (1) edge[->,above] node {$a$} (2)
+ (2) edge[loop above] node {$b$} (2)
+ (1) edge[->,above] node {$a$} (3)
+ (3) edge[->, below, bend right] node {$b$} (4)
+ (4) edge[->, above, bend right] node {$a$} (3)
+ (2) edge[->, above] node {$a$} (4);
+\end{tikzpicture}
+\end{document}
diff --git a/exams/finite-automata/index.md b/exams/finite-automata/index.md
new file mode 100644
index 0000000..3c1dad7
--- /dev/null
+++ b/exams/finite-automata/index.md
@@ -0,0 +1,303 @@
+---
+outline: deep
+title: "Exam Task: Non-deterministic Finite Automata"
+subtitle: "From: Exam 2 - 2021"
+---
+
+
+# Non-deterministic Finite Automata
+
+In the seminars, we have encountered *Deterministic Finite Automata* (DFAs). In this task, we will
+work with a generalized version of the DFA that is called *Non-deterministic Finite Automaton
+(NFA)*. NFA is the 5-tuple
+
+* set of states $\mathcal{Q}$,
+* a finite set of input symbols $\Sigma$ (called alphabet),
+* a set of transitions $\Delta \subseteq \mathcal{Q} \times \Sigma \times \mathcal{Q}$,
+* a start state $q_0$,
+* a set of final states $\mathcal{F} \subseteq \mathcal{Q}$.
+
+In other words, NFA is just a directed graph whose vertices are states and transitions are edges
+labelled by symbols from $\Sigma$, i.e., if $(s,a,t)\in\Delta$ then there is an edge leading
+from the state $s$ to the state $t$ labelled by $a\in\Sigma$. We say that NFA accepts a word
+$w=a_1a_2\ldots a_n\in\Sigma^*$ (i.e., a finite sequence of symbols form $\Sigma$) if there
+exists a path leading from the start state into a final one labelled consecutively by symbols
+$a_1,a_2,\ldots,a_n$.
+
+An example of an NFA is depicted in the figure below. This NFA accepts e.g. words $abb$ or
+$aba$. On the other hand, it accept neither $ba$ nor $abab$.
+
+Example of NFA where $\mathcal Q=\{1,2,3,4\}$, $\Sigma=\{a,b\}$, $q_0=1$, $\mathcal F=\{2,3\}$
+and $\Delta=\{(1,a,2),(2,b,2),(1,a,3),(3,b,4),(4,a,3),(2,a,4)\}$.
+
+
+
+
+## Haskell
+Your assignment is to implement a function that generates the language accepted by a given NFA.
+However, since such a language is potentially infinite, the problem is simplified to listing all
+words of given length that are accepted by the automaton.
+
+For the purpose of this task, the NFA is defined as follows. First, each possible transition is
+represented as the triplet
+
+```haskell
+data Transition a b = Tr a b a
+```
+
+where the type `a` represents the states in $\mathcal{Q}$ and `b` are the symbols $\Sigma$;
+hence, the transition from the first member of the triplet to the third member of the triplet is
+possible if and only if the next input symbol is equal to the second member of the triplet.
+The NFA itself is defined as
+```haskell
+data Automaton a b = NFA [Transition a b] a [a]
+```
+where the first member is the exhaustive list of transitions, the second member is the start state
+$q_0$, and the third is the list of final states $\mathcal{F}$. The particular automaton
+from the figure is defined as follows:
+```haskell
+nfa::Automaton Int Char
+nfa = NFA [Tr 1 'a' 2,
+ Tr 2 'b' 2,
+ Tr 1 'a' 3,
+ Tr 3 'b' 4,
+ Tr 4 'a' 3,
+ Tr 2 'a' 4]
+ 1
+ [2,3]
+```
+
+### `accepts`-function
+The first part of the task is to decide whether a particular word is accepted by a given automaton.
+To that end, implement the function
+```haskell
+accepts :: (Eq a, Eq b) => Automaton a b -> [b] -> Bool
+```
+which takes and automaton and a list of symbols that represents the word, and returns \texttt{True}
+iff the word is accepted. Notice, `a` and `b` are instances of `Eq`. The function is used as
+follows.
+```haskell
+> accepts nfa "aba"
+True
+
+> accepts nfa "abab"
+False
+```
+
+
+_**Hint:**_ One possibility how to implement the function `accepts` is to maintain a list of
+accessible states after consuming an initial segment of the input word.
+
+Consider the NFA from the figure and the word `"aba"`. We start with the list containing the start
+state `[1]`. Then we modify this list by the transitions corresponding to the letters as follows:
+```haskell
+[1] -a-> [2,3] -b-> [2,4] -a-> [3,4]
+```
+Finally, we check if the resulting list of states
+contains a final state. `3` is a final state, the output is `True`.
+
+Another example for `"abab"` is
+```haskell
+[1] -a-> [2,3] -b-> [2,4] -a-> [3,4] -b-> [4]
+```
+As `4` is not a final state, the ouput is `False`.
+
+One more example for `"ba"`:
+```haskell
+[1] -b-> [] -a-> []
+```
+So the output is `False`.
+
+
+### `lwords`-function
+
+Next, given the automaton, its alphabet, and a length, generate the list of all words of the given
+length that are accepted by the automaton. Implement the function
+```haskell
+lwords :: (Eq a, Eq b) => [b] -> Automaton a b -> Int -> [[b]]
+```
+where the first input is the alphabet as list of unique symbols, the second is the automaton, and
+the third is the word length. The function returns a list of the accepted words, i.e., a list of
+lists of symbols. In terms of the task evaluation, the ordering of the words is not relevant. The
+function operates as follows.
+
+```haskell
+> lwords "ab" nfa 0
+[]
+> lwords "ab" nfa 1
+["a"]
+> lwords "ab" nfa 3
+["aaa","aba","abb"]
+```
+
+_**Hint:**_ first, generate all possible words of the given length. Then, filter the words using
+the function `accepts`.
+
+::: details Solution
+```haskell
+import Control.Monad.State
+
+data Transition a b = Tr a b a deriving Show
+data Automaton a b = NFA [(Transition a b)] a [a] deriving Show
+
+--nfa::Automaton Int Char
+--nfa = NFA [(Tr 1 'a' 1), (Tr 1 'b' 1), (Tr 1 'c' 1), (Tr 1 'b' 2), (Tr 1 'c' 2)] 1 [2]
+
+
+walk :: (Eq a, Eq b) => Automaton a b -> a -> [b] -> Bool
+walk aut@(NFA trs start finals) state [] | elem state finals = True
+ | otherwise = False
+walk aut@(NFA trs start finals) state (c:ws) = or [ (walk aut t ws) | tr@(Tr f c' t) <- trs, c' == c, f == state]
+
+accepts :: (Eq a, Eq b) => Automaton a b -> [b] -> Bool
+accepts aut@(NFA trs start finals) word = walk aut start word
+
+combinations :: [a] -> Int -> State [[a]] [[a]]
+combinations cs 0 = do ws <- get
+ return ws
+combinations cs n = do ws <- get
+ let ws' = [ c:w | c <- cs , w <- ws]
+ put ws'
+ combinations cs (n-1)
+
+lwords :: (Eq a, Eq b) => [b] -> Automaton a b -> Int -> [[b]]
+lwords abc aut n = reverse $ [ w | w <- p, accepts aut w] where
+ p = evalState (combinations abc n) [[]]
+
+```
+:::
+
+## Racket
+
+Implement the functions `accepts` and `lwords`. In this task, you may assume that the input word
+will always be a string. Thus you can use functions `string->list` and `list->string` for converting
+strings to lists of characters and vice versa.
+Of course, we need to adapt the automaton representation for Racket.
+First, each possible transition is represented as the triplet:
+```scheme
+(struct transition (from-state symbol to-state))
+```
+where `from-state` and `to-state` are states in $\mathcal{Q}$ and `symbol` is a symbol in $\Sigma$.
+We can construct the NFA itself as
+```scheme
+(struct automaton (trans init-state final-states))
+```
+where the first member `trans` is the exhaustive list of transitions, the second member `init-state`
+is the start state $q_0$, and the third member `final-state` is the list of final states
+$\mathcal{F}$.
+The particular automaton from the figure is defined as follows.
+```scheme
+(define nfa
+ (make-automaton
+ (list (make-trans 1 #\a 2)
+ (make-trans 2 #\b 2)
+ (make-trans 1 #\a 3)
+ (make-trans 3 #\b 4)
+ (make-trans 4 #\a 3)
+ (make-trans 2 #\a 4))
+ 1
+ (list 2 3)))
+```
+
+Implement the function
+```scheme
+(accepts automaton word)
+```
+which takes an automaton and a string that represents the word to be parsed by the automaton, and
+returns `#t` if the word is accepted, `#f` otherwise.
+The function is used as follows:
+```scheme
+> (accepts nfa "aba")
+#t
+
+> (accepts nfa "abab")
+#f
+```
+\noindent
+
+Next, given the automaton, its alphabet, and a length, generate the list of all words of the given
+length that are accepted by the automaton. Implement the function
+```scheme
+(lwords alphabet automaton n)
+```
+where the first input `alphabet` is the alphabet passed as a string, the second input
+`automaton` is the automaton, and the third input `n` is the word length.
+The function returns a list of the accepted words, i.e., a list of lists of characters.
+In terms of the task evaluation, the ordering of the words is not relevant.
+The function operates as follows:
+```scheme
+> (lwords "ab" nfa 0)
+'()
+
+> (lwords "ab" nfa 1)
+'("a")
+
+> (lwords "ab" nfa 3)
+'("aaa" "aba" "abb")
+```
+
+For testing purposes your file should be named `task3.rkt` and start with the following lines:
+```scheme
+#lang racket
+
+(provide accepts
+ lwords)
+```
+
+::: details Solution
+```scheme
+#lang racket
+
+(provide accepts
+ lwords)
+
+(struct transition (from-state symbol to-state))
+(struct automaton (trans init-state final-states))
+
+(define ((s-next fa a) s)
+ (define tr (automaton-trans fa))
+ (map transition-to-state
+ (filter (lambda (t) (and (equal? s (transition-from-state t))
+ (equal? a (transition-symbol t))))
+ tr)))
+
+(define ((next fa) a ss)
+ (define tr (automaton-trans fa))
+ (apply append (map (s-next fa a) ss)))
+
+(define (accepts fa w)
+ (define states (foldl (next fa) (list (automaton-init-state fa)) (string->list w)))
+ (if (eq? #f (ormap (lambda (s) (member s (automaton-final-states fa))) states))
+ #f
+ #t))
+
+(define (words alp n)
+ (cond
+ ((= n 0) '())
+ ((= n 1) (map list alp))
+ (else (apply append
+ (map (lambda (w)
+ (map (lambda (a) (cons a w)) alp))
+ (words alp (- n 1)))))))
+
+(define (lwords alp nfa n)
+ (filter (lambda (w) (accepts nfa (list->string w)))
+ (words (string->list alp) n)))
+
+
+(define nfa
+ (automaton
+ (list (transition 1 #\a 2)
+ (transition 2 #\b 2)
+ (transition 1 #\a 3)
+ (transition 3 #\b 4)
+ (transition 4 #\a 3)
+ (transition 2 #\a 4))
+ 1
+ (list 2 3)))
+
+
+(accepts nfa "aba")
+(lwords "ab" nfa 3)
+```
+:::
diff --git a/exams/finite-automata/task.rkt b/exams/finite-automata/task.rkt
new file mode 100644
index 0000000..5aef08a
--- /dev/null
+++ b/exams/finite-automata/task.rkt
@@ -0,0 +1,53 @@
+#lang racket
+
+(provide accepts
+ lwords)
+
+(struct transition (from-state symbol to-state))
+(struct automaton (trans init-state final-states))
+
+(define ((s-next fa a) s)
+ (define tr (automaton-trans fa))
+ (map transition-to-state
+ (filter (lambda (t) (and (equal? s (transition-from-state t))
+ (equal? a (transition-symbol t))))
+ tr)))
+
+(define ((next fa) a ss)
+ (define tr (automaton-trans fa))
+ (apply append (map (s-next fa a) ss)))
+
+(define (accepts fa w)
+ (define states (foldl (next fa) (list (automaton-init-state fa)) (string->list w)))
+ (if (eq? #f (ormap (lambda (s) (member s (automaton-final-states fa))) states))
+ #f
+ #t))
+
+(define (words alp n)
+ (cond
+ ((= n 0) '())
+ ((= n 1) (map list alp))
+ (else (apply append
+ (map (lambda (w)
+ (map (lambda (a) (cons a w)) alp))
+ (words alp (- n 1)))))))
+
+(define (lwords alp nfa n)
+ (filter (lambda (w) (accepts nfa (list->string w)))
+ (words (string->list alp) n)))
+
+
+(define nfa
+ (automaton
+ (list (transition 1 #\a 2)
+ (transition 2 #\b 2)
+ (transition 1 #\a 3)
+ (transition 3 #\b 4)
+ (transition 4 #\a 3)
+ (transition 2 #\a 4))
+ 1
+ (list 2 3)))
+
+
+(accepts nfa "aba")
+(lwords "ab" nfa 3)
diff --git a/exams/finite-automata/task3_4_en.pdf b/exams/finite-automata/task3_4_en.pdf
new file mode 100644
index 0000000..cf782f8
Binary files /dev/null and b/exams/finite-automata/task3_4_en.pdf differ
diff --git a/exams/least-common-ancestor/index.md b/exams/least-common-ancestor/index.md
new file mode 100644
index 0000000..b5cbc17
--- /dev/null
+++ b/exams/least-common-ancestor/index.md
@@ -0,0 +1,227 @@
+---
+outline: deep
+title: "Exam Task: Least Common Ancestor"
+subtitle: "From: Exam 3 - 2023"
+---
+
+# Least Common Ancestor
+
+Suppose we have a binary tree $t$. For any two nodes $x,y$ in the tree $t$, the *least common
+ancestor* of $x$ and $y$ is defined as the node $z$ satisfying the following two conditions:
+
+1. $x$ and $y$ are descendants of $z$,
+2. if there is a node $z'$ having $x$ and $y$ as descendants, then $z$ is descendant of $z'$.
+
+To find the least common ancestor of two nodes $x$ and $y$ in a tree $t$, we follow the steps below:
+
+1. find the path $p_x$ from the root $r$ of $t$ to $x$ (i.e., a list of nodes starting in $r$ and
+ ending in $x$),
+2. find the path $p_y$ from $r$ to $y$,
+3. consider the common prefix of $p_x$ and $p_y$, the last node in the common prefix is the least
+ common ancestor.
+
+Consider, for example, the binary tree depicted in Figure~\ref{fig:tree}. The least common ancestor
+of $3$ and $5$ is $2$. Indeed, the path from the root $1$ to $3$ is $1,2,3$. The path from $1$
+to $5$ is $1,2,4,5$. Their common prefix is $1,2$ whose last element is $2$.
+
+Similarly, the least common ancestor of $5$ and $8$ is $1$. The least common ancestor of
+$7$ and $7$ is $7$.
+
+
+
+
+## Racket
+
+In Racket, implement a function `(common-ancestor x y tree)` that takes
+two nodes `x`, `y` and a binary tree `tree`, and returns the least common-ancestor
+of `x` and `y` in `tree`. If `x` or `y` does not belong to `tree`,
+the function returns \Verb|#f|.
+
+To represent binary trees in Racket, use the following structures:
+```scheme
+(struct node (val left right) #:transparent)
+(struct leaf (val) #:transparent)
+```
+Thus the leaves (nodes without children) are represented as, for instance, `(leaf 6)`.
+The nodes with children are represented as, for instance, `(node 1 (leaf 2) (leaf 3))`.
+
+To implement the function `common-ancestor`, implement first a function
+`(find-path x tree)` that finds a path from the root of `tree` to `x`.
+For example,
+```scheme
+(define tree (node 1 (node 2 (leaf 5) (leaf 6))
+ (node 3 (leaf 4) (leaf 7))))
+
+> (find-path 7 tree)
+'(1 3 7)
+```
+
+Your file should be called \texttt{task3.rkt} and should export the `find-path` and
+`common-ancestor` functions and the structures `node` and `leaf`.
+```scheme
+#lang racket
+
+(provide find-path
+ common-ancestor
+ (struct-out node)
+ (struct-out leaf))
+
+(struct node (val kids) #:transparent)
+(struct leaf (val) #:transparent)
+
+(define (find-path x tree)
+ ; Implement me!
+ )
+
+(define (common-ancestor x y tree)
+ ; Implement me!
+ )
+```
+
+### Example
+
+```scheme
+(define tree2 (node 1 (node 2 (leaf 3)
+ (node 4 (leaf 5)
+ (leaf 6)))
+ (node 7 (leaf 8)
+ (leaf 9))))
+
+> (common-ancestor 3 5 tree2)
+2
+
+> (common-ancestor 3 15 tree2)
+#f
+```
+
+### Hints
+
+To find the common prefix of two lists, use the function `(take-common-prefix lst1 lst2)`.
+
+::: details Solution
+```scheme
+#lang racket
+
+(provide find-path
+ common-ancestor
+ (struct-out node)
+ (struct-out leaf))
+
+(struct node (val left right) #:transparent)
+(struct leaf (val) #:transparent)
+
+(define tree (node 1 (node 2 (leaf 5) (leaf 6)) (node 3 (leaf 4) (leaf 7))))
+
+(define (find-path x tree)
+ (match tree
+ [(leaf y) (if (equal? x y)
+ (list x)
+ '())]
+ [(node y l r) (if (equal? x y)
+ (list x)
+ (let* ([pl (find-path x l)]
+ [pr (find-path x r)]
+ [both (append pl pr)])
+ (if (null? both)
+ '()
+ (cons y both))))]))
+
+(define (common-ancestor x y tree)
+ (define px (find-path x tree))
+ (define py (find-path y tree))
+ (define common (take-common-prefix px py))
+ (if (null? common)
+ #f
+ (last common)))
+```
+:::
+
+## Haskell
+
+In \textbf{Haskell}, implement a function
+`commonAncestor :: Eq a => a -> a -> Tree a -> Maybe a` that takes
+two nodes and a binary tree, and returns the least common ancestor of these two nodes.
+If it does not exist, the function returns `Nothing`.
+
+To represent binary trees in Haskell, use the following data type:
+```haskell
+data Tree a = Leaf a
+ | Node a (Tree a) (Tree a) deriving (Eq,Show)
+```
+Thus the leaves (nodes without children) are represented as, for instance, `Leaf 6`.
+The nodes with children are represented as, for instance,
+`Node 1 (Leaf 2) (Leaf 3)`.
+
+To implement the function `commonAncestor`, implement first a function\\
+`findPath :: Eq a => a -> Tree a -> [a]` that finds for a given node and a binary tree
+the path from the root to that node. For example,
+```haskell
+tree = Node 1 (Node 2 (Leaf 5) (Leaf 6)) (Node 3 (Leaf 4) (Leaf 7))
+
+> findPath 7 tree
+[1,3,7]
+```
+
+Your file should be called `Task4.hs` and should export the `commonAncestor`,
+`findPath` functions and the type `Tree`.
+
+```haskell
+module Task4 (findPath, commonAncestor, Tree(..)) where
+
+data Tree a = Leaf a
+ | Node a (Tree a) (Tree a) deriving (Eq,Show)
+
+findPath :: Eq a => a -> Tree a -> [a]
+-- implement me!
+
+commonAncestor :: Eq a => a -> a -> Tree a -> Maybe a
+-- implement me!
+```
+
+### Example
+
+```haskell
+tree2 = Node 1 (Node 2 (Leaf 3)
+ (Node 4 (Leaf 5)
+ (Leaf 6)))
+ (Node 7 (Leaf 8)
+ (Leaf 9))
+
+> commonAncestor 3 5 tree2
+Just 2
+
+> commonAncestor 3 15 tree2
+Nothing
+```
+
+::: details Solution
+```haskell
+module Task4 (findPath, commonAncestor, Tree(..)) where
+
+data Tree a = Leaf a
+ | Node a (Tree a) (Tree a) deriving (Eq,Show)
+
+tree = Node 1 (Node 2 (Leaf 5) (Leaf 6)) (Node 3 (Leaf 4) (Leaf 7))
+tree2 = Node 1 (Node 2 (Leaf 3)
+ (Node 4 (Leaf 5)
+ (Leaf 6)))
+ (Node 7 (Leaf 8)
+ (Leaf 9))
+
+findPath :: Eq a => a -> Tree a -> [a]
+findPath x (Leaf y) | x == y = [x]
+ | otherwise = []
+findPath x (Node y l r) | x == y = [x]
+ | otherwise = let pl = findPath x l
+ pr = findPath x r
+ in if null (pl ++ pr) then []
+ else y:(pl++pr)
+
+commonAncestor :: Eq a => a -> a -> Tree a -> Maybe a
+commonAncestor x y t | null common = Nothing
+ | otherwise = Just $ last common
+ where px = findPath x t
+ py = findPath y t
+ common = [x | (x,y) <- zip px py, x==y ]
+```
+:::
diff --git a/exams/least-common-ancestor/main_en.pdf b/exams/least-common-ancestor/main_en.pdf
new file mode 100644
index 0000000..4981c44
Binary files /dev/null and b/exams/least-common-ancestor/main_en.pdf differ
diff --git a/exams/least-common-ancestor/task3.reference.rkt b/exams/least-common-ancestor/task3.reference.rkt
new file mode 100644
index 0000000..919b04b
--- /dev/null
+++ b/exams/least-common-ancestor/task3.reference.rkt
@@ -0,0 +1,32 @@
+#lang racket
+(provide find-path
+ common-ancestor
+ (struct-out node)
+ (struct-out leaf))
+
+(struct node (val left right) #:transparent)
+(struct leaf (val) #:transparent)
+
+(define tree (node 1 (node 2 (leaf 5) (leaf 6)) (node 3 (leaf 4) (leaf 7))))
+
+(define (find-path x tree)
+ (match tree
+ [(leaf y) (if (equal? x y)
+ (list x)
+ '())]
+ [(node y l r) (if (equal? x y)
+ (list x)
+ (let* ([pl (find-path x l)]
+ [pr (find-path x r)]
+ [both (append pl pr)])
+ (if (null? both)
+ '()
+ (cons y both))))]))
+
+(define (common-ancestor x y tree)
+ (define px (find-path x tree))
+ (define py (find-path y tree))
+ (define common (take-common-prefix px py))
+ (if (null? common)
+ #f
+ (last common)))
\ No newline at end of file
diff --git a/exams/least-common-ancestor/task4.reference.hs b/exams/least-common-ancestor/task4.reference.hs
new file mode 100644
index 0000000..882ad04
--- /dev/null
+++ b/exams/least-common-ancestor/task4.reference.hs
@@ -0,0 +1,28 @@
+module Task4 (findPath, commonAncestor, Tree(..)) where
+
+data Tree a = Leaf a
+ | Node a (Tree a) (Tree a) deriving (Eq,Show)
+
+tree = Node 1 (Node 2 (Leaf 5) (Leaf 6)) (Node 3 (Leaf 4) (Leaf 7))
+tree2 = Node 1 (Node 2 (Leaf 3)
+ (Node 4 (Leaf 5)
+ (Leaf 6)))
+ (Node 7 (Leaf 8)
+ (Leaf 9))
+
+findPath :: Eq a => a -> Tree a -> [a]
+findPath x (Leaf y) | x == y = [x]
+ | otherwise = []
+findPath x (Node y l r) | x == y = [x]
+ | otherwise = let pl = findPath x l
+ pr = findPath x r
+ in if null (pl ++ pr) then []
+ else y:(pl++pr)
+
+commonAncestor :: Eq a => a -> a -> Tree a -> Maybe a
+commonAncestor x y t | null common = Nothing
+ | otherwise = Just $ last common
+ where px = findPath x t
+ py = findPath y t
+ common = [x | (x,y) <- zip px py, x==y ]
+
\ No newline at end of file
diff --git a/exams/least-common-ancestor/tree.tex b/exams/least-common-ancestor/tree.tex
new file mode 100644
index 0000000..e697455
--- /dev/null
+++ b/exams/least-common-ancestor/tree.tex
@@ -0,0 +1,27 @@
+\documentclass[border=0pt]{standalone}
+\usepackage{graphicx}
+\usepackage{fancyvrb}
+\usepackage{xcolor}
+\usepackage{bm}
+\usepackage{pgf}
+\usepackage{tikz}
+
+
+\definecolor{myGray}{rgb}{0.85,0.85,0.85}
+
+\begin{document}
+
+\begin{tikzpicture}[level/.style = {level distance = 1.5cm, sibling distance = 1cm},
+ level 1/.style={sibling distance=2cm}]
+ \node {$1$}
+ child {node {$2$}
+ child {node {$3$}}
+ child {node {$4$}
+ child {node {$5$}}
+ child {node {$6$}}}}
+ child {node {$7$}
+ child {node {$8$}}
+ child {node {$9$}}};
+\end{tikzpicture}
+
+\end{document}
diff --git a/exams/n2-knights/index.md b/exams/n2-knights/index.md
index 256c514..dcf3acd 100644
--- a/exams/n2-knights/index.md
+++ b/exams/n2-knights/index.md
@@ -17,7 +17,7 @@ looks like the letter "L".
The $N^2$-knights puzzle concerns placing $O(N^2)$ knights on an $n \times n$ chessboard so that no
two knights can attack each other. Below, you can see a valid configuration for a 8x8 board.
-
+
Determine the validity of $N^2$-knights board configurations.
diff --git a/exams/photo-skyscraper/index.md b/exams/photo-skyscraper/index.md
new file mode 100644
index 0000000..0a8983c
--- /dev/null
+++ b/exams/photo-skyscraper/index.md
@@ -0,0 +1,146 @@
+---
+outline: deep
+title: "Exam Task: Photographing Skyscrapers"
+subtitle: "From: Exam 1 - 2023"
+---
+
+# Photographing Skyscrapers
+
+You are an avid photographer that is obsessed with regular structures and you want to take pictures
+of cities that are built on regular grids. It turns out that you are also really into roof tops so
+you want to see as many of them in your pictures as possible. Naturally, you wonder from which side
+of a given city (North/South/East/West) you should be taking the picture. Luckily a befriended
+architect gave you maps of the cities you want to photograph. The maps are very simplified and can
+be represented as lists of lists of integers, so for example in Scheme:
+
+```scheme
+; north
+(define city
+ '((3 0 3 7 3)
+ (2 5 5 1 2)
+ (6 5 3 3 2)
+ (3 3 5 4 9)
+ (3 5 3 9 0)))
+; south
+```
+Every number represents the height of a building.
+
+A roof is *visible* if all other roofs between it and the edge of the grid are *smaller* than it.
+Only consider roofs in the same row or column. The first roof at the edge of a grid is always
+visible.
+
+
+## Racket
+
+In Racket, write a function `(best-view city)` that outputs the direction with the most roofs
+visible, along with the number of roofs visible from that direction. The direction should be one of
+four symbols: `'N`, `'S`, `'E`, and `'N`. The result should be a pair of the format
+`'(direction . number)`.
+
+```scheme
+(define city
+ '((3 3 3)
+ (1 2 3)
+ (1 2 3)))
+
+; 'N has 3 roofs, 'S has 5, 'E has 3, and 'W is the best with 7
+> (best-view city)
+'(W . 7)
+```
+
+Your file should be called `task3.rkt` and should `provide` the `best-view` function.
+```scheme
+#lang racket
+
+(provide best-view)
+
+(define (best-view city)
+ ; Implement me!
+ )
+```
+
+::: details Solution
+```scheme
+#lang racket
+(provide best-view)
+
+(define (visible-roofs-row row [height 0] [n 0])
+ (if (empty? row)
+ n
+ (if (< height (car row))
+ (visible-roofs-row (cdr row) (car row) (+ n 1))
+ (visible-roofs-row (cdr row) height n))))
+
+(define (transpose mat) (apply map list mat))
+
+
+(define (visible-roofs city dir)
+ (define m
+ (match dir
+ ['W city]
+ ['E (map reverse city)]
+ ['N (transpose city)]
+ ['S ((compose (curry map reverse) transpose) city)]))
+ (apply + (map visible-roofs-row m)))
+
+
+(define (best-view city)
+ (define views (list (list 'N (visible-roofs city 'N))
+ (list 'S (visible-roofs city 'S))
+ (list 'E (visible-roofs city 'E))
+ (list 'W (visible-roofs city 'W))))
+ (define (inner m v) (if (< (cadr m) (cadr v)) v m))
+ (define sol (foldl inner (list 'None 0) views))
+ (cons (car sol) (cadr sol)))
+```
+:::
+
+
+
+## Haskell
+
+In Haskell, write a function `bestView city` that outputs the direction with the most roofs visible,
+along with the number of roofs visible from that direction. The direction should be one of four
+characters: `'N'`, `'S'`, `'E'`, or `'W'`. The result should be a pair in the format `(direction,
+number)`.
+
+```haskell
+city = [[3, 3, 3],
+ [1, 2, 3],
+ [1, 2, 3]]
+
+-- 'N' has 3 roofs, 'S' has 5, 'E' has 3, and 'W' is the best with 7
+bestView city -- ('W', 7)
+```
+
+Your file should be called `Task4.hs` and should export the `bestView` function.
+```haskell
+module Task4 (bestView) where
+
+bestView :: [[Int]] -> (Char, Int)
+bestView city = ... -- Implement me!
+```
+
+::: details Solution
+```haskell
+ module Task4 (bestView) where
+
+import Data.List
+
+roofs xss = sum $ inner <$> xss
+ where
+ inner xs = length (group $ scanl1 max xs)
+
+morph 'N' = transpose
+morph 'S' = fmap reverse . transpose
+morph 'E' = fmap reverse
+morph _ = id
+
+bestView :: [[Int]] -> (Char, Int)
+bestView city =
+ let dirs = "NSEW"
+ views = roofs . (`morph` city) <$> dirs
+ opts = zip dirs views
+ in last $ sortOn snd opts
+```
+:::
diff --git a/exams/photo-skyscraper/main_en.pdf b/exams/photo-skyscraper/main_en.pdf
new file mode 100644
index 0000000..6820617
Binary files /dev/null and b/exams/photo-skyscraper/main_en.pdf differ
diff --git a/exams/photo-skyscraper/task3.reference.rkt b/exams/photo-skyscraper/task3.reference.rkt
new file mode 100644
index 0000000..2ead80c
--- /dev/null
+++ b/exams/photo-skyscraper/task3.reference.rkt
@@ -0,0 +1,32 @@
+#lang racket
+(provide best-view)
+
+(define (visible-roofs-row row [height 0] [n 0])
+ (if (empty? row)
+ n
+ (if (< height (car row))
+ (visible-roofs-row (cdr row) (car row) (+ n 1))
+ (visible-roofs-row (cdr row) height n))))
+
+(define (transpose mat) (apply map list mat))
+
+
+(define (visible-roofs city dir)
+ (define m
+ (match dir
+ ['W city]
+ ['E (map reverse city)]
+ ['N (transpose city)]
+ ['S ((compose (curry map reverse) transpose) city)]))
+ (apply + (map visible-roofs-row m)))
+
+
+(define (best-view city)
+ (define views (list (list 'N (visible-roofs city 'N))
+ (list 'S (visible-roofs city 'S))
+ (list 'E (visible-roofs city 'E))
+ (list 'W (visible-roofs city 'W))))
+ (define (inner m v) (if (< (cadr m) (cadr v)) v m))
+ (define sol (foldl inner (list 'None 0) views))
+ (cons (car sol) (cadr sol)))
+
diff --git a/exams/photo-skyscraper/task4.reference.hs b/exams/photo-skyscraper/task4.reference.hs
new file mode 100644
index 0000000..2a948e2
--- /dev/null
+++ b/exams/photo-skyscraper/task4.reference.hs
@@ -0,0 +1,19 @@
+module Task4 (bestView) where
+
+import Data.List
+
+roofs xss = sum $ inner <$> xss
+ where
+ inner xs = length (group $ scanl1 max xs)
+
+morph 'N' = transpose
+morph 'S' = fmap reverse . transpose
+morph 'E' = fmap reverse
+morph _ = id
+
+bestView :: [[Int]] -> (Char, Int)
+bestView city =
+ let dirs = "NSEW"
+ views = roofs . (`morph` city) <$> dirs
+ opts = zip dirs views
+ in last $ sortOn snd opts
diff --git a/img/building-trees-sequence.svg b/img/building-trees-sequence.svg
new file mode 100644
index 0000000..ed58f8e
--- /dev/null
+++ b/img/building-trees-sequence.svg
@@ -0,0 +1,114 @@
+
+
diff --git a/img/finite-automata-dfa.svg b/img/finite-automata-dfa.svg
new file mode 100644
index 0000000..67bde60
--- /dev/null
+++ b/img/finite-automata-dfa.svg
@@ -0,0 +1,121 @@
+
+
diff --git a/img/least-common-ancestor-tree.svg b/img/least-common-ancestor-tree.svg
new file mode 100644
index 0000000..ef993ce
--- /dev/null
+++ b/img/least-common-ancestor-tree.svg
@@ -0,0 +1,69 @@
+
+