diff --git a/.dockerignore b/.dockerignore
index c0f6f7fb39a464028bf6a0fb5d5e5c2056230210..1df9886013ca9a4b75e45dbcd72e723da31b4229 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,12 +1,9 @@
 .git/*
 .git*
-Dockerfile
-docker-run.sh
+.mdbook-plantuml-cache
 .dockerignore
 .idea/*
-.jekyll-cache/*
-**/.jekyll-cache/*
-_site/*
-assets/resized/*
-uml/*
+book/
+Dockerfile
+docker-run.sh
 README.md
diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b9f9def10c7394483f676677159bdf0709b439a0
--- /dev/null
+++ b/.github/workflows/docker-build.yml
@@ -0,0 +1,44 @@
+on:
+  push:
+    branches: [ master ]
+  workflow_dispatch:
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    permissions:
+      packages: write
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 10
+
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v2
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v2
+
+      - name: Login to ghcr registry
+        uses: docker/login-action@v1
+        with:
+          registry: ghcr.io
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Prepare version info
+        run: |
+          echo "LATEST_COMMIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
+
+      - name: Build and push Docker images
+        uses: docker/build-push-action@v4
+        with:
+          context: .
+          file: ./Dockerfile
+          tags: |
+            ghcr.io/marcobuster/sweng:${{ env.LATEST_COMMIT_SHA }}
+            ghcr.io/marcobuster/sweng:latest
+          push: true
diff --git a/.github/workflows/mdbook-deploy.yml b/.github/workflows/mdbook-deploy.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9d4ce52292b00ac328740ea89eb357e937e1cc5d
--- /dev/null
+++ b/.github/workflows/mdbook-deploy.yml
@@ -0,0 +1,103 @@
+name: Deploy mdBook site to Pages
+
+on:
+  # Runs on pushes targeting the default branch
+  push:
+    branches: [ master ]
+
+  # Allows you to run this workflow manually from the Actions tab
+  workflow_dispatch:
+
+# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+permissions:
+  contents: read
+  pages: write
+  id-token: write
+
+# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
+# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
+concurrency:
+  group: "pages"
+  cancel-in-progress: false
+
+jobs:
+  # Build job
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: Install apt requirements
+        run: |
+          sudo apt-get update -y
+          sudo apt-get install -y wget default-jre default-jdk graphviz libssl-dev pkg-config
+
+      - name: Restore from cache
+        uses: actions/cache/restore@v3
+        id: cache-restore
+        with:
+          path: |
+            plantuml.jar
+            ~/.cargo/bin/
+            ~/.cargo/registry/index/
+            ~/.cargo/registry/cache/
+            ~/.cargo/git/db/
+          key: ${{ runner.os }}
+
+      - name: Install mdBook
+        if: steps.cache-restore.outputs.cache-hit != 'true'
+        run: |
+          curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh
+          rustup update
+          cargo install mdbook
+          cargo install mdbook-plantuml
+
+      - name: Download PlantUML
+        if: steps.cache-restore.outputs.cache-hit != 'true'
+        run: |
+          wget https://github.com/plantuml/plantuml/releases/download/v1.2022.13/plantuml-1.2022.13.jar -O plantuml.jar
+
+      - name: Install PlantUML
+        run: |
+          echo "#!/bin/bash" > plantuml ; echo "java -jar $PWD/plantuml.jar \"\$@\"" >> plantuml
+          sudo mv plantuml /usr/bin/plantuml
+          sudo chmod +x /usr/bin/plantuml
+
+      - name: Save PlantUML to cache
+        id: cache-save
+        uses: actions/cache/save@v3
+        with:
+          path: |
+            plantuml.jar
+            ~/.cargo/bin/
+            ~/.cargo/registry/index/
+            ~/.cargo/registry/cache/
+            ~/.cargo/git/db/
+          key: ${{ steps.cache-restore.outputs.cache-primary-key }}
+
+      - name: Setup Pages
+        id: pages
+        uses: actions/configure-pages@v3
+
+      - name: Build with mdBook
+        run: mdbook build
+
+      - name: Fix assets path
+        run: grep -rl 'src="/assets/' book/ | xargs sed -i 's/src="\/assets\//src="\/sweng\/assets\//g'
+
+      - name: Upload artifact
+        uses: actions/upload-pages-artifact@v1
+        with:
+          path: ./book
+
+  # Deployment job
+  deploy:
+    environment:
+      name: github-pages
+      url: ${{ steps.deployment.outputs.page_url }}
+    runs-on: ubuntu-latest
+    needs: build
+    steps:
+      - name: Deploy to GitHub Pages
+        id: deployment
+        uses: actions/deploy-pages@v2
diff --git a/.gitignore b/.gitignore
index c37f544fe5006aeb55cf6b06aa6ba4c5919ecf49..a8f434c9d8689b974ecd9113ecb82028f5a16419 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,8 @@ public/
 resized/
 .vscode/
 uml/
-?/
+book
+
+# PlantUML plugin
+mdbook-plantuml-img/
+.mdbook-plantuml-cache/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 79a1961db75e8c48d5f87b3dab7975a5ed973945..0000000000000000000000000000000000000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-image: ruby:3.1
-
-variables:
-  JEKYLL_ENV: production
-  LC_ALL: C.UTF-8
-
-before_script:
-  - bundle install
-
-build:
-  stage: build
-  script:
-    - apt-get update -y
-    - apt-get install -y wget default-jre default-jdk graphviz
-    - wget https://github.com/plantuml/plantuml/releases/download/v1.2022.13/plantuml-1.2022.13.jar -O plantuml.jar
-    - echo "#!/bin/bash" > plantuml
-    - echo "java -jar $PWD/plantuml.jar \"\$1\" \"\$2\"" >> plantuml
-    - mv plantuml /usr/bin/plantuml
-    - chmod +x /usr/bin/plantuml
-    - sed -i "s/\/sweng/\/sweng\/$CI_COMMIT_REF_SLUG/g" _config.yml
-    - bundle exec jekyll build -d build
-  artifacts:
-    name: jekyll-build.zip
-    paths:
-      - build
diff --git a/Dockerfile b/Dockerfile
index 4dffa4b15d030c9dcf6688c44383f649027881fd..423ca3e6dd7ecaf2c0aa9c5ae75e128e9ca0a329 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,23 +1,28 @@
-FROM ruby:3.1
+# plantuml image
+FROM ubuntu AS plantuml
 
-# Install graphviz and PlantUML
 RUN apt-get update -y 
 RUN apt-get install -y wget default-jre default-jdk graphviz
 RUN wget https://github.com/plantuml/plantuml/releases/download/v1.2022.13/plantuml-1.2022.13.jar -O plantuml.jar
 RUN echo "#!/bin/bash" > plantuml ; echo "java -jar $PWD/plantuml.jar \"\$1\" \"\$2\"" >> plantuml
 RUN mv plantuml /usr/bin/plantuml
-RUN chmod +x /usr/bin/plantuml 
+RUN chmod +x /usr/bin/plantuml
 
-# Install Ruby project dependencies (Jekyll and plugins)
-WORKDIR /usr/src/app
+# main image
+FROM rust:slim-buster
+
+RUN apt-get update && \
+    apt-get install --no-install-recommends -y \
+    libssl-dev pkg-config
+RUN cargo install mdbook
+RUN cargo install mdbook-plantuml
 
-COPY Gemfile .
-COPY Gemfile.lock .
-RUN bundle install
+COPY --from=plantuml /usr/bin/plantuml /usr/bin/plantuml
+
+EXPOSE 3000
+WORKDIR /usr/src/app
 
 # Copy project files
 COPY . .
 
-EXPOSE 4000
-EXPOSE 35729
-ENTRYPOINT ["bundle", "exec", "jekyll", "serve", "--host", "0.0.0.0"]
+ENTRYPOINT ["mdbook"]
diff --git a/Gemfile b/Gemfile
deleted file mode 100644
index 161f047a7d4ab64460c97010c72a955b4e227aa5..0000000000000000000000000000000000000000
--- a/Gemfile
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-# gem "rails"
-
-gem "jekyll", "~> 4.2"
-
-gem "webrick", "~> 1.7"
-
-gem "jekyll-toc"
-
-gem "jekyll-responsive-image"
-
-gem "jekyll-plantuml"
diff --git a/Gemfile.lock b/Gemfile.lock
deleted file mode 100644
index 4ac477dc5af200abbdd4f6b71a1f105986c70918..0000000000000000000000000000000000000000
--- a/Gemfile.lock
+++ /dev/null
@@ -1,84 +0,0 @@
-GEM
-  remote: https://rubygems.org/
-  specs:
-    addressable (2.8.1)
-      public_suffix (>= 2.0.2, < 6.0)
-    colorator (1.1.0)
-    concurrent-ruby (1.1.10)
-    em-websocket (0.5.3)
-      eventmachine (>= 0.12.9)
-      http_parser.rb (~> 0)
-    eventmachine (1.2.7)
-    ffi (1.15.5)
-    forwardable-extended (2.6.0)
-    http_parser.rb (0.8.0)
-    i18n (1.12.0)
-      concurrent-ruby (~> 1.0)
-    jekyll (4.2.2)
-      addressable (~> 2.4)
-      colorator (~> 1.0)
-      em-websocket (~> 0.5)
-      i18n (~> 1.0)
-      jekyll-sass-converter (~> 2.0)
-      jekyll-watch (~> 2.0)
-      kramdown (~> 2.3)
-      kramdown-parser-gfm (~> 1.0)
-      liquid (~> 4.0)
-      mercenary (~> 0.4.0)
-      pathutil (~> 0.9)
-      rouge (~> 3.0)
-      safe_yaml (~> 1.0)
-      terminal-table (~> 2.0)
-    jekyll-plantuml (1.3.4)
-      jekyll (> 2.0)
-    jekyll-responsive-image (1.6.0)
-      jekyll (>= 2.0, < 5.0)
-      rmagick (>= 2.0, < 5.0)
-    jekyll-sass-converter (2.2.0)
-      sassc (> 2.0.1, < 3.0)
-    jekyll-toc (0.17.1)
-      jekyll (>= 3.9)
-      nokogiri (~> 1.11)
-    jekyll-watch (2.2.1)
-      listen (~> 3.0)
-    kramdown (2.4.0)
-      rexml
-    kramdown-parser-gfm (1.1.0)
-      kramdown (~> 2.0)
-    liquid (4.0.3)
-    listen (3.7.1)
-      rb-fsevent (~> 0.10, >= 0.10.3)
-      rb-inotify (~> 0.9, >= 0.9.10)
-    mercenary (0.4.0)
-    nokogiri (1.13.8-x86_64-linux)
-      racc (~> 1.4)
-    pathutil (0.16.2)
-      forwardable-extended (~> 2.6)
-    public_suffix (5.0.0)
-    racc (1.6.0)
-    rb-fsevent (0.11.2)
-    rb-inotify (0.10.1)
-      ffi (~> 1.0)
-    rexml (3.2.5)
-    rmagick (4.3.0)
-    rouge (3.30.0)
-    safe_yaml (1.0.5)
-    sassc (2.4.0)
-      ffi (~> 1.9)
-    terminal-table (2.0.0)
-      unicode-display_width (~> 1.1, >= 1.1.1)
-    unicode-display_width (1.8.0)
-    webrick (1.7.0)
-
-PLATFORMS
-  x86_64-linux
-
-DEPENDENCIES
-  jekyll (~> 4.2)
-  jekyll-plantuml
-  jekyll-responsive-image
-  jekyll-toc
-  webrick (~> 1.7)
-
-BUNDLED WITH
-   2.3.23
diff --git a/README.md b/README.md
index aa09fbd1d9ba8ab3c26e1f55c3562264bc459be7..428727f25351b08470b02ef7c3d9155c204f68b3 100644
--- a/README.md
+++ b/README.md
@@ -10,14 +10,6 @@ I _maintainer_ ufficiali sono:
 - Daniele Ceribelli
 - Matteo Mangioni
 
-e gli altri contributori sono stati:
-- Matteo Yon
-- Francesco Protospataro
-- Ilyaas Ouardi
-- Armani Islam
-- Mattia Mendecino
-- Andrea Cambiaghi
-
 ma _chiunque_ può contribuire e proporre _Merge Request_. 
 
 Nel file [AUTHORS.md](./AUTHORS.md) sono presenti tutti i contributori per ogni lezione.
@@ -30,306 +22,4 @@ Tutti i contenuti sono rilasciati sotto licenza [Creative Commons BY-NC-SA 4.0](
 
 ## Visualizzare
 
-Gli appunti sul branch `master` e sui branch `lezioni/*` sono [automaticamente compilati](https://gitlab.di.unimi.it/silab-gang/appunti-deploy) in HTML e disponibili su [https://appunti.studentiunimi.it/sweng/](https://appunti.studentiunimi.it/sweng/).
-
-## Contribuire
-
-### Stack tecnologico
-
-Tutti i contenuti sono scritti in Markdown e quindi convertiti in HTML autoamticamente da [Jekyll](https://jekyllrb.com/). 
-Le pagine su appunti.studentiunimi.it sono automaticamente _compilate_ in HTML da un workflow di GitLab CI, ma solo quando approdano sul branch `master` o su un branch `lezioni/*`.
-Una volta compilate viene lanciato un webhook a uno [script Python](https://gitlab.di.unimi.it/silab-gang/appunti-deploy/-/blob/master/handler.py) che scarica i file di build e li inserisce nella cartella corrispondente al branch. 
-Un container Docker di nginx (dietro Traefik) si occuperà quindi di servirli sul web e gestire l'autenticazione.
-
-Utilizziamo alcuni plugin di Jekyll per consentire di utilizzare LaTeX, immagini responsive e tabelle.
-Per strutture complesse, è possibile embeddare dell'HTML (e del CSS) nel file Markdown.
-
-> ### CONSIGLIATO: installazione di tutte le dipendenze tramite Docker
-> Prima di iniziare, è necessario avere Docker installato. Quindi:
-> 1. entra nel branch `master` e sincronizzalo con l'ultima versione remota:
->
->       ```
->       $ git switch master
->       $ git pull
->       ```
-> 
-> 2. costruisci l'immagine Docker: 
-> 
->       ```
->       $ docker build -t appunti_sweng .
->       ```
-> 3. crea un container ed eseguilo, ricondandoti di:
->       - mappare le porte 4000/tcp e 35729/tcp sul tuo host;
->       - mappare la cartella del progetto a `/usr/src/app` nel container;
->       - mappare correttamente l'utente;
->       - aggiungere a Jekyll l'opzione `--live-reload`.
->
->       In ambiente UNIX, ho creato uno script che permette di fare tutte le cose di cui sopra con un comando. Per eseguirlo fare:
->
->       ```
->       $ ./docker-run.sh
->       ```
-
-Aprendo la pagina https://localhost:4000/sweng nel nostro browser potremo visualizzare un'anteprima della pagina HTML compilata, aggiornata ad ogni modifica del file Markdown originale.
-È estremamente consigliato arrivare a questo punto prima di continuare: non inviare patch prima di aver verificato che Jekyll compili il file in una pagina sensata.
-
-Oltre a Jekyll, è naturalmente necessario avere Git installato sulla propria macchina.
-Come editor di testo, consigliamo VSCode (meglio ancora [VSCodium](https://vscodium.com/)) ma qualsiasi va bene. 
-
-### Regole base di Git
-
-- Utilizziamo GitLab e non GitHub perché abbiamo iniziato con GitLab e non abbiamo motivo per cambiare.
-- Abilita l'[autenticazione a due fattori](https://docs.gitlab.com/ee/user/profile/account/two_factor_authentication.html). 
-- Consigliamo l'utilizzo dell'[autenticazione SSH](https://docs.gitlab.com/ee/user/ssh.html) con GitLab. 
-- Imposta il tuo nome e cognome reale; per esempio: `git config --global user.name "Carlo Bellettini"`;
-- Utilizza la tua email universitaria, se vuoi; per esempio: `git config --global user.email "carlo.bellettini@unimi.it"`. Ricordati di aggiungere l'email al tuo account GitLab.
-- Consigliamo di impostare e attivare la [firma dei commit](https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/) tramite GPG.
-
-### The _"Silab Gang" Notes Engineering Development Process_
-Per ogni __argomento__ (spesso corrispondente a una _lezione_) N...
-![](https://i.imgur.com/tJhGSaN.png)
-
-Ad ogni argomento corrisponde un file Markdown sotto la directory di Jekyll `_posts/`. 
-
-#### Organizzazione dei branch
-
-La gestione dei branch è simile a GitFlow, ma non è uguale.
-Osserviamo le tipologie di branch:
-- `master`: contiene l'ultima versione stabile di tutti gli appunti. Ad ogni commit viene azionata una GitLab CI che aggiorna la pagina su GitLab Pages. Solo i maintainer possono mergiare su questo branch. 
-- `lezioni/N` (ma forse avremmo dovuto chiamarli `argomenti/N`): contiene l'ultima versione _instabile_ di uno specifico argomento. 
-I contributori della lezione `N` pulleranno dal branch `lezioni/N` per sincronizzare i contributi degli altri nel proprio branch, e mergieranno (o chiederanno di mergiare) i propri contributi nel branch `lezioni/N`. Nel nome c'è un _leading zero_: `lezioni/04`, `lezioni/12`.
-- __branch utente__: iniziano con il nome dell'utente (lower case e breve) e sono utilizzati come _"sandbox"_ personale. 
-
-Tutti i branch devono essere creati partendo da `master`.
-È consentito e apprezzato il fast forward in caso di merge banali. 
-
-Essendo i branch `lezioni/N` condivisi, è __NECESSARIO__ aggiornare il branch con il remoto facendo `git pull`. Prima di mergiare in `lezioni/N`, quindi:
-1. entra nel branch `lezioni/N`: `git switch lezioni/N`;
-2. scarica le ultime modifiche: `git pull`;
-3. entra nel tuo branch: `git switch mio/branch`;
-4. mergia il branch `lezioni/N`: `git merge lezioni/N`;
-5. risolvi gli eventuali conflitti;
-6. entra nel branch `lezioni/N`;
-7. mergia il tuo branch: `git merge lezioni/N`;
-8. carica le tue modifiche: `git push lezioni/N`.
-
-> `lezioni/N` contiene sempre l'___ultima versione instabile___ e tutti i contributori la utilizzano come riferimento per quell'argomento.
-> I contributi non mergiati in `lezioni/N` non saranno considerati da nessuno e sono quindi inutili.
-> In ogni caso, non si committa mai direttamente a `lezioni/N` ma prima si passa sempre per un _branch utente_.
-
-#### Esempio
-Lo studente Carlo Bellettini prende appunti durante la (sua?) lezione 5 e crea un branch `carlo/05-appunti`. Anche lo studente Mattia Monga prende appunti e pubblica le modifiche su `mattia/05-appunti`. 
-Carlo, da bravo contributore, si impegna a integrare gli appunti; crea il branch `carlo/05-integrazione` e mergia inanzitutto i suoi appunti (`carlo/05-appunti`) quindi quelli di Mattia (`mattia/05-appunti`). 
-
-Il secondo merge da parte di Carlo degli appunti di Mattia causerà sicuramente dei conflitti, che Carlo dovrà risolvere: non è codice, è testo, e due studenti prenderanno gli appunti in modo completamente diverso!
-Il concetto stesso di _integrazione_ è proprio questo. 
-
-Una volta terminato il lavoro, Carlo mergierà il suo branch `carlo/05-integrazione` in `lezioni/05`, quindi aprirà una Merge Request da `lezioni/05` verso il branch `master`.
-
-Inizia il processo di _review_: altri contributori (ovvero tutti a parte Carlo) controlleranno la correttezza e la __completezza__ (!) degli appunti proposti. 
-Se (ancora, per esempio) Marco trova dei problemi, può creare un proprio branch `marco/05-review` partendo dal branch `lezioni/05`, committare le proprie proposte e quindi rimegiarle in `lezioni/05`.
-
-Infine, una volta che tutti i reviewer sono contenti, la Merge Request viene mergiata in master e gli appunti vengono aggiunti in GitLab Pages.
-
-### Issues e Merge Requests
-
-Per coordinare il lavoro tra di noi, utilizziamo principalmente la funzione "Issue" di GitLab. 
-Tutte le issues sono elencate [qui](https://gitlab.com/silab-gang/sweng/-/issues).
-
-C'è una issue per ogni argomento. Ogni issue...
-- ha un titolo con il numero (corrispondente all'`N` nei nomi di branch) e al nome dell'argomento;
-- ha una descrizione, contentente i riferimenti alle lezioni relative all'argomento (come la data) e altre note opportune (_"il prof. ha spiegato il pattern Observer in questa lezione"_, ...);
-- ha un label per tracciare lo stato nel processo (Da Fare / In esecuzione / In attesa di review / Fatto); 
-- ha un epico per tracciare il progresso dei [4 macro argomenti](https://gitlab.com/silab-gang/sweng/-/milestones/) del corso.
-- ha un utente assegnato: solitamente è l'integratore principale della issue.
-
-Nelle issue si può discutere e coordinare il lavoro, ma le review si fanno nelle merge request.
-Le osservazioni sul _processo_ si fanno nell'issue, quelle sul _contenuto_ nella merge request.
-
-Le merge request sono collegate alla relativa issue semplicemente citandola.
-È possibile utilizzare la [revisione GitLab](https://docs.gitlab.com/ee/user/project/merge_requests/reviews/) per indicare i problemi: se trovi un problema sei invitato a risolverlo subito, per velocizzare il processo.
-
-## Convenzioni Jekyll
-
-Il Markdown scritto su Jekyll è particolare e richiede l'utilizzo di alcune convenzioni, specialmente per lavorare insieme.
-
-Jekyll è orientato alla creazione di blog personali, quindi ogni _argomento_ (pagina) è, secondo la terminologia di Jekyll, un _blog post_.
-Questo ha implicazioni fastidiose: per esempio, è necessario assegnare una data ad ogni _"post"_ anche se non ha molto senso se si sta parlando di appunti.
-Per convenzione, per ogni _post_ utilizziamo sempre come data (e ora) quello della prima lezione in cui l'argomento è stato introdotto.
-
-### Nomi di file e intestazione
-
-Tutti i _post_ vanno creati nella cartella `_posts/`. 
-Ogni file all'interno di `posts/` DEVE avere come il seguente nome: `YYYY-MM-DD-titolo-argomento.md` dove YYYY-MM-DD è la __data__ del post in quel formato e `titolo-argomento` il titolo (breve) che comparirà nell'URL. 
-
-All'inizio di ogni post Markdown DEVE ESSERE PRESENTE il _Jekyll header_.
-Di seguito un esempio:
-```
----
-layout: post
-title:  "[01] Introduzione"
-date:   2022-10-03 14:30:00 +0200
-toc:    true
----
-```
-
-Il numero nel titolo racchiuso tra parentesi quadre deve essere uguale al numero di argomento N assegnato nell'issue e nei nomi di branch.
-Il parametro `toc: true` serve per generare automaticamente la Table Of Contents all'inizio del file HTML compilato.
-
-Se non vedete un post che avete creato sul vostro Jekyll, molto probabilmente state sbagliando una di queste convenzioni.
-
-È possibile che durante la prima fase di presa degli appunti durante la lezione vengano utilizzati dei nomi di file diversi. 
-È compito degli integratori sceglierne uno adatto e degli altri contributori adottarlo.
-
-### Immagini
-
-Per includere immagini utilizziamo il plugin `jekyll-responsive-image`, che le ottimizza per tutti i dispositivi.
-
-Le immagini sono da mettere nella cartella `assets/` e tracciate con Git (altrimenti gli altri non le vedranno!). 
-Per convenzione nostra, i nomi dei file immagine è `N_nome-immagine.png` dove `N` è il numero di lezione giù più volte citato in questa guida.
-
-Per includere un'immagine, è sufficiente inserire nel Markdown la seguente stringa e Jekyll si occuperà ad inserire l'immagine.
-
-```
-{% responsive_image path: 'assets/N_nome-immagine.png' %}
-```
-
-L'immagine deve essere grande almeno 800 pixel in larghezza.
-Per convertire un'immagine più piccola, puoi utilizzare il seguente comando (richiede ImageMagick installato):
-
-```bash
-$ convert assets/N_nome-immagine.png -filter point -resize 810 assets/N_nome-immagine.png
-```
-
-### Diagrammi UML
-
-(Quasi) tutti i diagrammi UML mostrati durante le lezioni dal prof. Bellettini sono generati utilizzando [PlantUML](https://plantuml.com/), uno strumento open source che genera diagrammi in formato vettoriale partendo da del semplice testo.
-È quindi perfetto per il nostro caso d'uso (_pun intendend_).
-
-La sintassi per generare un diagramma dal Markdown di Jekyll è la seguente:
-
-```
-{% plantuml %}
-Object <|-- ArrayList
-Object : equals()
-ArrayList : Object[] elementData
-ArrayList : size()
-{% endplantuml %}
-```
-
-Informazioni complete sulla sintassi con esempi sono sul sito di PlantUML.
-
-Oltre al plugin, per generare i diagrammi è necessario installare l'eseguibile `plantuml`. Nei sistemi UNIX-like: 
-1. in una cartella che vuoi (come nella HOME), scarica il file `.jar` con `$ wget https://github.com/plantuml/plantuml/releases/download/v1.2022.13/plantuml-1.2022.13.jar -O plantuml.jar`;
-2. crea un file chiamato `/usr/bin/plantuml` avente come contenuto
-```bash
-#!/bin/bash
-java -jar /path/to/plantuml.jar "$1" "$2"
-```
-3. rendi il file eseguibile: `$ sudo chmod +x /usr/bin/plantuml`.
-
-Se possibile, __cerca sempre di utilizzare un diagramma UML al posto di uno screenshot__.
-
-## Convenzioni di stile e contenuto
-
-Gli appunti devono essere chiari, concisi ma __completi__.
-L'obiettivo è creare la _bibbia_ del corso: idealmente studiandola da zero si dovrebbe arrivare al 30L.
-
-In tale prospettiva proponiamo una guida alle fasi di integrazione e di review che chiarifichi che cosa dev'essere presente negli appunti e lo stile di scrittura consigliato.
-
-Naturalmente, queste indicazioni valgono per gli appunti proposti per il branch master: per gli appunti presi a lezione è assolutamente OK essere vaghi o brevi.
-
-### Guida all'integrazione
-
-La fase di integrazione degli appunti dovrebbe servire per riunire gli appunti di tutti i partecipanti in un unico documento.
-Per agevolare la fase di review e riscrittura, tuttavia, questo non può limitarsi a un semplice _merge_ dei rispettivi file: l'integratore ha il compito di fornire a colui che dovrà riscrivere gli appunti la miglior base possibile su cui lavorare. \
-Ecco dunque alcuni consigli utili in tal senso:
-
-- Assicurarsi che __CI SIA TUTTO__: idealmente la fase di review dovrebbe solo fare "refactoring" degli appunti senza aggiungere nessun concetto, per cui è espressa responsabilità dell'integratore assicurarsi che il risultato finale sia assolutamente completo in quanto nessuno controllerà più i contenuti.
-
-- __Una frase, una riga__: al termine di ciascuna frase (_ndr. una proposizione terminata da punto_) andare a capo in Markdown.
-Questo infatti non spezza il paragrafo, come si può vedere dalla preview, ma agevola moltissimo il versioning con Git in quanto ogni frase viene così trattata come una linea di codice indipendente dalle altre.
-
-- Assicurarsi che le stesse cose non siano dette in più punti diversi e, nel caso, integrarle tra di loro;
-
-- Tenere i propri appunti sottomano per accertarsi che ogni concetto citato a lezione sia riportato: è chiaro che all'esame viene chiesto _tutto_, compresi i riferimenti esterni, per cui occorre includere negli appunti ogni nozione rilevante;
-
-- Organizzare gli argomenti in maniera logica, evitando salti logici in avanti e in indietro per agevolare il lavoro di review;
-
-- Sfruttare le potenzialità di Markdown (_es. titoli di vario livello, tabelle, elenchi..._) e rispettarne per quanto possibile le convenzioni (_es. linea vuota dopo i titoli, nessuno spazio alla fine di una riga..._);
-
-- Tenere __sempre__ la preview di jekyll aperta per verificare che immagini e/o schemi vengano mostrati correttamente.
-
-### Guida alla review
-
-Gli appunti definitivi dovrebbero costituire un discorso omogeneo e fluido, come fossero un piccolo libro di testo. \
-Per fare ciò, ecco alcune accortezze di stile e consigli utili durante la fase di review e riscrittura: si tratta solo di indicazioni (_"Just rules"_ ^-^), per cui non sentitevi in dovere di seguirle alla lettera.
-
-#### <big>Contenuto</big>
-
-- Immaginare sempre di stare parlando con chi non sa nulla della materia: leggendo gli appunti dall'inizio alla fine si dovrebbe essere in grado di comprendere tutto.
-È quindi importante:
-
-  - non citare concetti senza che siano stati già spiegati precedentemente: se invece sono già stati spiegati può essere utile richiamarli con una formula del tipo _"Come sappiamo..."_ o _"Come abbiamo già visto..."_ seguita da un breve accenno al concetto;
-
-  - non dare per scontato nessuna conoscenza;
-
-- Se qualcosa è preso pari pari dalle slide può essere un campanello d'allarme.
-Conviene dunque farsi le seguenti domande:
-
-  - La frase si sposa bene con lo stile del discorso?
-  Come potrei riscriverla in modo da rendere il fluire del discorso più omogeneo?
-
-  - Il concetto espresso non è affrontato da nessun'altra parte?
-  Se sì, tale ripetizione è davvero necessaria e funzionale?
-
-- Mantenere convenzione "una frase, una riga" adottata nella fase di integrazione (_vd. sopra_): specialmente nella fase di review è importante che modificare una singola frase non comporti modificare interi paragrafi.
-
-- Tenere i propri appunti sottomano per verificare ulteriormente che non manchi nulla: sebbene la fase di integrazione dovrebbe in teoria creare un documento completo di tutto, può capitare che qualcosa sia sfuggito.
-
-#### <big>Stile</big>
-
-- Adottare una __sintassi semplice__: gli appunti dovrebbero essere completi ma facili da seguire;
-
-- Avere una qualche estensione di __controllo ortografico__ attiva (_es. Code Spell per vscode_);
-
-- Usare il più possibile l'__impersonale__: non _"possiamo fare X"_ ma _"si può fare X"_;
-
-- Sforzarsi di presentare gli argomenti nel modo più chiaro possibile, legandoli tra di loro in unico discorso logico.
-Per favorire questo approccio, ogni argomento dovrebbe essere affrontato nel modo seguente:
-
-    1. __Presentare il problema__: es. _"Spesso capita di dover gestire X, Y e Z"_;
-
-    2. __Discutere e analizzare il problema__: es. _"Il problema ha queste queste e queste caratteristiche, che non possiamo risolvere con quanto visto finora"_;
-
-    3. __Proporre la/le soluzione/i al problema e discuterle__, confrontandole se più di una: es. _"In un primo momento si potrebbe pensare di risolvere così; tuttavia questo approccio ha questi difetti. Ecco allora che si è pensato di fare quest'altro"_;
-
-    4. __Concludere con un breve riassunto__ su quanto visto, che servirà inoltre a introdurre il prossimo argomento: es. _"Abbiamo quindi visto come risolvere sta cosa; la soluzione pone però un nuovo problema..."_.
-
-- Preferire i discorsi omogenei agli elenchi: usare gli elenchi __SOLO__ quando necessari.
-Alcuni esempi dei pochi casi in cui un elenco è accettabile sono:
-
-  - un __elenco puntato__ quando si elencano più cose contrapposte tra di loro (_es. diversi approcci o soluzioni a un problema_)
-
-  - un __elenco numerato__ quando si specificano le varie fasi di un processo (_es. ciclo di vita del software_)
-
-- Utilizzare la __separazione in paragrafi__ in modo coscienzioso: all'interno di un paragrafo dovrebbe idealmente essere trattato _un unico concetto_.
-Diversi aspetti dello stesso concetto possono essere separati nello stesso paragrafo andando a capo (_con \ al termine della riga_), mentre quando si passa al concetto successivo è bene aprire un nuovo paragrafo.
-
-- Utilizzare la __corretta punteggiatura__.
-Può essere utile in tal senso rileggere mentalmente gli appunti appena scritti per assicurarsi che il discorso fluisca in modo scorrevole, ricordando che:
-
-  - la virgola (",") rappresenta una pausa brevissima utilizzata per riprendere fiato o per evidenziare tramite un inciso (_una frase compresa tra due virgole_) determinati concetti che espandono in modo significativo il discorso principale;
-
-  - i due punti (":") rappresentano una pausa breve e sono usati per introdurre elenchi o proposizioni strettamente correlate con quella principale;
-
-  - il punto e virgola (";") rappresentano una pausa media, e vanno utilizzati quando si vuole dare un legame debole alla proposizione con la precedente e al termine di ogni elemento di un elenco tranne l'ultimo (_dove invece si usa il punto_);
-
-  - le parentesi ("(...)") vengono utilizzate per incapsulare proposizioni che espandono la frase principale in modo non significativo: idealmente esse potrebbero essere saltate nella lettura senza togliere nulla al discorso.
-
-- Utilizzare il __grassetto__ per evidenziare concetti chiave e il _corsivo_ per sottolineare frasi importanti; all'interno delle parentesi si può inoltre utilizzare il corsivo per aumentare la rilevanza del contenuto.
-
-- Usare le congiunzioni correttamente per legare le frasi tra di loro (_es. dunque, perché, perciò, allora, in quanto..._);
-
-- __NON IGNORARE I COMMENTI__: si tratta di richieste di aiuto da parte di chi ha fatto l'integrazione, che chiede un consiglio su una determinata questione.
-È dunque importante che per il termine della review tale problema sia stato risolto: se vi trovate in difficoltà potete sempre chiedere sul gruppo!
+Gli appunti sul branch `master` sono automaticamente compilati in HTML e disponibili su [https://marcobuster.github.io/sweng/](https://marcobuster.github.io/sweng/).
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index 06c763919dcfde653b059a06157265ed9700c6d3..0000000000000000000000000000000000000000
--- a/_config.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-# Welcome to Jekyll!
-#
-# This config file is meant for settings that affect your whole blog, values
-# which you are expected to set up once and rarely need to edit after that.
-# For technical reasons, this file is *NOT* reloaded automatically when you use
-# 'jekyll serve'. If you change this file, please restart the server process.
-
-# Site settings
-title: Ingegneria del software
-email: your-email@domain.com
-description: > # this means to ignore newlines until "baseurl:"
-  Appunti del corso di Ingegneria del software
-baseurl: "/sweng" # the subpath of your site, e.g. /blog
-url: "/" # the base hostname & protocol for your site
-
-# Outputting
-permalink: /:year/:title
-
-# Build settings
-markdown: kramdown
-exclude:
-  - README.md
-  - Gemfile*
-  - .git*
-  - .dockerfile
-  - Dockerfile
-  - docker-run.sh
-  - plantuml.jar
-
-plugins:
-  - jekyll-toc
-  - jekyll-responsive-image
-  - jekyll-plantuml
-
-# responsive-image settings
-responsive_image:
-  template: _includes/image.html
-  sizes:
-    - width: 320
-    - width: 480
-    - width: 800
-  base_path: assets
-  output_path_format: assets/resized/%{width}/%{basename}
diff --git a/_includes/footer.html b/_includes/footer.html
deleted file mode 100644
index 18a137dad9cd91a4cdf203f3e0ae5613fe2ec91e..0000000000000000000000000000000000000000
--- a/_includes/footer.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<footer class="site-footer">
-
-  <div class="wrapper">
-
-    <h2 class="footer-heading">{{ site.title }}</h2>
-
-    <div class="footer-col-wrapper">
-      <div class="footer-col footer-col-1">
-        <ul class="contact-list">
-          <li>La riproduzione è riservata.</li>
-        </ul>
-      </div>
-
-      <div class="footer-col footer-col-3">
-        <p>{{ site.description }}</p>
-      </div>
-    </div>
-
-  </div>
-
-</footer>
diff --git a/_includes/head.html b/_includes/head.html
deleted file mode 100644
index 6b542fb42a10c667ae2e2e6788c31679d94ba97f..0000000000000000000000000000000000000000
--- a/_includes/head.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<head>
-  <meta charset="utf-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="viewport" content="width=device-width, initial-scale=1">
-
-  <title>{% if page.title %}{{ page.title | escape }}{% else %}{{ site.title | escape }}{% endif %}</title>
-  <meta name="description" content="{% if page.excerpt %}{{ page.excerpt | strip_html | strip_newlines | truncate: 160 }}{% else %}{{ site.description }}{% endif %}">
-
-  <link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}">
-  <link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
-  <link rel="alternate" type="application/rss+xml" title="{{ site.title }}" href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}">
-
-  <script type="text/javascript" async
-    src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-MML-AM_CHTML">
-  </script>
-  <script type="text/javascript"
-    src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js">
-  </script>
-  <script type="text/javascript" async
-    src="https://cdnjs.cloudflare.com/ajax/libs/imager.js/0.5.1/Imager.min.js">
-  </script>
-
-  <script type="text/javascript" async>
-    $(document).ready(() => {
-      $(".plantuml").parent().addClass("plantuml-parent");
-    })
-  </script>
-</head>
diff --git a/_includes/header.html b/_includes/header.html
deleted file mode 100644
index 33efca6ad7cf7778b765cfe8bcd179dbee648c02..0000000000000000000000000000000000000000
--- a/_includes/header.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<header class="site-header">
-
-  <div class="wrapper">
-
-    <a class="site-title" href="{{ site.baseurl }}/">{{ site.title }}</a>
-
-    <nav class="site-nav">
-      <a href="#" class="menu-icon">
-        <svg viewBox="0 0 18 15">
-          <path fill="#424242" d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z"/>
-          <path fill="#424242" d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z"/>
-          <path fill="#424242" d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z"/>
-        </svg>
-      </a>
-
-      <div class="trigger">
-        {% for my_page in site.pages %}
-          {% if my_page.title %}
-          <a class="page-link" href="{{ my_page.url | prepend: site.baseurl }}">{{ my_page.title }}</a>
-          {% endif %}
-        {% endfor %}
-        <a class="page-link" href="https://gitlab.com/silab-gang{{ site.baseurl }}">Codice sorgente</a>
-      </div>
-    </nav>
-
-  </div>
-
-</header>
diff --git a/_includes/icon-github.html b/_includes/icon-github.html
deleted file mode 100644
index e501a16b18780f2f5beb70d992a5e7f0635f65fb..0000000000000000000000000000000000000000
--- a/_includes/icon-github.html
+++ /dev/null
@@ -1 +0,0 @@
-<a href="https://github.com/{{ include.username }}"><span class="icon icon--github">{% include icon-github.svg %}</span><span class="username">{{ include.username }}</span></a>
diff --git a/_includes/icon-github.svg b/_includes/icon-github.svg
deleted file mode 100644
index 4422c4f5dcc3b5046aa2fd13910880249bbe5e88..0000000000000000000000000000000000000000
--- a/_includes/icon-github.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg viewBox="0 0 16 16"><path fill="#828282" d="M7.999,0.431c-4.285,0-7.76,3.474-7.76,7.761 c0,3.428,2.223,6.337,5.307,7.363c0.388,0.071,0.53-0.168,0.53-0.374c0-0.184-0.007-0.672-0.01-1.32 c-2.159,0.469-2.614-1.04-2.614-1.04c-0.353-0.896-0.862-1.135-0.862-1.135c-0.705-0.481,0.053-0.472,0.053-0.472 c0.779,0.055,1.189,0.8,1.189,0.8c0.692,1.186,1.816,0.843,2.258,0.645c0.071-0.502,0.271-0.843,0.493-1.037 C4.86,11.425,3.049,10.76,3.049,7.786c0-0.847,0.302-1.54,0.799-2.082C3.768,5.507,3.501,4.718,3.924,3.65 c0,0,0.652-0.209,2.134,0.796C6.677,4.273,7.34,4.187,8,4.184c0.659,0.003,1.323,0.089,1.943,0.261 c1.482-1.004,2.132-0.796,2.132-0.796c0.423,1.068,0.157,1.857,0.077,2.054c0.497,0.542,0.798,1.235,0.798,2.082 c0,2.981-1.814,3.637-3.543,3.829c0.279,0.24,0.527,0.713,0.527,1.437c0,1.037-0.01,1.874-0.01,2.129 c0,0.208,0.14,0.449,0.534,0.373c3.081-1.028,5.302-3.935,5.302-7.362C15.76,3.906,12.285,0.431,7.999,0.431z"/></svg>
diff --git a/_includes/icon-gitlab.html b/_includes/icon-gitlab.html
deleted file mode 100644
index 49ed37941799b09d57515649b2edbd9b5bf27845..0000000000000000000000000000000000000000
--- a/_includes/icon-gitlab.html
+++ /dev/null
@@ -1 +0,0 @@
-<a href="https://gitlab.com/{{ include.username }}"><span class="icon icon--gitlab">{% include icon-gitlab.svg %}</span><span class="username">{{ include.username }}</span></a>
diff --git a/_includes/icon-gitlab.svg b/_includes/icon-gitlab.svg
deleted file mode 100644
index 4aa6f6f6b8074c5b1589500bb6ca4005cc3b4dd3..0000000000000000000000000000000000000000
--- a/_includes/icon-gitlab.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M15.97 9.058l-.895-2.756L13.3.842c-.09-.282-.488-.282-.58 0L10.946 6.3H5.054L3.28.842C3.188.56 2.79.56 2.7.84L.924 6.3.03 9.058c-.082.25.008.526.22.682L8 15.37l7.75-5.63c.212-.156.302-.43.22-.682"/></svg>
diff --git a/_includes/icon-twitter.html b/_includes/icon-twitter.html
deleted file mode 100644
index e623dbd6efc532519f62cca86e4509429018e92d..0000000000000000000000000000000000000000
--- a/_includes/icon-twitter.html
+++ /dev/null
@@ -1 +0,0 @@
-<a href="https://twitter.com/{{ include.username }}"><span class="icon icon--twitter">{% include icon-twitter.svg %}</span><span class="username">{{ include.username }}</span></a>
diff --git a/_includes/icon-twitter.svg b/_includes/icon-twitter.svg
deleted file mode 100644
index dcf660e7bb376738d0129a1f8e3e8acbc6ac2aca..0000000000000000000000000000000000000000
--- a/_includes/icon-twitter.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg viewBox="0 0 16 16"><path fill="#828282" d="M15.969,3.058c-0.586,0.26-1.217,0.436-1.878,0.515c0.675-0.405,1.194-1.045,1.438-1.809c-0.632,0.375-1.332,0.647-2.076,0.793c-0.596-0.636-1.446-1.033-2.387-1.033c-1.806,0-3.27,1.464-3.27,3.27 c0,0.256,0.029,0.506,0.085,0.745C5.163,5.404,2.753,4.102,1.14,2.124C0.859,2.607,0.698,3.168,0.698,3.767 c0,1.134,0.577,2.135,1.455,2.722C1.616,6.472,1.112,6.325,0.671,6.08c0,0.014,0,0.027,0,0.041c0,1.584,1.127,2.906,2.623,3.206 C3.02,9.402,2.731,9.442,2.433,9.442c-0.211,0-0.416-0.021-0.615-0.059c0.416,1.299,1.624,2.245,3.055,2.271 c-1.119,0.877-2.529,1.4-4.061,1.4c-0.264,0-0.524-0.015-0.78-0.046c1.447,0.928,3.166,1.469,5.013,1.469 c6.015,0,9.304-4.983,9.304-9.304c0-0.142-0.003-0.283-0.009-0.423C14.976,4.29,15.531,3.714,15.969,3.058z"/></svg>
diff --git a/_includes/image.html b/_includes/image.html
deleted file mode 100644
index 6c1ff079606e7eab3fa6f8678630ed0860dba3ae..0000000000000000000000000000000000000000
--- a/_includes/image.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% assign smallest = resized | sort: 'width' | first %}
-
-<div class="responsive-image" align="center" style="margin-bottom: 15px">
-    <img class="responsive-image__placeholder" src="{{ site.baseurl }}/{{ smallest.path }}">
-    <div class="responsive-image__delayed" data-src="{{ site.baseurl }}/assets/resized/{width}/{{ original.basename }}"></div>
-</div>
-
-<script>
-    $(document).ready(() => {
-        new Imager('.responsive-image__delayed', {
-            availableWidths: [{{ resized | map: 'width' | join: ', ' }}],
-            onImagesReplaced: function() {
-                $('.responsive-image__placeholder').hide()
-            }
-        });
-    });
-</script>
diff --git a/_layouts/default.html b/_layouts/default.html
deleted file mode 100644
index e4ab96fb0b93f94b18e15f00917f31bbe0ceeaa2..0000000000000000000000000000000000000000
--- a/_layouts/default.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-  {% include head.html %}
-
-  <body>
-
-    {% include header.html %}
-
-    <div class="page-content">
-      <div class="wrapper">
-        {{ content }}
-      </div>
-    </div>
-
-    {% include footer.html %}
-
-  </body>
-
-</html>
diff --git a/_layouts/page.html b/_layouts/page.html
deleted file mode 100644
index ce233ad75bfb7966cc211c065626d880aa46ef95..0000000000000000000000000000000000000000
--- a/_layouts/page.html
+++ /dev/null
@@ -1,14 +0,0 @@
----
-layout: default
----
-<article class="post">
-
-  <header class="post-header">
-    <h1 class="post-title">{{ page.title }}</h1>
-  </header>
-
-  <div class="post-content">
-    {{ content }}
-  </div>
-
-</article>
diff --git a/_layouts/post.html b/_layouts/post.html
deleted file mode 100644
index 96204913b02576dec1b79cefc97fdd5f953e090c..0000000000000000000000000000000000000000
--- a/_layouts/post.html
+++ /dev/null
@@ -1,15 +0,0 @@
----
-layout: default
----
-<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
-
-  <header class="post-header">
-    <h1 class="post-title" itemprop="name headline">{{ page.title }}</h1>
-    <p class="post-meta"><time datetime="{{ page.date | date_to_xmlschema }}" itemprop="datePublished">{{ page.date | date: "%b %-d, %Y" }}</time>{% if page.author %} • <span itemprop="author" itemscope itemtype="http://schema.org/Person"><span itemprop="name">{{ page.author }}</span></span>{% endif %}</p>
-  </header>
-
-  <div class="post-content" itemprop="articleBody">
-    {{ content | toc }}
-  </div>
-
-</article>
diff --git a/_posts/.gitkeep b/_posts/.gitkeep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/_posts/2022-10-03-introduzione.md b/_posts/2022-10-03-introduzione.md
deleted file mode 100644
index 13d1ea5cfb087411369d6a8742012d8ea908f1ed..0000000000000000000000000000000000000000
--- a/_posts/2022-10-03-introduzione.md
+++ /dev/null
@@ -1,324 +0,0 @@
----
-layout: post
-title: "[01] Introduzione"
-date:   2022-10-03 14:30:00 +0200
-toc: true
----
-
-# Informazioni logistiche
-- Non ci sarà lo streaming però ci sono le videolezioni
-- Teoria
-    - Lun 14:30-17:00 Aula 403
-    - Mer 14:30-17:00 Aula 403
-- Laboratorio
-    - Gio 13:30-17:30 due turni equivalenti
-        - Turno A matricole pari
-        - Turno B matricole dispari
-    - Due persone per computer, a coppia
-- Non c'è libro di testo, ma consigliati: 
-    - Software Engineering (Carlo Ghezzi, Dino Mandridi)
-    - Design Patterns (Eric Freeman, Elisabeth Robison)
-    - Handbook of Software and Systems Engineering (Albert Endres, Dieter Rombath)
-
-## Esami
-- __Laboratorio__
-    - prova pratica di laboratorio di 4 ore
-    - OPPURE _per chi segue tutti i laboratori_ ci saranno due laboratori valutati A COPPIE
-- __Teoria__ 
-    - prova orale
-- Prima di fare l'orale bisogna fare il laboratorio
-- La prova di laboratorio vale all'infinito
-
-# Introduzione 
-
-## Storia 
-Con la diffusione dei primi computer in ambito accademico, negli anni '50 e '60 si è subito colta la necessità di superare metodi di produzione "artigianale" del software: sebbene il cliente e il programmatore coincidessero e i programmi fossero prettamente matematici, si iniziavano già a vedere i primi problemi. Negli anni '70, si inizia dunque a pensare a dei metodi, dei processi e a degli strumenti che potessero migliorare e ___"assicurare"___ la __qualità del software__, sviluppando un approccio di tipo ingegneristico costituito da una serie di fasi.
-
-## Approccio ingegneristico
-1. __Target__: ci si prefigge un obiettivo da raggiungere.
-2. __Metric__: si definisce una metrica per misurare la qualità del software, ovvero quanto esso si avvicina al target prefissato.
-3. __Method, Process, Tool__: si provano una serie di metodi, processi e strumenti per avvicinarsi all'obiettivo.
-4. __Measurements__: si misura tramite la metrica stabilita se le strategie implementate sono state utili e quanto ci hanno avvicinato (o allontanato) all'obiettivo. A seconda dei risultati ottenuti vi sono due possibili strade:
-    - risultati soddisfacenti (_aumento delle metrica_) - accettiamo come buoni metodi e processi utilizzati.
-    - risultati insoddisfacenti (_diminuzione della metrica_)- ci sono dei peggioramenti o dei forti effetti collaterali, di conseguenza bisogna modificare il lavoro qualcosa: si possono cambiare target o metrica se ci si rende conto di non aver ben definito l'obiettivo, ma più comunemente bisogna rivedere i processi e metodi usati.
-
-Ma __che cosa si intede per target__? Gli obiettivi da raggiungere possono essere di due tipi: la risoluzione dei __problemi nella progettazione del software__ e l'assicurazione di una qualche __qualità che il software dovrà avere__. È dunque necessario chiedersi le seguenti domande:
-- Quali problemi ci sono?
-- Quali qualità deve avere il software?
-
-### Problemi principali
-Vediamo allora a questo punto alcuni dei problemi che possono insorgere durante lo sviluppo di software, partendo dal presupposto che una delle più grandi fonti di problemi sono le __persone__.  L'obiettivo della disciplina è infatti principalmente quello di risolvere i __problemi di comunicazione__, che possono essere:
-- tra il __programmatore__ e il __cliente__: sono esperti di domini diversi ed è difficile comprendersi;
-- tra un __programmatore__ e altri __programmatori__.
-
-Un'altra fonte di problemi sono le __dimensioni__ del software, che possono raggiungere valori molto elevati in termini di milioni di righe di codice e migliaia di _"anni uomo"_ di lavoro. Lo sviluppo software non è più piccolo e domestico, e questo crea chiaramente problemi di manutenzione e gestione della codebase.
-
-Il software è infine __facilmente malleabile__, ovvero modificabile nel tempo: il moltiplicarsi di versioni, evoluzioni e variazioni di target può creare non poche difficoltà.
-
-# Qualità
-Per fare fronte ai problemi sopracitati si sviluppano allora una serie di processi per lo sviluppo software: essi non assicurano la bontà del programma finito, ma possono assicurare la presenza di _proprietà desiderabili_ del prodotto, dette __qualità__. Le qualità del prodotto, che costituiscono a conti fatti un "valore per le persone", si dividono innanzitutto in due tipi:
-- __qualità esterne__: qualità che vengono colte dal cliente;
-- __qualità interne__: qualità che vengono esclusivamente colte dallo sviluppatore.
-
-Le qualità interne influenzano molto le qualità esterne (per esempio se ho un codice ottimizzato ed efficiente, il mio software produrrà i risultati più velocemente). Prima di vedere quali siano le proprietà auspicabili in un software, però, facciamo un'importante distinzione a livello terminologico tra __requisiti e specifiche__:
-- I __requisiti__ sono quello che il cliente vuole che il software faccia. Spesso sono cambiati in corso d'opera oppure sono espressi in modo sbagliato, per cui è necessaria un'interazione continua. 
-- Le __specifiche__ sono ciò che è stato formalizzato dal programmatore a partire dai requisiti: si tratta di una definizione più rigorosa di che cosa dovrà fare il software. Si noti però che se i requisiti sono stati espressi in modo non corretto anche le specifiche risulteranno inesatte (vd. <a href="#g1">G1</a>).
-
-Fatta questa doverosa distinzione, vediamo quali sono le qualità che un software dovrebbe idealmente possedere.
-
-## Qualità del software
-
-Un software di qualità deve <b><i>funzionare</i></b>, <b><i>essere bello</i></b> e <b><i>"farmi diventare ricco"</i></b>.
-
-<table style="margin-bottom: 20px">
-<thead>
-    <tr>
-        <th>Un software deve...</th>
-        <th>Qualità</th>
-        <th>Descrizione</th>
-    </tr>
-</thead>
-<tbody>
-    <tr>
-        <th rowspan="3"><i>Funzionare</i></th>
-        <th>Correttezza</th>
-        <td>
-            Un software è corretto se soddisfa la specifica dei suoi <i>requisiti funzionali</i>. Si tratta di una proprietà <i>"matematica"</i> e relativamente dimostrabile in modo formale.
-        </td>
-    </tr>
-    <tr>
-        <th>Affidabilità</th>
-        <td>
-            Un software è affidabile quando ci si può fidare del suo funzionamento, ovvero ci si può aspettare che faccia ciò che gli è stato chiesto.
-            Se è molto difficile perseverare la correttezza, in quanto si tratta una proprietà assoluta, l'affidabilità è invece relativa: un software può essere affidabile (o <i>dependable</i>) nonostante contenga qualche errore.
-        </td>
-    </tr>
-    <tr>
-        <th>Robustezza</th>
-        <td>
-            Un software è robusto se si comporta in modo accettabile anche in circostanze non previste nella specifica dei requisiti, senza generare effetti troppo negativi.
-        </td>
-    </tr>
-    <tr>
-        <th rowspan="3"><i>Essere bello</i></th>
-        <th>Usabilità</th>
-        <td>
-            Un software è usabile (o <i>user-friendly</i>) se i suoi utenti lo ritengono facile da utilizzare.
-            Si possono fare degli esperimenti (le grandi aziende lo fanno) per testare e quantificare l’usabilità del software ponendolo di fronte a dei soggetti umani (vd. <a href="#nn23">NN23</a>).
-        </td>
-    </tr>
-    <tr>
-        <th>Prestazioni</th>
-        <td>
-            Ad ogni software è richiesto un certo livello di prestazioni. L'efficienza è una qualità interna e misura come il software utilizza le risorse del computer; la performance, d'altro canto, è invece una qualità esterna ed è basata sui requisiti dell'utente. Essa ha effetto sull'usabilità, e spesso viene considerata alla fine dello sviluppo software visto che vari avanzamenti tecnologici possono efficientare algoritmi e processi prima troppo costosi.
-        </td>
-    </tr>
-    <tr>
-        <th>Verificabilità</th>
-        <td>
-            Un software è verificabile se le sue proprietà sono verificabili facilmente: è importante essere in grado di poter dimostrare la correttezza e la performance di un programma, e in tal senso la <b>leggibilità</b> del codice è fondamentale. 
-            La verifica può essere fatta con metodi formali o informali, come il testing.
-            È considerata una qualità interna, ma alcune volte può diventare una qualità esterna: per esempio, in ambiti in cui la sicurezza è critica il cliente può chiedere la verificabilità di certe proprietà.
-        </td>
-    </tr>
-    <tr>
-        <th rowspan="2"><i>Farmi diventare ricco</i></th>
-        <th>Riusabilità</th>
-        <td>
-            Le componenti del software che costruiamo dovrebbero essere il più riutilizzabili possibile così da risparmiare tempo in futuro: ciò può essere fatto non legandole troppo allo specifico contesto applicativo del software che stiamo sviluppando.
-            Con la proprietà di riusabilità, utilizziamo un prodotto per costruire - anche con modifiche minori - un altro prodotto (vd. <a href="#mi15">MI15</a>).
-        </td>
-    </tr>
-    <tr>
-        <th>Manutenibilità</th>
-        <td>
-            Per <i>manutenzione software</i> si intendono tutte le modifiche apportate al software dopo il rilascio iniziale.
-            Questa proprietà può essere vista come due proprietà separate:
-            <ul style="margin-bottom: 0;">
-                <li><b>Riparabilità</b>: un software è riparabile se i suoi difetti possono essere corretti con una quantità di lavoro ragionevole.</li>
-                <li><b>Evolvibilità</b>: indica la capacità del software di poter evolvere aggiugendo funzionalità. È importante considerare questo aspetto fin dall'inizio: studi rilevano come l'evolvibilità decresce con il passare delle release (vd. <a href="#l27-28">L27-28</a>).</li>
-            </ul>
-        </td>
-    </tr>
-</tbody>
-</table>
-
-### Leggi rilevanti
-
-<a id="g1"></a>
-__Prima legge di R.Glass (G1)__. 
-> La mancanza di requisiti è la prima causa del fallimento di un progetto.
-
-<a id="nn23"></a>
-__Legge di Nielsen-Norman (NN23)__.
-> L'usabilità è misurabile.
-
-<a id="mi15"></a>
-__Legge di McIlroy (MI15)__. 
-> Riutilizzare il software permette di incrementare la produttività e la qualità.
-
-<a id="l27-28"></a>
-__Leggi di M. Lehman (L27-28)__. 
-> Un sistema che viene utilizzato cambierà. 
-
-> Un sistema che evolve incrementa la sua complessita a meno che non si lavori appositamente per ridurla.
-
-## Qualità del processo
-> Un progetto è di qualità se segue un buon processo.
-
-Sappiamo che il prodotto è influenzato dal processo che viene utilizzato per svilupparlo, di conseguenza possiamo parlare anche di  __qualità del processo__.
-
-Anche un processo deve funzionare, essere essere bello e farmi diventare ricco, ma dobbiamo interpretare queste parole in maniera differente.
-
-Quali caretteristiche ha un processo di qualità?
-
-<table style="margin-bottom: 20px">
-<thead>
-    <tr>
-        <th>Un processo deve...</th>
-        <th>Qualità</th>
-        <th>Descrizione</th>
-    </tr>
-</thead>
-<tbody>
-    <tr>
-        <th rowspan="1"><i>Funzionare</i></th>
-        <th>Robustezza</th>
-        <td markdown="span">
-            Un processo deve poter resistere agli imprevisti, come la mancanza improvvisa di personale o il cambiamento delle specifiche.
-            Esistono certificazioni (<i>CMM: Capability Maturity Model</i>) che valutano la robustezza di alcuni processi aziendali e che vengono per esempio considerate nei bandi pubblici.
-        </td>
-    </tr>
-    <tr>
-        <th rowspan="1"><i>Essere bello</i></th>
-        <th>Produttività</th>
-        <td markdown="span">
-            La produttività di un team è molto meno della somma della produttività individuale dei suoi componenti. È una metrica difficile da misurare: conteggi come il numero di linee codice scritte o la quantità di <i>tempo-uomo</i> richiesta per un lavoro si rivelano spesso un po' fallaci (per esempio, la gravidanza umana non è un'attività parallelizzabile, ma si potrebbe dire che servono 9 mesi-donna per creare un bambino).
-        </td>
-    </tr>
-    <tr>
-        <th rowspan="1"><i>Farmi diventare ricco</i></th>
-        <th>Tempismo</th>
-        <td markdown="span">
-            Un processo deve consegnare il prodotto nei tempi stabiliti, in modo da rispettare i tempi del mercato. È spesso conveniente la tecnica dello <b>sviluppo incrementale</b>, ovvero la consegna frequente di parti sempre più grandi del prodotto (es. compilatore ADA): essa permette infatti di conquistare il cliente ancor prima di avere il prodotto finito. 
-        </td>
-    </tr>
-</tbody>
-</table>
-
-# Il processo di produzione del software
-Il processo che seguiamo per costruire, consegnare, installare ed evolvere il prodotto software, dall'idea fino alla consegna e al ritiro finale del sistema, è chiamato __processo di produzione software__.
-
-Innanzitutto occorre riconoscere diverse problematiche.
-- I __requisiti__ imposti dal cliente possono cambiare spesso.
-- Produrre software __non è _solo_ scrivere codice__ (alla Programmazione I).
-- Bisogna risolvere i __problemi di comunicazione__ tra tutte le diverse figure in gioco (tra sviluppatori, tra progettista e sviluppatori, ecc).
-- Bisogna essere __rigorosi__, anche se può essere difficile. Ci sono lati positivi e negativi: la rigorisità può facilitare la comprensione di ciò che bisogna fare ma implica  al contempo molta fatica extra, e viceversa.
-    > __Ipotesi di Bauer-Zemanek (BZh3)__: Metodi formali riducono in maniera significativa gli errori di progettazione, oppure permettono di eliminarli e risolverli prima.
-    
-    Trovare gli errori prima della fase di sviluppo permette di facilitarne la risoluzione e di risparmiare sia tempo che soldi: tanto prima si individua un errore, tanto più facile sarà risolverlo.
-- Ci sono __tanti aspetti__ da considerare, che andranno affrontati uno alla volta. Per parlare di aspetti diversi ho bisogno di metodi comunicazione diversi, che interessano ruoli diversi in tempi diversi (_Aspect Oriented Programming_).
-
-Tenendo a mente tutto queste problematiche è necessario decidere come organizzare l'attività di sviluppo software in modo da mitgarle. Per modellare un ciclo di vita del software, occorre dunque in primo luogo __identificare le varie attività necessarie__ e quindi:
-- deciderne le precedenze temporali;
-- decidere chi le debba fare.
-
-In particolare, ci si pone due domande:
-- cosa devo fare adesso?
-- fino a quando e come?
-
-L'ingegneria del software prova a rispondere a queste domande per individuare quali siano le fasi necessarie per sviluppare un software e quale sia la loro migliore disposizione temporale. È dunque bene specificare da subito che lo sviluppo di un programma non è solo coding: tale presupposto genera conseguenze disastrose.
-
-Inizialmente, infatti, nell'ambito dello sviluppo software è stato adottato il modello ___code-and-fix___, che consisteva nei seguenti passi:
-1. scrivi il codice;
-2. sistemalo per eliminare errori, migliorare funzionalità o aggiungere nuove funzionalità.
-
-Ben presto però questo modello si è dimostrato pesantemente inefficace in gruppi di lavoro complessi, specialmente quando il cliente non era più lo sviluppatore stesso ma utenti con poca dimestichezza con i computer, generando codice estremamente poco leggibile e manutenibile.
-
-Per organizzare meglio l'attività di sviluppo e non ricadere negli errori del passato gli ingegneri del software hanno dunque individuato diverse __fasi__ del ciclo di vita di un software che, combinate, permettessero di produrre del software di qualità. Diamo dunque un'occhiata a quelle principali.
-
-## Le fasi del ciclo di vita del software
-
-### Studio di fattibilità
-Lo studio di fattibilità è l'attività svolta prima che il processo di sviluppo inizi, per decidere se dovrebbe iniziare _in toto_.
-L'__obiettivo__ è quello di produrre un __documento in linguaggio naturale__ presentante diversi scenari di sviluppo con soluzioni alternative, con una discussione sui trade-off in termini di benefici e costi attesi.
-
-Più specificatamente, il documento include:
-- uno studio di diversi scenari di realizzazione, scegliendo:
-    - le architetture e l'hardware necessario;
-    - se sviluppare in proprio oppure subappaltare ad altri.
-- stima dei costi, tempi di sviluppo, risorse necessarie e benfici delle varie soluzioni.
-
-È spesso difficile fare un'analisi approfondita, a causa del poco tempo disponibile o di costi troppo elevati: spesso viene commissionata esternamente.
-
-### Analisi e specifica dei requisiti
-L'analisi e specifica dei requisiti è l'attività più critica e fondamentale del processo di produzione del software.
-L'obiettivo è la stesura di un ___documento di specifica___ <!-- ... -->.
-
-
-In questa fase i progettisti devono:
-- comprendere il __dominio applicativo__ del prodotto, dialogando con il cliente e la controparte tecnica;
-- identificare gli __stakeholders__, ovvero tutte le figure interessate al progetto, e studiarne le richieste. Spesso non si tratta di figure omogenee (può essere il _top manager_ fino al segretario) e le loro necessità sono molto diverse;
-- capire quali sono le __funzionalità richieste__: la domanda più importante che deve porsi il programmatore è il _cosa_ non il _come_; al cliente non devono infatti interessare gli aspetti tecnici e le scelte architetturali interne. Le __specifiche__ vanno quindi viste dal punto di vista del cliente.
-- stabilire un __dizionario comune__ tra cliente e sviluppatore che può anche far parte della specifica per agevolare la comunicazione;
-- definire __altre qualità__ eventualmente richieste dal cliente: per esempio, _"la centrale non deve esplodere"_ non è un dettaglio implementativo, ma un requisito. Queste ulteriori qualità, che non sempre sono solo esterne, si dicono __requisiti non funzionali__.
-
-Lo scopo del _documento di specifica_ è duplice: da una parte, deve essere analizzato e approvato da __tutti gli stakeholders__ in modo da verificare il soddisfacimento delle aspettative del cliente, e dall'altra è usato dai programmatori per sviluppare una soluzione che le soddisfi, fungendo da punto di partenza per il design.
-È un documento contrattuale e deve essere scritto in modo formale per evitare contestazioni contrattuali e ambiguità.
-
-Deve essere presente anche un __piano di test__, ovvero una collezione di collaudi che certificano la correttezza del lavoro: se questi test hanno esito positivo il lavoro viene pagato, altrimenti il progetto non viene accettato. A differenza dei lavori di altri tipi di ingegneria, per esempio l'ingegneria civile, dove il collaudo è diretto, nell'ingegneria del software è molto difficile collaudare tutti i casi e gli stati possibili.
-
-Un altro output di questa fase può essere anche il __manuale utente__, ovvero la _"vista esterna"_ (ciò che il cliente vuole vedere, evitando i dettagli implementativi) del sistema da progettare.
-
-> __Legge di David__: Il valore dei modelli che rappresentano il software da diversi punti di vista dipendono dal punto di vista preso (assunto), ma non c'è nessuna vista che è la migliore per ogni scopo.
-
-### Progettazione (design)
-Il _design_ è l'attività attraverso la quale gli sviluppatori software strutturano l'applicazione a diversi livelli di dettaglio.
-Lo scopo di questa fase è quello di scrivere un __documento di specifica di progetto__ contenente la descrizione dell'architettura software (i diversi linguaggi e viste). 
-
-Durante questa fase occorre quindi:
-- scegliere un'__architettura software di riferimento__;
-- __scomporre__ in moduli o oggetti gli incarichi e i ruoli: si tratta del cosiddetto _object oriented design_, non necessariamente accompagnato da object oriented programming;
-- __identificare i patterns__, ovvero problemi comuni a cui è già stata trovata una soluzione generale giudicata come _"bella"_ dalla comunità degli sviluppatori (ne vedremo un paio più avanti nel corso). I pattern favoriscono alcune qualità, come il design.
-
-### Programmazione e test di unità
-In questa fase le _"scatole nere"_ - i moduli o oggetti definiti al punto precedente - vengono realizzate e per ognuna di esse vengono definiti dei __test unitari__ che ne mostrano la correttezza.
-Vi è infatti spesso la brutta abitudine di non fare il testing durante lo sviluppo di ciascun componente, ma solamente alla fine di tutto: questa usanza è molto pericolosa perché un problema scoperto solo alla fine è molto più oneroso da risolvere.
-
-I singoli moduli vengono testati indipendentemente, anche se alcune funzioni da cui dipendono non sono ancora sono state implementate: per risolvere tale dipendenza si utilizzano allora moduli fittizzi (___stub___) che emulino le funzionalità di quelli mancanti.
-Altri moduli, detti ___driver___, forniscono invece una situazione su cui applicare il modulo che sto testando.
-Nei linguaggi più utilizzati esistono framework che facilitano le suddette operazioni al programmatore.
-
-L'obiettivo di questa fase è avere un __insieme di moduli__ separati __sviluppati indipendentemente__ con un'interfaccia concordata e __singolarmente verificati__.
-
-### Integrazione e test di sistema
-In questa fase i moduli singolarmente implementati e testati vengono __integrati__ insieme a formare il software finito. In alcuni modelli di sviluppo (come nello sviluppo incrementale) questa fase viene accorpata alla precedente.
-
-Nei test, i moduli _stub_ e _driver_ vengono sostituiti con i componenti reali formando un sistema sempre più grande fino ad ottenere il risultato richiesto.
-È poi fondamentale testare che l'intero programma funzioni una volta assemblato (non basta che le singole parti funzionino!): test di questo tipo vengono detti __test di integrazione__. 
-
-L'integrazione può essere adottata seguendo un approccio _top down_ o _bottom up_. La fase finale è l'___alpha testing___, ovvero il testing del sistema in condizioni realistiche.
-
-### Consegna, installazione e manutenzione
-Dopo aver completato lo sviluppo, il software deve essere __consegnato__ ai clienti. Prima di consegnarlo a tutti, si seleziona un gruppo di utenti per raccogliere ulteriore feedback; questa fase è chiamata ___beta testing___.
-
-L'__installazione__ (deployment) definisce il _run-time_ fisico dell'architettura del sistema. Per esempio, un servizio di rete potrebbe necessitare di apparecchiatura server da installare e particolari configurazioni.
-
-Infine, la __manutenzione__ può essere definita come l'insieme delle attività finalizzate a modificare il sistema dopo essere stato consegnato al cliente. La manutenzione può essere di tipo:
-- __correttivo__: sistemare errori nel sistema;
-- __adattivo__: adattare il software ai nuovi requisiti (vd. _evolvibilità_);
-- __perfettivo__: migliorare certi aspetti interni al programma senza modificare gli aspetti esterni. Serve per migliorare la futura manutenzione riducendo il cosiddetto _debito tecnico_.
-
-Come già detto, è necessario sviluppare avendo in mente la futura manutenzione di ciò che si sta scrivendo: infatti, il __costo__ della manutenzione concorre al costo del software in una misura spesso superiore al 60%.
-
-L'_output_ di questa fase è un __prodotto migliore__.
-
-### Altre attività
-Alle attività sopracitate se ne aggiungono altre:
-- __Documentazione__: può essere vista come attività trasversale. Per esempio, un documento di specificazione contenente diagrammi UML e una descrizione narrativa che spiega le motivazione dietro certe decisioni può essere il risultato principale della fase di progettazione.
-È un'attività spesso da procastinare, perché le specifiche possono cambiare spesso. In alcuni gruppi esistono delle figure che si occupano di questa attività, anche se può essere pericoloso: non tutti possono capire ciò che un programmatore ha creato.
-- __Verifica e controllo qualità__ (Quality Assurance): nella maggior parte dei casi, la verifica è svolta attraverso review e ispezioni. L'obiettivo è anticipare il prima possibile la scoperta e la sistemazione degli errori in modo da evitare di consegnare sistemi difettosi. Andrebbe fatta costantemente e non tutta alla fine.
-- __Gestione del processo__: gestione incentivi (premi di produzione), responsabilità, formazione del personale, perfezionamento del processo con l'esperienza guadagnata, eccetera. 
-- __Gestione delle configurazioni__: gestione delle relazioni inter-progettuali, ovvero delle risorse di sviluppo non appartenenti ad un singolo progetto. Un esempio potrebbe essere una libreria condivisa tra più progetti, i quali vorrebbero modificare la libreria stessa.
-
-Tutte queste diverse attività saranno specificate successivamente entrando nel dettaglio.
diff --git a/_posts/2022-10-05-Ciclo-Vita-Software.md b/_posts/2022-10-05-Ciclo-Vita-Software.md
deleted file mode 100644
index 2d1042b2fec7307c93e3f22ef4c0f246b163e725..0000000000000000000000000000000000000000
--- a/_posts/2022-10-05-Ciclo-Vita-Software.md
+++ /dev/null
@@ -1,282 +0,0 @@
----
-layout: post
-title: "[02] Modelli di ciclo di vita del software"
-date:   2022-10-05 14:30:00 +0200
-toc: true
----
-
-In questa lezione vedremo i principali modelli di ciclo di vita del software, ovvero famiglie di processi di sviluppo che si distinguono per il modo in cui le fasi di produzione viste nella scorsa lezione vengono organizzate in un processo unitario. Ognuno di tali modelli avrà i propri pro e i propri contro, ed è bene da subito capire che non esiste il modello giusto per ogni situazione.
-
-# Modelli sequenziali
-
-Il modo più semplice e immediato di organizzare le fasi del ciclo di vita di un software è sicuramente quello __sequenziale__: i vari passaggi vengono posti in un ordine prestabilito e vengono attraversati uno alla volta uno dopo l'altro. Da questa idea nascono i cosiddetti _modelli sequenziali_, di cui il più famoso è certamente il _modello a cascata_.
-
-## Modello a cascata
-
-### Caratteristiche e punti di forza
-
-{% responsive_image path: assets/02_waterfall-model.png %}
-
-Nato negli anni '50 ma diventato famoso solo negli anni '70 grazie allo sviluppo di un grosso software per la difesa area chiamato SAGE (_Semi-Automated Ground Environment_), il modello a cascata organizza le fasi in una serie di step sequenziali: fatto uno si passa al successivo fino ad arrivare alla fine, come in una sorta di _catena di montaggio_. Viene infatti forzata una __progressione lineare__ da una fase alla successiva; non è previsto in questo modello tornare indietro a uno step precedente.
-
-Sebbene varino molto da processo a processo, la maggior parte dei processi che segue il modello a cascata include almeno le seguenti fasi organizzate in quest'ordine:
-
-1. Requisiti
-2. Progetto
-3. Codifica
-4. Testing
-5. Prodotto
-
-Ognuno di tali step produce un output, detto __semilavorato__, che è dato come input allo step successivo. In virtù dell'affidamento su tali semilavorati intermedi il modello a cascata si dice __document-based__: tra una fase e l'altra si crea infatti un documento che è il mezzo di trasmissione dell'informazione. Questo aspetto permette una __buona separazione dei compiti__ tra i vari dipendenti che lavorano al progetto: ognuno è infatti specializzato in una singola fase e una volta prodotto il documento utile ad avviare la fase successiva il suo coinvolgimento nel progetto non è più necessario ed esso può essere assegnato ad altri lavori.
-
-La linearità del modello rende inoltre possibile __pianificare i tempi__ accuratamente e monitorare semplicemente lo stato di avanzamento in ogni fase: è infatti sufficiente stimare la durata di ogni fase per ottenere una stima del tempo di completamento dell'intero progetto. Si tratta però di una stima a senso unico: una volta finita una fase non è possibile ridurre il tempo speso, e in caso di inconvenienti l'unica opzione è cercare di assorbire il ritardo.
-
-### Criticità
-
-Sebbene il modello a cascata abbia il grande pregio di aver posto l'attenzione sulla comunicazione tra gli elementi del progetto in un momento storico in cui il modello di sviluppo più diffuso era di tipo _code-and-fix_, esso soffre di numerose criticità.
-
-In primo luogo il modello __non prevede una fase di manutenzione__ del software prodotto: esso assume di non dover apportare modifiche al progetto dopo averlo consegnato, e impedisce dunque di _"tornare indietro"_ in alcun modo. Ovviamente questa assunzione è un'illusione smentita nella quasi totalità nei casi: qualunque software è destinato ad evolvere, e più un software viene usato più cambia. Una volta finito lo sviluppo ciò che si può fare è rilasciare al più piccole patch, che tuttavia non fanno altro che disallineare la documentazione prodotta precedentemente con il software reale.
-
-Il modello soffre inoltre di una generale __rigidità__, che mal si sposa con la flessibilità naturalmente richiesta dall'ambiente di sviluppo software. In particolare, l'impossibilità di tornare indietro implica un __congelamento dei sottoprodotti__: una volta prodotto un semilavorato esso è fisso e non può essere modificato; questo è particolarmente critico per le stime e specifiche fatte durante le prime fasi, che sono fisiologicamente le più incerte.
-
-Infine, il modello a cascata adotta un approccio volto alla __monoliticità__: tutta la pianificazione è orientata ad un singolo rilascio, e l'eventuale manutenzione può essere fatta solo sul codice. Inutile dire che si tratta di una visione fallace, in quanto come già detto più volte il software è destinato ad essere modificato e ad evolvere.
-
-### _Who's Afraid of The Big Bad Waterfall?_
-
-> LIBRO: __The Leprechauns of Software Engineeering__ di Laurent Bossavit.
-
-In realtà, il modello a cascata non è mai stato veramente elogiato, ma è sempre stato utilizzato come paragone negativo per proporre altri modelli o variazioni.
-Nel corso del tempo la sua presentazione è stata erroneamente attribuita al paper [_"Managing the development of large software systems: concepts and techniques"_](https://dl.acm.org/doi/10.5555/41765.41801) di W.W. Royce, di cui veniva citata solo la prima figura: Royce stava a dire il vero presentando quel modello per descrivere la sua esperienza nello sviluppo software, per poi proporre altri concetti più moderni (come lo sviluppo incrementale) che non sono però mai stati colti dalla comunità scientifica.
-
-Anche noi utilizziamo il modello a cascata solo come paragone negativo, e in generale nell'ambiente di sviluppo software esso non è più applicato alla lettera. Alcuni suoi aspetti si sono però mantenuti come linee guida generali (es. l'ordine delle fasi); è infatti bene chiarire subito che esistono due tipi di modelli:
-
-- __prescrittivi__: forniscono delle indicazioni precise da seguire per svolgere un processo;
-- __descrittivi__: colgono certi aspetti e caratteristiche di particolari processi esistenti, ma non obbligano a seguirli in modo rigoroso.
-
-Tutti i modelli visti per ora ricadono perlopiù nell'ambito descrittivo, mentre i modelli AGILE che vedremo più avanti tendono ad essere più di tipo prescrittivo.
-
-### Riassunto pro e contro
-
-<table style="margin-bottom: 20px">
-    <thead>
-        <tr>
-            <th style="text-align:center">Pro</th>
-            <th style="text-align:center">Contro</th>
-        </tr>
-    </thead>
-    <tbody>
-        <tr>
-            <td>
-                <ul style="margin-top: 15px">
-                    <li>Document-based</li>
-                    <li>Buona suddivisione dei compiti</li>
-                    <li>Semplice pianificazione dei tempi</li>
-                </ul>
-            </td>
-            <td>
-                <ul style="margin-top: 15px">
-                    <li>Rigidità</li>
-                    <li>Congelamento dei sottoprodotti</li>
-                    <li>Monoliticità</li>
-                </ul>
-            </td>
-        </tr>
-    </tbody>
-</table>
-
-## Modello a V (denti di pesce cane)
-
-{% responsive_image path: assets/02_v-model.png %}
-
-Dal modello a cascata nascono poi numerose varianti che cercano di risolverne i vari problemi: tra queste spicca per rilevanza il __modello a V__, che introduce fondamentalmente una __più estesa fase di testing__.
-
-Nonostante sia ancora un modello sequenziale come il modello a cascata, nel modello a V vengono infatti evidenziati nuovi legami tra le fasi di sviluppo, che corrispondono alle attività di __verifica__ e __convalida__: alla fine di ogni fase si _verifica_ che il semilavorato ottenuto rispetti la specifica espressa dalla fase precedente, e inoltre si richiede la _convalida_ del fatto che esso sia in linea con i veri vincoli e necessità del cliente. Come si vede, questo modello pone l'accento sul rapporto con il cliente, che viene continuamente coinvolto con la richiesta di feedback su ciascun sottoprodotto generato.
-
-Volendo formalizzare, le due nuove attività introdotte sono dunque:
-
-- __verifica__ (freccie grigie): controlla la correttezza rispetto alla descrizione formale delle specifiche;
-- __validazione__ (freccie bianche): controlla la compatabilità del sistema con le esigenze del cliente tramite feedback continuo.
-
-# Modelli iterativi
-
-Osservando il modello a cascata e le sue varianti ci si è ben presto resi conto che la stringente sequenzialità delle fasi costituiva un grosso limite non conciliabile con la flessibilità richiesta dallo sviluppo software e con la naturale mutevolezza dei requisiti imposti dal cliente. Si inizia dunque a pensare di permettere agli sviluppatori di _ripetere_ alcune fasi più di una volta, ciclando su di esse fino a ottenere un prodotto soddisfacente: nascono così i primi __modelli interativi__.
-
-## Modello a cascata con singola retroazione
-
-{% responsive_image path: 'assets/02_waterfall-retroazione.png' %}
-
-Uno dei primi modelli iterativi è in realtà una variante del modello a cascata, in cui si permette di fare un'unico salto indietro; a parire da una fase si può cioè __ritornare alla fase precedente__: così, per esempio, si può _iterare_ tra _Codifica_ e _Testing_ fino a consegnare il prodotto.
-
-Anche in questo modello non si può però tornare indietro dalla consegna per eseguire attività di manutenzione; inoltre, l'introduzione di un'iterazione rende molto __più difficile pianificare__ il lavoro e monitorarne l'avanzamento: si tratta questa di una caratteristica condivisa da molti modelli iterativi.
-
-## Modello a fontana
-
-{% responsive_image path: 'assets/02_fountain-model.png' %}
-
-Nel 1993 nasce, in contrapposizione al modello a cascata, il cosiddetto __modello a fontana__, che amplia il concetto di iterazione permettendo in qualunque momento di __tornare alla fase iniziale__: se ci si accorge della presenza di errori si torna indietro all'inizio (_software pool_) e si ricontrollano tutte le fasi precedenti. Ovviamente questo non implica buttare tutto il lavoro già fatto, quanto piuttosto risolvere l'errore con un approccio che parta innanzitutto dalla modifica dei requisiti (se possibile), delle specifiche e solo dopo del codice, evitando di rattoppare solo quest'ultimo alla bell'e meglio come nel modello _code-and-fix_.
-
-Il modello a fontana è inoltre il primo in cui sono previste delle azioni dopo la consegna; dopo l'ultima fase (_programma in uso_), infatti, si aprono ancora due strade: __manutenzione ed evoluzione__. La consegna del prodotto non è quindi più l'atto finale, ma solo un altro step del processo: ecco quindi che si aprono le porte ad una __visione incrementale__ dello sviluppo software, che approfondiremo nel prossimo paragrafo.
-
-Anche qui si perdono purtroppo le garanzie sui tempi di sviluppo: una volta ritornato all'inizio per sistemare un errore non è infatti affatto detto che riuscirò a ritornare alla fase da cui sono partito, ma potrei imbattermi in altri errori durante le fasi precedenti costringendomi a iterare su di esse più di una volta.
-
-# Modelli incrementali
-
-Un modello incrementale è un particolare modello iterativo in cui nelle iterazioni è inclusa anche la consegna: questo permette di sviluppare il software a poco a poco, rilasciandone di volta in volta parti e componenti che costruiscano _incrementalmente_ il programma finito.
-
-Si noti la differenza tra incrementale e iterativo; si può parlare infatti di:
-
-- __implementazione iterativa__: dopo aver raccolto le specifiche e aver progettato il sistema, _iterativamente_ sviluppo i componenti, li integro nel prodotto finale, quindi consegno.
-- __sviluppo incrementale__: l'iteratività interessa tutte le fasi, comprese quelle di specifiche e realizzazione.  
-
-Lo sviluppo incrementale riconosce la criticità della variabilità delle richieste e la integra nel processo. 
-La manutenzione non è quindi più una particolarità ma è vista come normale e perfettamente integrata nel modello: in tal senso, la richiesta di una nuova feature o la correzione di un errore generano gli stessi step di sviluppo.
-
-## Modello prototipale
-
-Un particolare modello incrementale è quello protitipale: in questo modello viene introdotto il concetto di __protitipi usa e getta__ (_throw away_), interi programmi che vengono costruiti e poi vengono buttati via.
-
-Lo scopo del prototipo __non è consegnare__ un prodotto finito, ma __ricevere feedback__ dal cliente per essere sicuri di aver compreso a pieno i suoi requisiti, oppure testare internamente un'idea o uno strumento. Per questo motivo tali prototipi vengono costruiti fregandosene di correttezza, pulizia del codice, leggibilità eccetera.
-I protitipi possono dunque essere:
-
-- __pubblici__: per capire meglio i requisiti del cliente (vd. <a href="#b3">L3</a>);
-- __privati__: per esplorare nuovi strumenti, linguaggi, scelte per problemi difficili; inoltre, molto spesso una volta programmata una soluzione si capisce anche meglio il problema (_"do it twice"_).
-
-La tentazione coi prototipi pubblici può essere quella di consegnarli come prodotto finito, ma c'è il __rischio__ enorme di dover mantenere poi in futuro software non mantenibile, illeggibile e con altissimo debito tecnico.
-
-<a id="b3"></a>
-__Legge di Bohem (L3)__
-> La propotipizzazione riduce gli errori di analisi dei requisiti e di design, specialmente per le interfacce utente.
-
-## I problemi dei modelli incrementali
-
-Come già detto nessun modello è perfetto, e anche i modelli incrementali soffrono di alcuni problemi.
-
-Viene innanzitutto __complicato il lavoro di planning__: bisogna pianificare tutte le iterazioni e lo stato di avanzamento è meno visibile; inoltre, la ripetizione di alcune fasi richiede di avere sempre sul posto gli esperti in grado di eseguirle.
-Ad ogni iterazione, poi, dobbiamo rimettere mano a ciò che è stato fatto, in un processo che potrebbe non convergere mai a una versione finale.
-
-Ma cosa è un'iterazione, e quanto dura? Tagliare verticalmente sulle funzionalità non è infatti facile, soprattutto considerando che quando si consegna il prodotto esso dev'essere funzionante con tutti i layer necessari ed essere al contempo pensato per poter crescere con successivi attaccamenti. Ci sono dunque diversi rischi:
-
-- voler aggiungere troppe funzionalità nella prima iterazione;
-- overhead dovuto a troppe iterazioni;
-- avere un eccessivo overlapping tra le iterazioni: non si ha tempo di recepire il feedback dell'utente (es. Microsoft Office 2020 e 2019 vengono sviluppati contemporaneamente).
-
-### Pinball Life-Cycle
-
-{% responsive_image path: 'assets/02_pinball-life-cycle.png' %}
-
-Il _"modello meme"_ del Pinball Life-Cycle, creato da Ambler come critica ai modelli incrementali, estremizza queste problematiche: l'ordine in cui faccio le attività è casuale, incoltrollabile. Qualunque passo è possibile dopo qualunque altro, e non si possono imporre vincoli temporal: il processo è __non misurabile__.
-
-Si tratta ovviamente di una visione eccessivamente pessimistica, ma spesso nelle aziende non specializzate l'iter di sviluppo assomiglia effettivamente a questo.
-
-## Modelli trasformazionali
-
-{% responsive_image path: 'assets/02_transformational-models.png' %}
-
-Diametralmente opposti all'incubo del Pinball Life-Cycle troviamo i __modelli trasformazionali__: tali modelli pretendono infatti di controllare tutti i passi e i procedimenti in __modo formale__.
-
-Partendo dai requisiti scritti in linguaggio informale, tali modelli procedono tramite una sequenza di __passi di trasformazione__ dimostrabili tutti formalmente fino ad arrivare alla versione finale.
-Essi si basano infatti sull'idea che se le specifiche sono corrette e i passi di trasformazione sono dimostrati allora ottengo un programma corretto, ovvero di sicuro aderente alle specifiche di cui sopra. Inoltre, la presenza di una storia delle trasformazioni applicate permette un rudimentale versioning, con la possibilità di tornare indietro a uno stato precedente del progetto semplicemente annullando le ultime trasformazioni fatte.
-
-{% responsive_image path: 'assets/02_formal-transformations.jpg' %}
-
-Ad ogni passo si ottiene quindi un __protitipo__ che differisce dal prodotto finale per efficienza e completezza, ma che è possibile trasformare in un altro più efficiente e corretto. Non si tratta tuttavia di un processo totalmente automatico, anzi: ad ogni passo di "ottimizzazione", ovvero applicazione di una trasformazione, è richiesto l'intervento di un decisore umano che scelga che cosa ottimizzare.
-
-Viene quindi introdotto il concetto di __prova formale di correttezza__ delle trasformazioni applicate; per via di questo approccio molto matematico questo tipo di modelli è nella realtà applicato quasi solo negli ambienti di ricerca e produzione hardware.
-
-## <i>Meta</i>modello a spirale
-
-{% responsive_image path: 'assets/02_spiral-model.png' %}
-
-Introduciamo ora un metamodello, ovvero un modello che ci permette di rappresentare e discutere di altri modelli (una sorta di framework).
-
-Nel metamodello a spirale l'attenzione è posto sui __rischi__, ovvero sulla possibilità che qualcosa vada male (decisamente probabile nell'ambiente di sviluppo software).
-Per questo motivo il modello è di tipo incrementale e pone l'accento sul fatto che non abbia senso fare lo studio di fattibilità una sola volta, ma ad ogni iterazione serva una decisione. Le fasi generali sono dunque:
-
-- Determinazione di obiettivi, alternative e vincoli
-- Valutazione alternative, identificazione rischi (decido se ha senso andare avanti)
-- Sviluppo e verifica
-- Pianificazione della prossima iterazione
-
-Nella figura il raggio della spirale indica i __costi__, che ad ogni iterazione aumentano fisiologicamente.
-
-### Variante _"win-win"_
-
-Esiste una variante al modello a spirale che fa notare come i rischi ad ogni fase non sono solo rischi tecnologici ma anche __contrattuali__ con il cliente. Ad ogni iterazione bisogna dunque trovare con esso un punto di equilibrio _win-win_ in entrambi le parti "vincono" (o hanno l'illusione di aver vinto), così da far convergere tutti su un obiettivo comune.
-
-## Modello COTS (Component Off The Shelf)
-
-{% responsive_image path: 'assets/02_cots.png' %}
-
-Vediamo infine un modello che si concentra molto sulla __riusabilità__: si parte dalla disponibilità interna o sul mercato di moduli preesistenti sui quali basare il sistema, e che è dunque necessario solo integrare tra di loro.
-
-Non si creda che si tratti di un approccio facile: questo modello di design necessita di far dialogare componenti che non necessariamente comunicano già nel modo voluto.
-
-Si tratta tuttavia di un modello di sviluppo diverso perché richiede attività diverse. In particolare:
-
-- _Analisi dei requisiti_
-- ___Analisi dei componenti___: prima di progettare considero la disponibilità di componenti che implementano una parte o tutte le funzionalità richieste;
-- ___Modifica dei requisiti___: stabilisco se il cliente è disposto ad accettare un cambiamento nei requisiti necessario per utilizzare un componente particolare;
-- ___Progetto del sistema col riuso di componenti___: occorre progettare il sistema per far interagire componenti che non necessariamente sono stati originariamente progettati per interagire;
-- _Sviluppo e integrazione_;
-- _Verifica del sistema_.
-
-# Metodologie Agili
-
-Finora i modelli visti erano di tipo prettamente descrittivo; vediamo ora dei modelli più prescrittivi, che dicano cioè che cosa fare effettivamente durante lo sviluppo.
-
-Le metodologie agili _"nascono dal basso"_, ovvero solitamente da chi sviluppa, per colmare un disagio prevalente nell'usare i metodi tradizionali. Per tale motivo, di tali metodologie esiste un...
-
-## [Manifesto](https://agilemanifesto.org/iso/it/manifesto.html)
-
-Nelle parole di Fowler e i suoi collaboratori, per migliorare il modo in cui sviluppiamo il software dobbiamo dare più importanza ad alcuni valori rispetto agli altri:
-
-- Gli __individui__ e la __collaborazione tra individui__ è più importante di processi e strumenti.
-- Il __software che funziona__ è più importante della documentazione ben fatta.
-- La __collaborazione con il cliente__ è più importante del contratto.
-- __Rispondere al cambiamento__ è più importante che seguire un piano.
-
-> LIBRO: __Agile!__ di Bertrand Meyer
-
-Come si vede, si tratta di un drastico cambio di rotta rispetto allo sviluppo tradizionale, che si evolve anche in un business model diverso: piuttosto che farsi pagare a programma finito, adesso gli sviluppatori vogliono farsi pagare a tempo di sviluppo, dando però la garanzia al cliente di lavorare durante tale periodo esclusivamente per lui e al massimo delle proprie capacità. Al rapporto confluttuale con il cliente, in cui ciascuno cerca di fregare l'altro, si sostituisce dunque una collaborazione più estesa in cui, come vedremo, anche il cliente diventa parte del team di sviluppo.
-
-Vediamo dunque adesso alcune delle più famose metodologie agili, mettendone in evidenza gli aspetti peculiari.
-
-## Lean Software
-
-Nato dal progetto di _Lean Manufactioring_ della Toyota, ha l'obiettivo di __ridurre gli sprechi__, ovvero quei prodotti e sottoprodotti che non vengono consegnati al cliente  (es. testing, prototipi...) e dunque non generano valore: essi possono essere ignorati.
-
-Un'altra idea interessante è quella di posticipare il più possibile le scelte vincolanti per aiutare a risparmiare risorse: più possibilità mi lascio aperte, più mi sarà facile adattarmi (a patto però che l'adattamento sia veloce).
-
-## Kanban
-
-{% responsive_image path: 'assets/02_kanban.jpg' %}
-
-L'obiettivo è qui invece di __minimizzare il lavoro in corso__ (work in progress), ovvero concentrarsi in ogni momento su una sola cosa in modo da evitare i continui _context switch_ che costituiscono una perdita di tempo.
-Le attività possono per esempio essere organizzate in una tabella con 5 colonne:
-
-- __backlog__: richieste dal cliente
-- __da fare__: attività da fare in questa iterazione
-- __in esecuzione__
-- __in testing__
-- __fatto__
-
-La tabella dà a colpo d'occhio informazioni sullo stato del progetto per tutti. Ogni __card__ (storia) è assegnata a uno sviluppatore (o coppia nel _pair programming_), in modo che nella colonna in esecuzione vi sia una sola card per sviluppatore (o coppia); qualora il lavoro di un altro blocchi il mio lavoro in qualche modo è poi mia responsabilità aiutarlo per rimuovere il blocco.
-
-## _Scrum_
-
-L'obiettivo è __fissare i requisiti__ durante le iterazioni (__brevi__, da 2 a 4 settimane), in modo da permettere agli sviluppatori di lavorare in pace senza doversi adattare continuamente a nuove richieste. Solo al termine di ogni iterazione, infatti, si permette al cliente di rimettere in discussione i requisiti.
-
-## Crystal
-
-Sebbene non sia molto apprezzata o usata, questa tecnica introduce l'interessante concetto di __comunicazione osmotica__. Nel modello a cascata la comunicazione è fatta tramite documenti rigidi, ed è settorializzata; in Crystal la conoscenza viene condivisa nel team tramite _"osmosi"_, in modo che tutti sappiano un po' di tutto.
-
-Questo rende il processo più robusto, perché l'assenza di una persona esperta in un campo non è più in grado di bloccare completamente i lavori. Il pair programming è in quest'ottica: tra i due componenti la conoscenza è condivisa; Crystal estende questo concetto all'intero team.
-
-Si capisce però facilmente che questa tecnica funziona solo con team piccoli (max 8-10 persone), sebbene altre metodologie agili (_SAFE_) tentino di scalarla anche a team più massicci.  
-
-## eXtreme Programming (XP)
-
-Si tratta di una tecnica a cui dedicheremo una trattazione più approfondita nella prossima lezione. Per il momento accontentiamoci di enunciarne i due motti:
-
-- __incrementa quindi semplifica__;
-- __sviluppo guidato dal test__ (_test-first_: prima testa poi sviluppa).
\ No newline at end of file
diff --git a/_posts/2022-10-10-extreme-programming.md b/_posts/2022-10-10-extreme-programming.md
deleted file mode 100644
index 0ab5c751d130f3f2c5bdbdabe7d70252dc01e5f0..0000000000000000000000000000000000000000
--- a/_posts/2022-10-10-extreme-programming.md
+++ /dev/null
@@ -1,535 +0,0 @@
----
-layout: post
-title: "[03] eXtreme Programming"
-date:   2022-10-10 14:40:00 +0200
-toc: true
----
-
-# Test Driven Development
-
-Il _test driven development_ (TDD) è una __tecnica di progettazione__ del software che mira a far emergere "dal basso" il design più semplice in grado di risolvere un dato problema. Non si tratta ne un'attività di verifica ne di scrittura del codice, quanto piuttosto un approccio alla scrittura di questi ultimi.
-
-Il TDD si fonda su due concetti fondamentali, esplicitati nella seguente citazione:
-
-> TDD = __test-first__ + __baby steps__
-
-Il significato di questa espressione è che per scrivere del codice che esalti la semplicità della soluzione è necessario __scrivere prima il test rispetto al codice__ (_test-first_) e procedere a __piccoli passi__ (_baby steps_), realizzando cioè piccole porzioni di codice, testandole e solo allora andando avanti. Questa tecnica mira infatti a stabilire un ciclo di _feedback istantaneo_: facendo piccoli passi e testando ogni volta ciò che si appena scritto è meno probabile buttare molto tempo su una soluzione che non funziona, e anche in caso di errore è più facile individuare cosa lo genera e come risolverlo.
-
-Per applicare questo approccio _test-driven_ allo sviluppo effettivo di software, il TDD ha sviluppato il seguente "mantra": __rosso__, __verde__, __refactoring__. Quando si scrive codice bisogna infatti seguire le seguenti tre fasi:
-
-- Ogni volta che si deve aggiungere una feature __si scrive prima il test__ che la provi; non essendo ancora stata sviluppata, tale test dovrà fallire (<span style="Color: red">rosso</span>).
-
-- Si cerca poi di __soddisfare il test il più velocemente possibile__, facendolo diventare <span style="Color: green">verde</span>. Si ottiene così del codice corretto ma probabilmente molto brutto, quasi come fosse una bozza: tale codice serve però come feedback del fatto che l'algoritmo scelto funziona.
-
-- Si compie infine un'azione di __refactoring__ (_fattorizzazione_), ovvero si riorganizza e si riscrive il codice in modo da renderlo migliore assicurandosi però che il test continui ad essere soddisfatto (in questa fase dobbiamo rimanere in uno stato di <span style="Color: green">verde</span>).
-
-Questa ciclo in tre fasi va ripetuto con una cadenza frequente, ogni 2-10 minuti: ciò obbliga a concentrarsi su compiti semplici evitando così di perdersi in costruzioni software complicate che magari non funzionano neanche. Si preferisce invece prima fare qualche piccolo progresso (_increment_) e poi semplificare per migliorare il codice (_simplify_).
-
-È importante inoltre capire perché quel passaggio intermedio, la "bozza" menzionata al secondo punto dell'elenco precedente, è tanto importante: concentrarsi in primo luogo sulla creazione di una base funzionante permette subito di capire se si è scelta la strategia giusta per risolvere il problema corrente. Scrivere direttamente il codice "in bella" impiegherebbe molto più tempo e potrebbe non produrrebbe neanche un codice funzionante, siccome maggiore è la complessità del codice che si scrive più è probabile commettere errori.
-
-In virtù di quanto appena detto, l'uso del TDD come tecnica di progettazione garantisce inoltre due importanti vantaggi:
-
-- Spesso capita di scrivere codice difficilmente testabile: scrivere il test prima e il codice dopo aiuta invece a progettare prodotti la cui correttezza può essere provata.
-
-- Scrivere prima i test aiuta a definire chiaramente le interfacce del programma e come queste comunicano tra di loro, mentre se non dovessimo farlo potremmo avere delle dipendenze complicate da rimuovere.
-
-Durante il testing ci si pone dal __punto di vista del cliente__: la tecnica TDD ci permette dunque di osservare il codice da molteplici prospettive (sviluppatore e cliente), cosa che contribuisce ovviamente alla creazione di un prodotto migliore.
-
-## eXtreme Programming (XP)
-
-Ora possiamo iniziare a parlare di Extreme Programming (XP), una tecnica di sviluppo agile nata tra la fine degli anni '90 e l'inizio degli anni 2000 dalla mente di Kent Beck, che la ideò nell'ambito di un progetto Chrysler.
-
-### Le variabili in gioco
-
-Secondo Beck, durante lo sviluppo di software le principali variabili sono:
-
-- __portata__: la quantità di funzionalità da implementare, una variabile delicata dal valore _mutevole_ poiché il numero di funzionalità richieste può cambiare nel corso dello sviluppo;
-- __tempo__: il tempo che si può dedicare al progetto;
-- __qualità__: la qualità del progetto che si vuole ottenere, principalmente relativa a correttezza e affidabilità;
-- __costo__: le risorse finanziare che si possono impegnare per il progetto.
-
-Queste 4 variabili __non sono indipendenti__ tra di loro, in quanto cambiare una influenza automaticamente le altre, in positivo o in negativo. Ponendo quindi che la qualità non sia negoziabile (il software deve funzionare) bisognerà lavorare sulle altre, specialmente bilanciando costo e tempo.
-
-Nel panorama classico di sviluppo la portata era definita in modo rigido dal cliente, che richiedeva certe funzionalità non negoziabili e pagava lo sviluppatore a progetto completo.
-Con l'XP si stravolge invece la prospettiva: __il costo è orario__, il tempo disponibile non è fisso ma pari al tempo richiesto per lo sviluppo e la portata viene ricalcolata durante il progetto, essendo così l'unica variabile a variare effettivamente. Si tratta di un approccio _incrementale_ che mira ad avere sempre un prodotto consegnabile se il cliente decide di essere soddisfatto dello sviluppo: non si fa aspettare il cliente per dargli tutto il lavoro in un colpo solo, ma questo viene consegnato una parte alla volta. Oltre ad alleggerire la pressione sullo sviluppatore, questo approccio è utile per due motivi:
-
-- Il cliente è certo che lo sviluppatore si sia dedicando al progetto siccome vede il prodotto crescere a poco a poco.
-- Dà la possibilità al cliente di avere comunque qualcosa in mano se ad un certo punto vuole interrompere la collaborazione.
-- Permette al cliente di cambiare idea sulla portata e sulle funzionalità richieste in corso d'opera, bandendo la rigidità dei documenti di specifica.
-
-Tutti questi aspetti permettono di creare un rapporto molto meno conflittuale tra cliente e sviluppatore, cosa che crea le basi per una maggiore collaborazione tra le due parti.
-
-### Principi dell'extreme programming
-
-Parliamo ora un po' dei fondamenti della filosofia XP, confrontandoli con quanto veniva prescritto nell'ambiente di sviluppo classico. I principi dell'ingegneria del software classica erano infatti i seguenti:
-
-- __Separazione degli interessi__ (_aspects_ o _concerns_): separare tempi, responsabilità e moduli, ovvero tutte le varie viste o le varie dimensioni su cui si deve affrontare il problema.
-- __Astrazione e modularità__: bisogna usare le giuste astrazioni che ci permettono di dominare i problemi complessi (possono essere i diversi linguaggi di programmazione, linguaggi di descrizione o vari altri costrutti).
-- __Anticipazione del cambiamento__ (_design for change_): in fase di progettazione il programmatore deve pensare a come potrebbe cambiare il prodotto, accomodando la possibile  aggiunta di requisiti che il cliente magari non aveva neanche pensato; bisogna stare attenti però, perché spesso questo concetto complica arbitrariamente la progettazione e lo sviluppo, rischiando di far perdere molto tempo su cose che al cliente potrebbero non servire: può essere un'idea migliore partire da qualcosa di semplice ed incrementare man mano.
-- __Generalità__: per rendere più semplice la modifica e l'espansione futura è necessario scrivere interfacce molto generali ai sistemi che costruiamo.
-- __Incrementalità__: lo sviluppo avviene incrementalmente, un pezzetto alla volta.
-- __Rigore e formalità__: è importante essere rigidi e specifici sia nella comunicazione che nella descrizione dei requisiti.
-
-Sebbene non butti via tutti questi principi ma ne erediti invece alcuni per adattarli alle proprie esigenze (specialmente la _separazione degli interessi_, che viene data per scontata), l'XP pone l'accento su altri aspetti, ovvero:
-
-- __Feedback rapido__: bisogna mantenere un costante flusso di feedback; questo viene dato dai test, dai colleghi ma anche dal cliente, che dev'essere continuamente consultato sullo stato dei lavori. Tra le iniziative che favoriscono un veloce ciclo di feedback c'è lo _standup meeting_, una riunione mattutina fatta in piedi in cui ciascuno descrive in poche parole cosa ha fatto il giorno precedente e cosa intende fare oggi.
-- __Presumere la semplicità__: non bisogna complicare senza motivo né il codice, che dev'essere scritto con in mente ciò che serve a breve termine e non in un futuro remoto, né le relazioni tra colleghi, che non devono essere eccessivamente gerarchiche (tutti dovrebbero avere compiti molto simili); in generale si dovrebbe semplificare il più possibile in tutti gli ambiti del progetto.
-- __Accettare il cambiamento__: non ci si deve aspettare che il software sia immutabile; al contrario, deve essere dato per scontato il concetto di _flessibilità_ e _malleabilità_, ovvero che il cliente vorrà fare cambiamenti sia dopo che durante lo sviluppo del prodotto.
-- __Modifica incrementale__: ritornando al concetto di baby steps, ogni iterazione di sviluppo dovrebbe essere breve e le funzionalità introdotte piuttosto piccole; questa regola si applica tuttavia a tutti gli ambiti del progetto, tra cui la gestione del team: ovvero non bisognerebbe mai aggiungere più di una persona alla volta al gruppo di lavoro, in quanto aggiungerne di più potrebbe portare a passare più tempo ad istruirle che a sviluppare.
-- __Lavoro di qualità__: bisogna ovviamente ottenere un buon prodotto, ma per fare ciò la prospettiva cambia in favore dello sviluppatore, al quale si deve garantire un ambiente di lavoro salutare e un certo benessere; la fidelizzazione dei programmatori è importante perché più si trovano bene e meglio lavorano.
-
-I due punti più in contrasto sono il presumere la semplicità e l'anticipazione del cambiamento: ci sembra infatti più previdente pianificare per il futuro e anticipare eventuali cambiamenti, ma come vedremo nel prossimo paragrafo talvolta questo può essere controproducente.
-
-### Presumere la semplicità vs anticipazione del cambiamento
-
-XP mette davanti la semplicità all'anticipazione del cambiamento: non si scrive in anticipo codice che si pensa servirà in futuro. Questo non significa che non si stia progettando per il futuro, ma solo che questo non è il primo aspetto da guardare: il primo aspetto è la semplicità, ovvero fare le cose nella maniera più chiara possibile.
-
-Non pianificare per il futuro sembra rischioso: secondo uno studio condotto da Bohem nel 1976 viene ipotizzata una curva esponenziale per il corso delle modifiche all'aumento dell'avanzamento del progetto; più il progetto avanza più è costoso modificarlo, motivo per cui sembra necessario accomodare il cambiamento futuro in modo da ridurre tale costo. \
-Al contrario, XP presuppone una curva di tipo logaritmico che tenda ad un asintoto: passato un certo punto nello sviluppo il costo per le modifiche non subisce più cambiamenti sensibili, per cui non ha senso fasciarsi la testa in anticipo in quanto un codice semplice è relativamente facile da modificare.
-
-{% responsive_image path: assets/03_cost-curves.png %}
-
-Va inoltre considerato che Bohem parlava in realtà di cost-to-fix, non del costo per la modifica in sé; inoltre la sua statistica era poco affidabile poiché era stata costruita a partire da pochi dati. La curva esponenziale da lui descritta è stata poi successivamente ritrattata per accomodare il fatto che se un errore avviene in una fase affligge solo le successive, e non le precedenti.
-
-## Figure in gioco e responsabilità
-
-Al fine di organizzare il lavoro, XP individua diverse figure che partecipano allo sviluppo:
-
-- Cliente: colui che richiede funzionalità e conosce il dominio applicativo.
-- Sviluppatore: colui che sviluppa concretamente scrivendo codice.
-- Manager: colui che amministra lo sviluppo con uno sguardo generale.
-
-È interessante l'inclusione del cliente nel contesto dello sviluppo: esso non è più soltanto il committente ma ha un ruolo attivo nel lavoro, potendo cioè contribuire alla riuscita del progetto anche e soprattutto in virtù della già citata conoscenza del dominio applicativo.
-
-Ciascuna di tali figure ha responsabilità e diritti riassunti nella seguente tabella (_manager e cliente sono accorpati perché hanno grossomodo gli stessi compiti_):
-
-<table style="margin: 20px">
-    <tr style="text-align: center; background-color: #DEDEDE">
-        <th>Soggetto</th>
-        <th>Ha responsabilità di decidere...</th>
-        <th>Ha diritto di...</th>
-    </tr>
-    <tr>
-        <td style="padding-left: 15px; padding-right: 15px; text-align: center;">
-            <b>Manager/Cliente</b>
-        </td>
-        <td style="padding-left: 5px; padding-right: 15px; padding-top: 10px">
-            <ul>
-                <li>Portata del progetto, ovvero le funzionalità da realizzare</li>
-                <li>Priorità tra funzionalità e loro <i>business value</i></li>
-                <li>Date dei rilasci, anche nel caso di release incrementali</li>
-            </ul>
-        </td>
-        <td style="padding-left: 5px; padding-right: 15px; padding-top: 10px">
-            <ul>
-                <li>Sapere cosa può essere fatto, con quali tempi e quali costi</li>
-                <li>Vedere progressi nel sistema, provati dai test da lui definiti (<i>trasparenza</i>)</li>
-                <li>Cambiare idea, sostituendo funzionalità o cambiandone le priorità a intervalli di tempo fissi (<i>fine del ciclo di sviluppo incrementale</i>)</li>
-            </ul>
-        </td>
-    </tr>
-    <tr>
-        <td style="padding-left: 5px; padding-right: 15px; text-align: center;">
-            <b>Sviluppatore</b>
-        </td>
-        <td style="padding-left: 5px; padding-right: 15px; padding-top: 10px">
-            <ul>
-                <li>Stime dei tempi per le singole funzionalità (<i>no deadline imposte dall'alto</i>)</li>
-                <li>Scelte tecnologiche e loro conseguenze, ovvero <i>come</i> si realizzano le funzionalità richieste</li>
-                <li>Pianificazione dettagliata delle iterazioni</li>
-            </ul>
-        </td>
-        <td style="padding-left: 5px; padding-right: 15px; padding-top: 10px">
-            <ul>
-                <li>Ricevere dei requisiti chiari (<i>casi d'uso</i>) con priorità per le varie funzionalità</li>
-                <li>Cambiare le stime dei tempi man mano che il progetto procede e il contesto di sviluppo cambia</li>
-                <li>Identificare funzionalità pericolose o troppo difficili da realizzare</li>
-                <li>Produrre software di qualità, per il quale deve godere di un certo benessere</li>
-            </ul>
-        </td>
-    </tr>
-</table>
-
-Come si vede, per migliorare la fiducia tra sviluppatore e cliente sono necessari due requisiti: un certo grado di _trasparenza_ da parte di chi sviluppa, ottenuta dall'uso delle contiene release incrementali per mostrare come sta evolvendo il sistema, e una certa dose di _pazienza_ da parte del cliente, che deve accettare di lasciare allo sviluppatore la facoltà di decidere come si realizzano le funzionalità e di cambiare le prospettive temporali di sviluppo qualora fosse necessario.
-
-## Tecniche XP
-
-L'extreme programming fornisce una serie di metodologie pratiche per poter garantire tutto ciò che è stato descritto fino ad ora. Lo schema sottostante le descrive mettendole in relazione tra loro in modo che i vari aspetti negativi delle diverse pratiche siano compensati dagli aspetti positivi di quelle in relazione con loro; in sostanza abbiamo un mix perfetto di attività organizzate in modo da garantire i buoni principi di cui sopra.
-
-{% responsive_image path: assets/03_approccio.png %}
-
-### 1. Planning game
-
-È l'attività di pianificazione che viene fatta all'inizio di ogni iterazione e serve per "congelare" il sottoinsieme di requisiti sul quale il team lavorerà per le prossime ~2 settimane.
-
-Si parte dalle richieste del cliente espresse tramite _user stories_, una versione semplificata degli _use case_ degli UML; esse hanno come soggetto sempre un ruolo specifico nell'azienda del cliente e descrivono una funzionalità. Ogni _user story_ è dunque composta da tre parti:
-
-- il __soggetto__, ovvero il ruolo dell'utente nell'azienda (può anche essere esterno);
-- l'__azione__ che vuole eseguire il soggetto;
-- la __motivazione__ che spinge il soggetto a portare avanti l'azione descritta.
-
-Esempi di _user stories_ potrebbero essere:
-
-- > _Da bibliotecario, voglio poter visualizzare dove si trova un particolare libro in modo da poterlo reperire per i clienti._
-- > _Da utente della biblioteca, voglio poter visualizzare lo stato di un libro per poterlo prendere in prestito._
-
-Lo scopo del planning game è dunque quello di determinare quali funzionalità saranno presenti nel prossimo rilascio combinando priorità commerciali e valutazioni tecniche: questo richiede una collaborazione da parte del cliente, che come vedremo sarà presente in loco al momento della decisione.
-
-#### Procedura
-
-Quest'attività di pianificazione si divide fondamentalmente in tre fasi:
-
-1. All'inizio il cliente compila le __carte__, nient'altro che pezzetti di carta volutamente piccoli per impedire di scriverci troppo. Su ogni carta è presente:
-    - un identificativo numerico;
-    - una breve frase che descrive uno scenario d'uso;
-    - un caso di test che funge da test d'accettazione della funzionalità: si tratta in sostanza di un paio di esempi, di solito uno positivo e uno negativo, che devono essere soddisfatti per ritenere completa la feature;
-    - il valore di business che la funzionalità ha per il cliente.
-
-2. Per ogni carta il team di sviluppatori fa dunque una __stima__ del tempo necessario a realizzarla: raggiunta una stima comune questa viene scritta sulla carta e servirà per confrontare tale previsione con il tempo effettivamente impiegato, di cui si tiene conto sul suo retro. Per ciascuna carta uno sviluppatore assume infatti il ruolo di _tracker_, impegnandosi cioè a tracciare lo stato di avanzamento della relativa funzionalità durante le due settimane (_es. quante feature fatte, quanti bug segnalati, etc._).
-
-3. Il manager decide quindi sulla base di queste informazioni __quali carte verranno implementate__ durante prossima iterazione: per questa operazione prende in considerazione il valore delle feature, le dipendenze tra l'una e l'altra e una serie di altri fattori. Se, come dovrebbe essere, le varie funzionalità rappresentate nelle carte sono indipendenti, il manager può compiere questa scelta calcolando il rapporto tra il valore e il tempo stimato e usarlo per ordinare le carte: tuttavia l'operazione richiede una certa dose di ragionamento e non è mai così meccanica.
-
-{% responsive_image path: assets/03_user-story-card.png %}
-
-#### Le stime
-
-Abbiamo detto che le stime dei tempi vengono fatte dall'intero team in accordo; tuttavia il team è composto da persone diverse che quindi faranno stime diverse in funzione dell'esperienza e delle proprie capacità. È tuttavia importante raggiungere una stima accettata da tutti in quanto il team si impegna a rispettarla: se viene deciso che il tempo per una data scheda è di qualche ora e questa viene assegnata a uno sviluppatore che aveva fatto una stima di qualche giorno allora quest'ultimo si troverà in difficoltà nel portare a termine il compito; per questo motivo è importante il contributo anche degli sviluppatori junior o inesperti.
-
-Al di là del problema del raggiungimento di una stima comune, per il quale vedremo delle tecniche specifiche, ci possono essere una serie di problemi di stima legati alla funzionalità in sé. Potremmo infatti avere stime:
-
-- __molto differenti__ (ore vs giorni): in questo caso, è possibile che la carta non sia descritta o compresa correttamente; se uno sviluppatore stima poche ore e un altro qualche giorno c'è qualche problema.  in conclusione è necessario trovare un punto di incontro.
-
-- quasi uniformi, ma __molto alte__: se la stima supera il tempo di iterazione potrebbe essere che la storia sia troppo ampia. Non si può neanche iniziarla in questo ciclo e continuarla nel prossimo: se alla fine dell'iterazione non ho portato a termine il lavoro prefissato è come se non l'avessi fatto (anche se magari era stato completato all'80%), perché il cliente non lo vede nella release e tale lavoro non è dunque dimostrabile. Per ovviare a questo problema si può fare lo __splitting__ delle carte, ovvero scomporre una carta in più carte in modo da dividere il problema in sotto-problemi.
-
-- non uguali ma __simili__: non bisogna prendere la più bassa, alta o la media. Come abbiamo già detto, secondo XP bisogna arrivare ad un accordo in modo tale che chiunque nel team si riconosca nella stima effettuata.
-
-Oltre a ciò, la fase di stima dei tempi si porta dietro diverse problematiche intrinseche, tra cui:
-
-- __perdita di tempo__: per accordarsi su una stima comune si spende molto tempo (troppa comunicazione);
-
-- __effetto àncora__ (anchoring effect): si tratta di un effetto che si verifica quando bisogna assegnare un valore ad una quantità ignota. Poiché il cervello umano è più bravo a ragionare per relazioni piuttosto che per assoluti, una volta che viene fatta la prima stima numerica questa definisce l'ordine di grandezza delle stime successive, facendo cioè da punto di riferimento da cui è molto difficile distanziarsi: nel nostro caso quando il team si riunisce per fare delle stime e il primo membro dà la sua opinione, tutte le stime successive orbiteranno intorno ad essa. Tale effetto impedisce di fare una stima che prenda obiettivamente in considerazione le sensazioni di tutti i membri del team, e va dunque assolutamente evitato.
-
-Per evitare questi problemi e semplificare il processo di stima si sono sviluppati diversi processi, che data la loro natura giocosa aumentano anche l'engagement degli sviluppatori in questa fase di pianificazione.
-
-#### [Planning poker](https://en.wikipedia.org/wiki/Planning_poker)
-
-{% responsive_image path: assets/03_planning-poker.jpg %}
-
-Una per una vengono presentate brevemente le carte con le user stories facendo attenzione a non fare alcun riferimento alle tempistiche in modo da non creare subito un effetto àncora: in questa fase il team può fare domande, chiedere chiarimenti e discutere per chiarire assunzioni e rischi sulla user story, ma deve stare molto attento a non fare alcuna stima.
-
-Dopodiché ogni componente del team sceglie una carta dal proprio mazzo personale per rappresentare la propria stima e la pone coperta sul tavolo: su queste carte si trovano una serie di numeri senza unità di misura che vanno da 0 a 100 seguendo un andamento non uniforme; il loro scopo è quello di definire un'ordine di grandezza piuttosto che una stima precisa. Ci sono anche delle carte particolari, ovvero:
-
-- il punto di domanda indica che non si è in grado di dare una stima
-- la tazza di caffè indica che la riunione è andata troppo per le lunghe ed [è necessaria una pausa](https://www.youtube.com/watch?v=-gAlDOcXgyM).
-
-Fatta questa prima stima _blind_ le carte vengono girate contemporaneamente: idealmente vi dovrebbe essere l'unanimità sulla stima. Se così non è chi ha espresso le stime più basse e più alte ha ~1 minuto per motivare la propria scelta in modo da cercare di convincere gli altri; si noti che agli altri componenti del team non è concesso parlare per evitare di perdere troppo tempo! \
-Finito questo momento di consultazione tutti i membri del team fanno una nuova stima e si continua così finché non si raggiunge l'unanimità; solitamente le votazioni convergono dopo un paio di round.
-
-Ma qual'è l'unità di misura su cui si fanno le stime? Dipende: essa può essere scelta prima o dopo aver trovato un accordo; possono essere ore, giorni o pomodori (un pomodoro è formato da 25 minuti senza alcuna distrazioni,e dopo c'è una pausa). Ovviamente non si può pretendere di lavorare delle ore senza alcuna distrazione, per cui in queste stime si considera anche un certo __slack time__, ovvero un tempo cuscinetto per che comprende il "tempo perso" a causa di distrazioni.
-
-#### [Team Estimation Game](https://agilelearninglabs.com/2012/05/how-to-play-the-team-estimation-game/)
-
-Si tratta di un metodo un po più complesso articolato in 3 fasi e basato sul confronto tra i diversi task piuttosto che sulla stima numerica: esso si basa infatti sull'idea che sia semplice stabilire se un task sia più facile o più difficile di un altro, mentre è molto più complicato capire di quanto sia più facile/difficile. L'idea è dunque quella di splittare in fasi questa cosa di dover dare un valore al task considerandone sempre di più difficili per arrivare a fare una buona stima.
-
-##### __<big>PRIMA FASE</big>__
-
-{% responsive_image path: assets/03_team-estimation-1.jpg %}
-
-Si fa una pila con le storie e si mette la prima carta al centro del tavolo. I developer si mettono in fila e uno alla volta eseguono queste azioni:
-
-- il __primo della fila estrae una carta della pila__, la legge ad alta voce e la __posiziona__ a sinistra (più semplice), a destra (più complicata) o sotto (equivalente) la carta già presente sul tavolo.
-- il __prossimo developer__ può:
-  - __estrarre una nuova carta dalla pila__ e __posizionarla__ secondo le stesse regole, eventualmente inserendola in mezzo a due colonne già presenti;
-  - __spostare una carta precedentemente posizionata__ commentando la motivazione della sua scelta; può ovviamente succedere che tale carta venga rispostata nella sua posizione originale, ma dopo un po' si troverà un accordo sulla difficoltà del relativo task.
-
-Terminata la pila avremo le carte disposte sul tavolo in colonne di difficoltà comparabile, ordinate dalla meno difficile (sinistra) alla più difficile (destra).
-Oltre ad aver ridotto la comunicazione (molte carte non saranno contestate), usando questa tecnica abbiamo evitato anche l'effetto àncora rendendolo relativo: l'assenza di valori precisi evita il rischio di influenzare eccessivamente gli altri. Inoltre a differenza del planning poker si può tornare sulle proprie decisioni, cosa che favorisce un continuo adattamento e ripensamento delle stime.
-
-##### __<big>SECONDA FASE</big>__
-
-Si cerca dunque di quantificare le _distanze_ tra le carte.
-
-{% responsive_image path: assets/03_team-estimation-2.jpg %}
-
-Ci si mette di nuovo in coda davanti al tavolo con il mazzo di carte del planning poker (uno solo, non uno per persona) e __si cerca di etichettare le colonne in base alle difficoltà__.
-
-Si posiziona la prima carta (solitamente si parte da 2 perchè magari nella prossima iterazione può esserci qualcosa di ancora più facile) sopra la prima colonna.
-
-Quindi:
-- il __primo sviluppatore__ prende il valore successivo e lo posiziona sulla prima colonna che pensa abbia quel valore (rispetto al 2), oppure lo posiziona tra due colonne se pensa che sia un valore di difficoltà intermedio tra le due.
-- lo __sviluppatore successivo__ può invece:
-    - __estrarre una carta__ dal mazzo e __posizionarla__ secondo le regole di prima (la prima colonna che pensa abbia un particolare valore di difficoltà);
-    - __spostare una carta__ con un valore precedentemente posizionato, commentando la motivazione dello spostamento;
-    - __passare__ il turno, nel caso in cui non ci siano più carte nella pila e non si vogliono spostare altre carte.
-
-È possibile avere delle carte in cui sopra non c'è nessun numero, queste saranno assimilate alla colonna alla loro sinistra. 
-
-Al termine di questa fase, la situazione sarà simile alla seguente:
-
-{% responsive_image path: assets/03_fine-seconda-fase-estimation-game.jpg %}
-
-##### __<big>TERZA FASE</big>__
-
-Si stima il tempo in ore/uomo di una delle carte più semplici e successivamente si calcolano tutte le colonne in proporzione alla prima.
-Ma questa fase è davvero cosi utile? Nella pratica si è visto che __è inutile valutare il lavoro fatto in ore/uomo__, anche perchè con il passare del tempo la taratura può variare.
-
-Nella prossima sezione parliamo di come la nozione di __velocity__ risolve questo problema. 
-
-#### Velocity
-È importante riuscire a stimare la _velocità_ con la quale stiamo avanzando. 
-In fisica la velocità è data dal rapporto tra la distanza percorsa e il tempo per percorrerla. 
-Questa proprietà può essere usata anche nella gestione dello sviluppo agile: il numeratore è il punteggio delle storie mentre il denominatore è la lunghezza dell'iterazione (assimilabile in un'unità di tempo).
-
-La ___velocity___ nel mondo agile è quindi il __numero di story point__ guadagnati nell'arco dell'iterazione corrente.
-
-Essa riesce quindi a dare un'idea di quanto si è riusciti a fare in termini di complessità astratta.
-Se per esempio il team è riuscito a fare 50 punti nella iterazione appena finita, è ragionevole prefissarsi di fare almento 50 punti nell'iterazione successiva.
-
-La velocity __non può essere usata__ per dare __premi__, per __confrontare__ team diversi o __punire__ in caso di diminuzione, però si adatta al modo diverso degli sviluppatori di gestire le stime e dal fatto che si tende a sottostimare o sovrastimare carte diverse.
-
-All'atto di aggiungere una persona questa metrica deve inizialmente rimanere invariata, per prevedere la sua formazione; se la rimuovo ci sarà una perdita di produttività.
-
-La velocity __non deve considerare le storie lasciate incompiute__, quindi anche se l'ho completata al 90% devo considerarla come se non l'avessi fatta.
-Inoltre, __non deve essere__ imposta: la velocity di un team è fissa e non può essere aumentata.
-
-Esiste un movimento chiamato ___no estimates___, che evita al team tutta la parte delle stime. 
-Dall'esperienza del prof. Bellettini, però, questa metodologia funziona in team molto maturi che sono in grado di guidare il ciente a formulare storie simili in termini di difficoltà, avendo tutti una misura standard per le storie.
-
-### 2. Brevi cicli di rilascio
-Per ridurre i rischi, la vita e lo sviluppo dell'applicazione sono scanditi dai rilasci di diversioni del prodotto funzionanti, di solito uno ogni due settimane (come abbiamo visto in scrum con il _freez_, ma con un tempo di rilascio minore). 
-È necessario avere abbastanza tempo per sviluppare qualcosa di concreto, e il cliente per poter pensare alle richieste che ha fatto e stabilire se ha bisogno di modifiche.
-
-Betrand Meyer, nel suo libro _"Agile! The Good, the Hype and the Ugly"_, definisce questa idea _"brillante"_, _"forse l'idea agile con l'influenza e impatto maggiore nell'industria"_. 
-
-### 3. Uso di una metafora
-Definire un __nuovo vocabolario__ con cui parlare con l'utente (tecnica _non informatica_) ma anche ai nuovi sviluppatori.
-Serve per permettere una nominazione di classi e metodi omogenei e fornire una vista d'insieme.
-Siccome non c'è una vera documentazione in XP, possiamo usare queste metafore come una vista d'insieme, quindi sostituire in parte l'architettura del sistema, e far capire gli elementi fondamentali, il loro scopo e le relazioni fra loro. 
-
-### 4. Semplicità di progetto
-Ovvero l'___arte di massimizzare il lavoro non fatto___, o da non fare.
-Non è necessario riscrivere cose già esistenti e consolidate. 
-
-Uno slogan tipico è __KISS__: __Keep It Simple, Stupid__.
-
-Questo punto si contrappone al _design for change_ che viene invece visto come un appesantimento inutile, perchè una feature che aggiungiamo può essere scartata dal cliente.
-
-### 5. Testing
-È consolidato su due fronti:
-- i clienti scrivono i __test di accettazione__ (o funzionali) sulle schede per aumentare la loro fiducia nel programmi;
-- i programmatori scrivono i __test di unità__ perché la fiducia nel codice diventi parte del programma stesso.
-
-Nell'XP ogni aspetto viene massimizzato, ma in particolare il testing viene esasperato di più in quanto, oltre ad essere molto importante, molti altri aspetti si basano su di esso (vedi la figura all'inizio della sezione). 
-Ha il ruolo di __rete di protezione__ in tutte le fasi: ogni cambiamento è verificabile tramite i test.
-
-Il testing aiuta molto anche quando non si parte da zero con il programma, ma quando si deve modificare un programma proprietario precedentemente sviluppato anche in modalità non agile.
-Prima di apportare modifiche al codice scrivo i test e solo successivamente procedo, in modo da non causare problemi.
-
-Un altro concetto importante è che i test dovrebbero __coprire tutte le righe di codice__.
-
-### 6. Refactoring
-Anche da novizi, non bisogna avere paura di apportare modifiche che semplificano il progetto: bisogna avere coraggio.
-
-Il refactoring è l'operazione che __modifica solo le proprietà interne__ del software, __non le funzionalità__.
-L'obiettivo è eliminare l'entropia generata dalle continue modifiche e aggiunte.
-
-Il refactoring deve essere __graduale e continuo__ in modo da poter aggiungere funzionalità in maniera semplice. 
-Chiaramente, in caso di ristrutturazioni architetturali di grosse dimensioni di sistemi legacy non è sempre possibile procedere in questa maniera.
-
-Parti di codice non stimolate da test non sono utili ai fini della soluzione: o si aggiungono test per gestire i casi specifici, altrimenti si possono rimuovere _in toto_.
-
-Il refactoring è una delle tecniche più __importanti__ e __fondamentali__ dell'XP.
-
-### 7. Programmazione a coppie
-
-La programmazione a coppie (__pair programming__) è una tecnica controintuitiva: dal punto di vista del manager si pagano due persone per fare il lavoro di una, ma non è così.
-
-Ci sono diversi __vantaggi__:
-- in coppia, __ci si controlla a vicenda__ su ogni aspetto (codice, rispetto delle regole XP, idee); 
-- mentre il _pilota_ attua le idee, il _navigatore_ pensa cosa fare subito dopo: forma di __refactoring__;
-- favorisce l'__inserimento di nuovo personale__: piuttosto che lasciare i novizi da soli a studiare libroni, vengono affiancati e incitati a osservare e interagire con persone esperte che stanno lavorando;
-- fa ottenere una __proprietà collettiva__ (conoscenza osmotica), come descritta da Crystal. 
-Un altro punto importante sono i commenti _naive_ (ovvero fatti da programmatori junior) per permettere di chiarire concetti basilari date spesso per scontato. 
-
-Raddoppiare il numero di persone raddoppia la produttività?
-__No__, è stimato invece che la produttività aumenti circa del 50% - quindi non abbastanza per giustificare il costo.
-
-Diversi studi si chiedono se la produttività calcolata puntualmente sia una metrica sensata. 
-Secondo molti no, perché al termine di un'iterazione ciò che sembra poco produttivo in realtà lo è di più: il tempo non successivamente speso in verifica, convalida e refactoring è largamente assorbito dall'__ispezione continua del codice__ svoltasi durante le sessioni di pair programming.
-
-#### Critiche
-Betrand Meyer, nel suo libro _"Agile! The Good, the Hype and the Ugly"_, scrive:
-
-> __Applied judiciously, pair programming can unquestionably be useful__. Many developers enjoy the opportunity to program jointly with a peer, particularly to deal with a thorny part of an assignment. 
-> The basic techniques, in particular the idea of speaking your thoughts aloud for immediate feedback, are well understood and widely applied. (As a manager I regularly hear, from a developer, “On this problem I would like to engage in a round of pair programming with X ”, and invariably find it a good idea.)
-> 
-> What is puzzling is the insistence of XP advocates that this technique is the only way to develop software and has to be applied at all times. __Such insistence makes no sense__, for two reasons.
-> 
-> The first is the __inconclusiveness of empirical evidence__, noted above. Granted, lack of data is often used as a pretext to block the introduction of new techniques. 
-> When an idea is obviously productive, we should not wait for massive, incontrovertible proof. 
-> But here there is actually a fair amount of empirical evidence, and it does not show a significant
-advantage for pair programming. 
-> Pair programming may be good in some circumstances, but if it were always the solution the studies would show it. 
-> In the absence of scientific evidence, a universal move is based on ideology, not reason.
-> 
-> The second reason, which may also explain why studies’ results vary, is that __people are different__. 
-> Many excellent programmers love interacting with someone else when
-they write programs; and many excellent programmers do not. 
-> Those of the second kind want to think in depth, undisturbed. 
-> The general agile view is that communication should be encouraged and that the days of the solitary, silent genius are gone. 
-> Fine; but if your team has an outstanding programmer who during the critical steps needs peace, quiet and solitude, do you kick him out of the team, or force him to work in a way that for him may be torture?
-
-> It is one thing to require that people explain their work to others; it is another, quite dangerous, to __force a single work pattern__, especially in a highly creative and challenging intellectual endeavor. 
-> When Linus Torvalds was writing Linux, he was pretty much by
-himself; that did not prevent him from showing his code, and, later on, engaging thousands of people to collaborate on it.
-
-### 8. Proprietà collettiva
-Il codice non appartiene a una singola persona ma al _team_: non devono quindi esistere policy di _"code owners"_ a la Microsoft.
-Tutti i componenti del team sono quindi autorizzati a modificare e sistemare ogni parte del codice, anche se scritta da un altro. 
-
-Durante il giorno, più volte al giorno, è comune __cambiare coppia__ e saranno quindi possibili situazioni in cui nessuno dei due ha una profonda conoscenza della parte di codice che si sta trattando o che il task non si addica alle competenze della coppia.
-
-In tutti i casi, in XP ci si riferisce al team e non al singolo.
-
-### 9. Integrazione continua
-Nell'ottica di ricevere feedback rapidi dal cliente è necessario __integrare spesso__, anche __più volte al giorno__. 
-Questo non significa far passare i test d'unità per integrare tutto in un'unica operazione, ma essere graduali: è frequente scoprire che parti testate e funzionanti singolarmente una volta integrate nel prodotto finale non funzionano. 
-
-L'integrazione continua e graduale è una tecnica largamente utilizzata in tutti i campi, non solo nello sviluppo software.
-
-Al termine dello sviluppo di una _feature_, è compito della coppia integrarla nella __macchina di riferimento__.
-L'accesso a tale macchina deve essere regolato in maniera __esclusiva__: in situazioni di lavoro da remoto si può utilizzare un token.
-La macchina di riferimento si trova, per quanto riguarda le funzionalità, in una situazione __monotona crescente__.
-Ad ogni integrazione è necessario produrre sempre qualcosa di consegnabile al cliente. 
-
-Una _user story_ si definisce __completata__ solo dopo aver terminato l'integrazione, superato dei test di integrazione e aver mostrato al cliente il risultato della macchina complessiva dopo l'integrazione.
-
-Un'altro punto a favore della continua integrazione è che evita la situazione in cui una coppia modifichi la macchina __dopo molto tempo__ dalla propria ultima integrazione, aumentando di molto la probabilità di errori per le altre coppie. 
-
-Se una coppia __non riesce ad integrare__ blocca anche tutte le altre che non possono andare avanti con le use story, quindi sarà necessario che quella coppia rinunci, ritorni sulla sua macchina e cerchi di risolvere lì - tutte le coppie hanno una propria macchina su cui testano prima di farlo su quella comune.
-
-### 10. Settimana di 40 ore
-Il mestiere di sviluppatore ha sempre avuto dei __ritmi dettati dalle consegne__: lavorare troppo a lungo porta a un abbassamento della produttività, oltre che a stress e frustrazione. 
-
-Nell'XP si cerca di evitare queste situazioni in modo da avere una resa migliore, avere maggior soddisfazione nel lavorare nel team e nell'azienda, avere meno problemi fuori dal lavoro (tante volte questo eccessivo lavoro può causare problemi familiari) e inoltre abbassare la probabilità per l'azienda di perdere dipendenti. 
-
-Purtroppo però il mestiere dello sviluppatore non è meccanico e molto spesso si vuole portare a termine quello che si sta facendo perchè magari si è quasi alla soluzione, inoltre si continua a pensare a come risolvere dei problemi anche fuori dall'orario lavorativo.
-
-### 11. Cliente sul posto
-
-Dal punto di vista del cliente, il suo inserimento nel luogo fisico di sviluppo è un vantaggio in quanto può essere sicuro che gli sviluppatori stiano lavorando per lui e __può verificare come procede il progetto__.
-
-Dal punto di vista degli sviluppatori, invece, è fondamentale avere il cliente sul posto per potergli __chiedere chiarimenti__ in caso di specifiche non chiare. 
-La possibilità di poter far domande è come avere una _documentazione vivente_; il cliente potrà continuare a lavorare per la sua azienda, ma dovrà dare priorità alle richieste degli sviluppatori. 
-
-Avere il cliente sul posto ha comunque dei __limiti__: quest'ultimo, infatti, deve essere scelto accuratamente per avere una persona rappresentativa di tutti gli stakeholder; il compito è forse impossibile.
-Se il cliente del posto non è disponibile, il team deve trovare dei modi per poter comunque avere un _punto di riferimento_: la tecnica Scrum introduce il concetto di __product owner__, un componente interno al team che si comporta come se fosse il cliente.
-
-Il cliente durante le iterazioni può __creare altre storie__ che a partire dall'iterazione successiva potrà inserire nel planning game; è inoltre disponibile per __test funzionali__.
-
-### 12. Standard di codifica
-È necessario prevedere delle regole (__convenzioni comuni__) che specificano come scrivere il codice, per aumentare la leggibilità e quindi la comunicazione attraverso il codice.
-
-Spesso, si utilizzano degli __strumenti__ per garantire il rispetto delle convenzioni o autocorreggere il codice auutomaticamente.
-
-Avere uno standard di codifica aiuta inoltre:
-- il refactoring;
-- la programmazione a coppie;
-- la proprietà collettiva.
-
-### 13. _They're just rules_
-L'ultima regola _"non è canonica"_, in quanto è stata aggiunta successivamente da alcuni agilisti.
-
-Al termine di un'iterazione si fa un __resoconto__ e quindi decidere come comportarsi per l'iterazione successiva.
-Nel suddetto resoconto si può anche decidere di __sospendere regole__ se si pensa che non siano adatte per la situazione o per il team e successivamente possono essere reintrodotte. 
-La decisione di non seguire una regola deve essere sempre fatta a livello di __team__, non dal singolo o dalla coppia.
-
-In conclusione, l'XP non è una tecnica così rigida e rigorosa: ad ogni iterazione, si possono effettuare test per trovare il giusto equilibrio.
-
-Questo punto non però è condiviso da tutti e una motivazione la si può trovare nel fatto che tutti i punti sono interconnessi tra loro, e quindio non è possibile studiarli singolarmente senza considerate anche gli altri perchè non avrebbero senso in quanto hanno una forte dipendenza l'una dall'altra; non a caso nei punti sopra si può notare come si influenzino a vicenda.
-
-## XP e modello a cascata
-È possibile tentare di  raggruppare le diverse tecniche dell'eXtreme Programming nelle macrofasi descritte dal modello a cascata.
-
-- __Requirements__:
-    - i _clienti fanno parte del team di sviluppo_: requirements viventi;
-    - _consegne incrementali_ e pianificazioni continue: evoluzione del progetto.
-- __Design__:
-    - _metafora_ come visione unificante del progetto;
-    - _refactoring_: è design puro, molto utile per rendere possibile l'evolvibilità del software;
-    - presumere la _semplicità_.
-- __Code__:
-    - _programmazione a coppie_;
-    - _proprietà collettiva_;
-    - _integrazione continua_;
-    - _standard di codifica_.
-- __Test__
-    - _test di unità_ continuo (da scriversi prima del codice);
-    - _test funzionale_ scritto dagli utenti nelle user stories.
-
-In XP è inoltre presente la nozione di _prototipo_ sotto il nome di ___spike___, ovvero programmi molto semplici creati per esplorare soluzioni potenziali.
-Sono utili per capire se ho compreso le specifiche, la tecnologia da utilizzare e l'approccio da avere con i componenti esterni con cui bisogna dialogare. 
-Questi prototipi vengono creati, mostrati al cliente e quindi scartati.
-
-## Documentazione
-La __documentazione__ cartacea __non è necessaria__: 
-il cliente, il compagno di peer programming e il codice _sono la documentazione_.
-
-La documentazione è sostituita dal codice in quanto:
-- i __test di unità__ che sono delle _specifiche eseguibili_, infatti li scrivo prima di fare il codice (prima dico cosa voglio tramite il test);
-- il __continuo refactoring__ consente di avere un codice estremamente leggibile e quindi elimina il bisogno dei commenti.
-Scrivere codice semplice tramite refactoring in modo che sia facilmente comprensibile è in realtà molto complesso.
-
-### CRC cards
-Le __Class Responsibility and Collaboration cards__ permettono di rappresentare classi e le relazioni tra di esse.
-Nate in ambiente didattico per spiegare l'OOP, sono ora utilizzati da alcuni team agile per discutere di design e il modo di utilizzo è simile a quello del planning game.
-
-> Le carte CRC sono realizzate su piccole schede di carta o cartoncino. Ciascuna carta descrive una classe (o un oggetto) in modo sommario, indicando:
-> 
-> - Il nome della classe
-> - Le sue superclassi e sottoclassi (dove applicabile)
-> - Le sue responsabilità
-> - Il nome di altre classi con cui questa classe collabora per svolgere i compiti di cui è responsabile
-> - L'autore
-> 
-> L'uso di schede di piccole dimensioni ha lo scopo di limitare la complessità della descrizione, evitando che vengano riportate troppe informazioni di dettaglio. Serve anche a impedire che a una classe vengano assegnate troppe responsabilità. Il supporto cartaceo consente una serie di attività gestuali utili in fase di brainstorming, come piazzare le carte su un tavolo e spostarle, riorganizzarle, o anche eliminarle e sostituirle facilmente con altre nel corso della discussione. Il posizionamento delle carte su un tavolo può essere usato intuitivamente per rappresentare informazioni aggiuntive; per esempio, due carte possono essere parzialmente sovrapposte per indicare una relazione di stretta collaborazione, o una carta può essere posta sopra un'altra per indicare una relazione di controllo/supervisione.
-> 
-> _Da [Wikipedia](https://it.wikipedia.org/wiki/Carte_di_Class_Responsibility_Collaboration), l'enciclopedia libera (licenza CC BY-SA 3.0)_.
-
-## Quando non utilizzare XP
-Back non esclude mai la possibilità di utilizzare l'XP: secondo lui diceva può provare ad utilizzare questo approccio sempre (anche se in realtà non è sempre possibile "provare"), a patto che vengano rispettati i 12 punti elencati sopra.
-
-Da questo possiamo concludere che Agile non si può usare quando:
-- l'__ambiente__ non permette l'applicazione dei 12 punti, come per esempio succede con i team dislocati in luoghi diversi;
-- ci sono __barriere managieriali__, come team troppo numerosi;
-- ci sono __barriere tecnologiche__, come quando per esempio non è possibile utilizzare una macchina specifica condivisa da tutte le coppie per i test, ostacolando l'integrazione continua.
-- ci sono __troppi stakeholders__ diversi e in contrasto tra loro;
-- situazioni in cui __la consegna incrementale non ha senso__, come per una _centrale nucleare_ (vero [Dyatlov](https://en.wikipedia.org/wiki/Anatoly_Dyatlov)?).
-
-## Critiche
-Di seguito sono elencate alcune critiche all'eXtreme Programming fatte da Meyer (già pluricitato in questo documento).
-
-- __Sottovalutazione dell'up-front__, ovvero la progettazione iniziale prima di partire. Per Meyer, a parte in casi eccezionali (sviluppatori o manager particolarmente bravi) la progettazione non può essere fatta in modo totalmente incrementale. Nell'esperienza dei tesisti e colleghi di dottorando del prof. Bellettini questo problema non è così presente, ma potrebbe trattarsi di _survivorship bias_.
-- __Sopravalutazione delle user stories__: secondo Meyer sono troppo specifiche per sostituire i requisiti.
-- __Mancata evidenziazione delle dipendenze tra user stories__. 
-Le _user stories_ dovrebbero essere indipendenti tra loro, ma questo non è quasi mai possibile; nel design classico si utilizzano i diagrammi di Gantt per chiarire tutte le dipendenze tra i diversi punti del sistema da realizzare.
-- Il __TTD può portare ad una visione troppo ristretta__.
-- __Cross functional team__: se i team sono troppo disomogenei, ovvero ci sono tante singole figure specializzate in un campo e queste devono collaborare in coppia, ci possono essere dei problemi.
-
-I punti di cui sopra cercano di evidenziare la mancanza di approfondimento e chiarezza dell'XP su alcuni aspetti dell'approccio ad un lavoro fornito da un cliente.
-
-È consigliata la lettura del libro di Meyer.
-
-## Mesi uomo
-È diffuso tra i manager pensare che la stima in tempo in mesi uomo sia graficata come un ramo di un iperbole, ovvero che il tempo diminuisca simil-esponenzialmente all'aumentare dei mesi uomo; tale stima non considera i tempi di _overhead_, ovvero il tempo impiegato per la comunicazione e tutto ciò che non è l'implementazione.
-I mesi uomo __non quindi sono una metrica valida__, ma sono utili solo a posteriori per valutare se un approccio ad un problema si è dimostrato valido.
-
-Nella realtà, __all'aumentare delle persone aumenta il bisogno di comunicare__.
-
-Quando il lavoro è strettamente sequenziale e non parallelizzabile (come la _gravidanza_) anche all'aumentare delle persone il tempo non cambia.
-
-Nel mondo dello sviluppatore software spesso c'è un __numero ideale di persone__ per un progetto; dopodiché, persone in più causano solo confusione e rallentano i tempi a causa della comunicazione.
-Il numero può anche essere grande, dipende dall'entità del progetto (esempio: space shuttle).
-
-In generale, le metodologie agili iniziano a __non funzionare più__ se il team è __più grande di 8-10 persone__.
-Quando il progetto non funziona più con un tale numero di persone, è necessario esplorare altre pratiche.
diff --git a/_posts/2022-10-12-open-source.md b/_posts/2022-10-12-open-source.md
deleted file mode 100644
index 8ea0a674d6c455b205957d40c9526df75704b7d0..0000000000000000000000000000000000000000
--- a/_posts/2022-10-12-open-source.md
+++ /dev/null
@@ -1,160 +0,0 @@
----
-layout: post
-title: "[04] Open source"
-date: 2022-10-12 14:40:00 +0200
-toc: true
----
-
-# Open source
-
-Apriamo ora le porte a un nuovo modo di sviluppare software che si è molto diffuso negli ultimi decenni ed è ora alla base di progetti applicativi molto importanti e famosi: il processo __open-source__. In due parole, un software open-source è un software il cui codice è liberamente consultabile online e il cui sviluppo si basa non su un singolo team ma su una community di sviluppatori indipendenti.
-
-Queste prospettive stravolgono l'idea di sviluppo classico, per cui affrontiamo le numerose differenze a partire da una famosa metafora forgiata da Eric Raymond.
-
-## _The Cathedral and the Bazaar_
-
-Raymond propone nel suo [articolo](http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/) due immagini per descrivere i due mondi contrapposti closed-source e open-source:
-
-- la __cattedrale__ (closed source): si tratta di un ambiente molto gerarchizzato, fortemente legato al progetto iniziale di un unico "architetto" responsabile dei lavori;
-- il __bazaar__ (open source): è l'ambiente più anarchico possibile, in cui ognuno lavora per sé e costruisce espandendo ciò che gli altri hanno già fatto.
-
-Entrambe le costruzioni risultano splendide e attraenti, ma sono chiaramente legate a modi di pensare la costruzione e la progettazione totalmente opposti.
-
-### Vita e vantaggi di un progetto open-source
-
-Nell'articolo Raymond prosegue a descrivere come nasce un progetto open-source, esordendo con la seguente citazione:
-
-> Ogni buon lavoro software inizia dalla frenesia personale di un singolo sviluppatore.
-
-Si delinea dunque la seguente timeline di un progetto open-source:
-
-1. Uno sviluppatore ha un problema e intende risolverlo sviluppando un'applicazione.
-2. Lo sviluppatore chiede ad amici o colleghi cosa sanno sull'argomento: alcuni hanno lo stesso problema o problemi simili, ma nessuno ha una soluzione.
-3. Le persone interessate cominciano a scambiarsi pareri e conoscenze sull'argomento.
-4. Coloro che intendono spendere delle risorse (di fatto il proprio tempo libero) per risolvere il problema danno il via ad un progetto informale.
-5. I membri del progetto lavorano sul problema finché non raggiungono dei risultati presentabili.
-
-    Fino a qui però il progetto non è ancora definibile open-source in quanto vi lavora solo un gruppo ristretto di amici e conoscenti: il vero progetto open nasce quando __viene messo online a disposizione di tutti il codice sorgente__. Continuando, dunque:
-
-6. Si rende noto il lavoro online e arrivano i primi suggerimenti esterni al gruppo: questi saranno tanto più frequenti quanto più il progetto pubblicato presenterà errori in quanto la comunità, principalmente composta da altri sviluppatori, sarà motivata a risolverli.
-7. Si crea interazione tra i vecchi e i nuovi membri del gruppo di sviluppo.
-8. Nuove informazioni e competenze vengono acquisite e si ritorna al punto 5.
-
-Raymond continua esponendo alcune delle caratteristiche e dei vantaggi dei progetti open-source, primo fra tutti il fatto che:
-
-> Se dai a tutti il codice sorgente, ognuno di essi diventa un tuo ingegnere.
-
-dove con questo si intende che la possibilità di vedere e commentare il codice sorgente permette a utenti esperti di suggerire modifiche e prendere parte attiva allo sviluppo. Talvolta si tende però a pensare che un progetto di questo tipo sia destinato unicamente ad altri informatici o sviluppatori, ma ciò non è affatto vero: tante attività utili a portare avanti un progetto open-source non richiedono necessariamente competenze informatiche, come per esempio la segnalazione di bug o la moderazione di contenuti nella comunità.
-
-A tal proposito, è importante il seguente concetto:
-
-> Se ci sono abbastanza occhi [che cercano errori], gli errori diventano di poco conto.
-
-Non c'è molto da spiegare: più sono le persone che controllano e leggono il codice più sarà probabile trovare gli errori in esso contenuti; inoltre, gli errori rilevati possono essere risolti più facilmente grazie al supporto della community di sviluppatori, che potrebbe già conoscere una soluzione. \
-L'accento posto sulla community viene ulteriormente rimarcato dal valore che viene attribuito ai beta-tester, che in un progetto open-source è chiunque utilizzi l'applicazione in qualunque suo stadio vista la sua estrema malleabilità:
-
-> Se tratti i tuoi beta-tester come se fossero la tua risorsa più importante, essi risponderanno diventando la tua risorsa più importante.
-
-Per mantenere attiva la community di sviluppatori è però necessario un costante monitoraggio e cura; per permettere al progetto open-source di sopravvivere anche quando l'interesse dei creatori originali si è spento è fondamentale passarne il controllo a qualcuno di fidato e competente, come ci ricorda Raymond nell'ultima citazione che riportiamo:
-
-> Quando hai perso interesse in un programma, l'ultimo tuo dovere è passarlo a un successore competente.
-
-Spesso questo passaggio di testimone non viene fatto e il progetto muore: occorre invece trovare qualcuno di interessato allo sviluppo, anche perché un programma in uso dovrà necessariamente cambiare ed evolvere in futuro.
-
-## Confronto tra modelli
-
-Per capire meglio i concetti fondanti del mondo open-source mostriamo in un modo sintetico un confronto tra lo stesso e i metodi di sviluppo tradizionale e agile nel riguardo di svariati concetti:
-
-| Cosa | Tradizionale | Agile | Open source |
-|------|--------------|-------|-------------|
-| __Documentazione__ | La documentazione è enfatizzata come strumento di controllo qualità e gestione. | La documentazione è de-enfatizzata. | Tutti i manufatti di sviluppo sono disponibili a chiunque, compresi il codice e la documentazione. |
-| __Requisiti__ | Gli analisti traducono le necessità dell'utente in specifiche software. | Gli utenti fanno parte del team. | Gli sviluppatori spesso sono anche gli utenti. |
-| __Assegnamento dello staff__ | Gli sviluppatori sono assegnati ad un unico progetto. | Gli sviluppatori sono assegnati ad un unico progetto. | Gli sviluppatori tipicamente lavorano su più progetti con diversi livelli di partecipazione (_impossibile pianificare lo sviluppo_). |
-| __Revisione del codice paritaria__ | La revisione del codice tra pari è ampiamente accettata ma raramente effettuata. | La _pair programming_ introduce una forma di revisione del codice tra pari. | La revisione del codice è una necessità ed è praticata quasi universalmente. |
-| __Tempi di rilascio__ | Tante feature in poche release massicce. | Tante piccole release incrementali. | Gerarchia dei tipi di release: _nightly_ (compilazione giornaliera dal branch master), _development_ e _stable_. |
-| __Organizzazione__ | I team sono gestiti dall'alto. | I team sono auto-organizzati. | I contributori individuali decidono per sé come organizzare la propria partecipazione. |
-| __Testing__ | Il testing è gestito dallo staff di _Quality Assurance_ (QA), che segue le attività di sviluppo. | Il testing è parte integrante dello  sviluppo (TDD). | Il testing e la QA possono essere svolti da tutti gli sviluppatori. |
-| __Distribuzione del lavoro__ | Parti differenti della codebase sono assegnate a persone differenti. | Chiunque può modificare qualsiasi parte della codebase. | Chiunque può modificare qualsiasi parte della codebase, ma solo i _committer_ possono rendere ufficiali le modifiche. |
-
-<p><!-- ugly spacer --></p>
-
-## _Care and Feeding of FOSS_
-
-Ma come si inserisce il modello di sviluppo open-source in un panorama tecnologico sempre più accentrato nelle mani di poche e potenti aziende? Il timore è infatti quello che rendendo il codice disponibile a tutti si potrebbe essere subito vittima di plagio da parte di giganti del settore in grado di realizzare in poco tempo ciò che prenderebbe a un team open-source svariati mesi, accaparrandosi così una larga fetta di mercato. Craig James smentisce tuttavia tale visione nel suo [articolo](https://web.archive.org/web/20081015010505/http://www.moonviewscientific.com/essays/software_lifecycle.htm), in cui descrive il ciclo di vita di un software FOSS (Free and Open Source Software) come la sequenza delle seguenti fasi:
-
-1. __Invenzione__: qualcuno ha un'idea e la implementa facendola funzionare.
-Dal momento in cui l'idea viene resa pubblica si assiste una grossa esplosione di progetti in tal senso finanziati da varie aziende che cercano di diventare leader nel nuovo mercato.
-2. __Espansione e innovazione__: il mondo si accorge dell'invenzione e la tecnologia inizia a espandersi in quanto le aziende si 'rincorrono' a vicenda cercando di aggiungere più funzionalità possibili. Questa fase non rappresenta un buon momento per far nascere un progetto open source: un piccolo gruppo non riuscirebbe a prevalere sulle grandi aziende; poiché inoltre le specifiche non sono ancora ben definite si rischierebbe di implementare funzioni inutili.
-3. __Consolidamento__: i prodotti di alcune aziende iniziano a dominare il mercato, mentre i competitor vengono assorbiti, falliscono o diventano di nicchia. Diminuisce complessivamente il numero di player e l'innovazione rallenta.
-4. __Maturità__: il problema e le specifiche sono ora ben chiare e consolidate. Per un prodotto commerciale è ormai difficile entrare nel mercato, ma per uno open source è paradossalmente il momento migliore: il piccolo team ha uno slancio innovativo che le grosse aziende non hanno più e il loro prodotto può brillare dei vantaggi del mondo open-source, tra cui sicurezza e flessibilità.
-5. __FOSS Domination__: lentamente, il prodotto open-source inizia a erodere il vantaggio tecnologico dei competitor commerciali, che d'altra parte non hanno alcun interesse ad innovare ulteriormente: ciascuna loro innovazione potrebbe infatti essere facilmente copiata ed essi sono inoltre già largamente rientrati del loro investimento iniziale. Il prodotto open-source inizia ad accaparrarsi fette di mercato.
-6. __The FOSS era__: alla fine il progetto open-source domina completamente il nuovo mercato, mentre le grandi aziende devono ripiegare sulla vendita di servizi più che di software.
-
-Il vantaggio di un progetto open-source non è dunque tanto la rapidità di espansione o l'abilità di intercettare il mercato, quanto il fatto che esso permetta una continua innovazione che _segue_ le necessità del mercato, cosa che le grandi aziende faticano a conseguire in quanto ancora legate a un paradigma di investimento in cui una volta fatto il lavoro e guadagnato un tot è più costoso fare manutenzione del software piuttosto che lasciarlo morire.
-
-## _The Emerging Economic Paradigm Of Open Source_
-
-Affrontiamo ora un fenomeno interessante: perché sempre più aziende investono in software open-source? A prima vista parrebbe un controsenso, perché da sempre i produttori di software proteggono il proprio prodotto tramite segreto aziendale: avere codice liberamente consultabile distrugge tale privatezza ed espone all'ascesa di competitor. Per rispondere a questa domanda ci serviamo di un [articolo](http://web.archive.org/web/20120724095330/http://perens.com/works/articles/Economic.html) di Bruce Perens sull'argomento.
-
-Perens fa in primo luogo notare che spesso per diverse aziende il software da sviluppare non è il prodotto, ma una __tecnologia abilitante essenziale__: per esempio, Amazon sviluppa molto software per il sito di e-commerce ma il suo prodotto non è il sito. In tali ambiti la scrittura di codice è un _costo_, non il prodotto su cui guadagnare.
-
-Dal punto di vista economico è poi importante stabilire se la tecnologia dà un vantaggio competitivo, ovvero se essa è __differenziante__ o __non differenziate__. Per fare ciò è sufficiente rispondere alle seguenti domande:
-
-- __Il cliente si accorge degli effetti del software?__ Per esempio, le persone si accorgono dell'esistenza del sistema di raccomandazione dei libri di Amazon?
-- __I competitor non hanno accesso allo stesso software?__ Se Amazon usasse il sistema di raccomandazione venduto anche a Feltrinelli allora non avrebbe senso mantenerlo privato.
-
-Se la risposta a una delle due domande è no, avere un software proprietario non apporta alcun vantaggio: un modello di sviluppo open-source sparpaglia invece i costi e genera valore, motivo per cui sempre più aziende lo scelgono.
-
-Perens conclude il suo articolo riportando una matrice in cui confronta 4 diversi modelli di sviluppo, ognuno con le sue caratteristiche che ne determinano l'applicabilità in diverse situazioni:
-
-- __Retail__: il software è sviluppato per poter essere venduto a chiunque lo desideri;
-- __In-House & Contract__: il software è sviluppato per un singolo committente;
-- __Consortium & Non-Open-Source Collaboration__: un modello secondo cui diverse aziende concorrenti si mettono insieme per sviluppare un software comune (molto poco diffuso);
-- __Open Source__: il software è disponibile gratuitamente e il suo codice è pubblico.
-
-Tali modelli vengono valutati in base all'efficienza (_quanti soldi vanno agli sviluppatori_), al tasso di fallimento del progetto, ai costi di distribuzione, al rischio di plagio, al fatto di proteggere o meno la differenziazione a livello commerciale del cliente e/o del venditore e alla dimensione richiesta del mercato perché il progetto sia un successo.
-
-{% responsive_image path: 'assets/04_confronto.png' %}
-
-Come si può notare dalla tabella, non tutti i paradigmi proteggono il vantaggio competitivo, con differenze dal punto di vista del cliente e del produttore: è dunque importante scegliere il modello di sviluppo che più si confà alle proprie esigenze.
-
-## _An empirical study of open-source and closed-source software products_
-
-Concludiamo il discorso sul mondo open-source riportando uno [studio](https://www.researchgate.net/publication/3188403_An_empirical_study_of_open-source_and_closed-source_software_products) portato avanti da J.W. Paulson e altri con lo scopo di verificare alcune affermazioni ritenute vere nei riguardi del software open-source.
-
-{% responsive_image path: 'assets/04_empirical-study.png' %}
-
-Per ognuna di tali affermazioni l'articolo definisce una metrica e la calcola nei riguardi di progetti open-source e closed-source, concludendo così che solo alcune delle dicerie sull'open-source risultano vere mentre molte altre (es. _è più sicuro_, _è più veloce_) si rivelano essere al più circostanziali.
-
-## Le sfide del modello open-source
-
-Per sua natura il sistema di sviluppo open-source pone delle sfide peculiari sconosciute all'ambiente di sviluppo tradizionale; prima di affrontare dunque gli strumenti di design e organizzazione del lavoro che sono nati per risolvere queste problematiche diamone una breve panoramica.
-
-### Difficile integrazione del software
-
-Quella dell'integrazione del software è in realtà una vecchia sfida che viene enormemente amplificata nell'ambito FOSS. Per integrare le nuove feature sviluppate in un software stabile diversi modelli avevano costruito le proprie pratiche:
-
-- Nel modello a cascata l'integrazione era una fase circoscritta e a sé stante.
-- A tale struttura molto rigida si contrappone lo schema innovativo _Stabilize & Synchronize_ nato in ambiente Microsoft: durante il giorno gli sviluppatori lavorano sul proprio pezzo di codice in cui sono responsabili, e di notte il software viene ricompilato da zero. La mattina dopo, si avevano dunque due possibilità:
-  - la compilazione falliva, il responsabile veniva trovato e _"punito"_;
-  - la compilazione aveva successo, il software integrato è quindi nella _"versione migliore possibile"_.
-- In XP l'integrazione veniva eseguita più volte al giorno in modo esclusivo: un solo sviluppatore alla volta poteva integrare il proprio lavoro sull'unica macchina di integrazione disponibile; questo permetteva di individuare facilmente eventuali problemi di integrazione e risolverli con rapidità.
-
-Ma come organizzare l'integrazione nel mondo open-source? Per sua natura, in questo ambito l'integrazione viene eseguita continuamente e senza coordinazione a priori: è anarchia totale, con lo svantaggio che da un giorno all'altro una enorme parte della codebase potrebbe cambiare in quanto un singolo sviluppatore potrebbe integrare mesi e mesi di lavoro in un'unica botta. Vedremo più avanti che strumenti si sono costruiti per contenere tale problematica.
-
-### Sfaldamento del team
-
-Nell'open source nascono inoltre problemi riguardanti la gestione del team. Occorre decidere:
-
-- come comunicare
-- come tenersi uniti
-- come coordinarsi
-- come ottenere nuove collaborazioni
-
-Per comunicare si utilizza di solito __internet__: si potrebbe dire che senza internet non potrebbe esistere il concetto stesso di open source. In particolare si utilizzano spesso dei __forum__ per organizzare il lavoro, in modo da tenere la community unita e rispondere dubbi ai nuovi utenti.
-
-Per quanto riguarda il coordinamento del lavoro approfondiremo nelle prossime lezioni vari strumenti per la sincronizzazione del lavoro e di versioning per codice e documentazione (come __git__).
-
-Deve poi essere facile, addirittura banale, poter compilare il codice e ricreare l'ambiente di sviluppo omogeneo per tutti; si utilizzano quindi strumenti di __automatizzazione delle build__ (come i __Makefile__) in modo che chiunque voglia partecipare possa farlo indipendentemente dalla propria configurazione software e hardware.
-
-È infine importante _educare_ i reporter dei bug e avere un sistema per organizzare per le __segnalazioni di errori__: il sistema dovrebbe essere accessibile a tutti in modo da evitare segnalazioni duplicate e consentire una facile organizzazione delle stesse. Vedremo più avanti come anche una segnalazione d'errore avrà il suo "ciclo di vita".
diff --git a/_posts/2022-10-17-scm.md b/_posts/2022-10-17-scm.md
deleted file mode 100644
index eb9e4438ba22f705b049ab11101b6d7b4ca3d17f..0000000000000000000000000000000000000000
--- a/_posts/2022-10-17-scm.md
+++ /dev/null
@@ -1,169 +0,0 @@
----
-layout: post
-title: "[05] Software Configuration Management"
-date: 2022-10-17 14:40:00 +0200
-toc: true
----
-
-# SCM
-Le soluzioni di Software Configuration Management nascono da problemi complessi purtroppo molto comuni nel mondo dello sviluppo software, come:
-- pubblicare un _hotfix_ su una versione precedente a quella in cui si sta sviluppando. Può essere difficile localizzare le versioni vecchie, modificarle e rimappare le modifiche sulle versioni nuove;
-- condividere lavori con altri gestendo accessi contemporanei e conflitti;
-- stabilire la responsabilità di ciascuna linea di codice. 
-
-Il Software Configuration Management è l'insieme di pratiche che hanno l'obiettivo di rendere sistematico il processo di sviluppo, tenendo traccia dei cambiamenti in modo che il prodotto sia in ogni instante in uno stato (_configurazione_) ben definito.
-
-## Storia
-Il Configuration Management negli anni '50 nell'ambito dell'industria aerospaziale. 
-Alla fine degli anni '70 inizia ad essere applicato all'ingegneria del software.
-
-Gli oggetti di cui si controlla l'evoluzione sono detti _configuration item_ o _artifact_ (manufatti, solitamente file).
-Dunque l'SMC ci permette di controllare le revisioni degli _artifact_ e il risultato di tali revisioni,questo processo è molto utile per la generazione di un prodotto a partire da una configurazione ben determinata.
-
-### Manufatti
-Gli _"oggetti"_ di cui si controlla l'evoluzione sono detti _configuration item_ o manufatti; generalmente sono file.
-Se si cambia nome a un file è come eliminarne uno e partire da zero con uno nuovo. 
-Originariamente i tool tracciavano i file indipendentemente, senza un senso logico (una _configurazione_) comune. 
-
-{% responsive_image path: 'assets/05_configuration-management.png' %}
-
-- anni '80: strumenti locali (SCCS, ...)
-- anni '90: strumenti client-server centralizzati (CVS, subversion, ...)
-- anni '00: strumenti distribuiti peer-to-peer (git, mercurial, bazaar, ...)
-
-Git nasce da un'esigenza di Linus Torvalds con il kernel Linux.
-
-## Centralizzato vs decentralizzato
-<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/4XpnKHJAok8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
-
-Il mondo open source preferisce un approccio decentralizzato al version control. Perché?
-- è possibile lavorare offline;
-- è molto più veloce, perché la rete non fa più da _bottleneck_;
-- supporta diversi modi di lavorare:
-    - simil centralizzato: un repository viene considerato "di riferimento";
-    - due peer che collaborano direttamente;
-    - gerarchico a più livelli (kernel Linux).
-
-Non c'è sincronizzazione automatica, ma ci sono comandi espliciti per fare _merge_ tra repository remote.
-In Git, per via della sua struttura modulare, è possibile utilizzare il proprio algoritmo _merge_ rispetto a quelli già inclusi. 
-
-
-### Cosa tracciare?
-Qualunque sistema si usi, occorre prendere decisioni importanti che influenzano la replicabilità della produzione.
-- Si traccia l'evoluzione anche di componenti fuori dal nostro controllo (librerie, compilatori)?
-- Si archiviano i file che sostitusicono il prodotto (eseguibile, ecc...)?
-
-La risposta ad entrambe queste domande è __no__, perché è scomodo, anti economico, costoso...
-Questo porta però problemi, perché non c'è perfetta replicabilità.
-
-## Meccanismo di base
-Ogni cambiamento è regolato da:
-- __check-out__: dichiara la volontà di lavorare partendo da una particolare revisione di un manufatto (o di una configurazione di diversi manufatti);
-- __check-in__ (o __commit__): dichiara la volonta di registrarne una nuova (spesso chiamata change-set).
-
-Queste operazioni vengono attivate rispetto a un _repository_.
-Scambio di dati tra il repository (che contiene tutte le configurazioni) e il workspace (l'ambiente in cui si trova nel filesystem).
-
-Solitamente ho un repository e $$n$$ workspace, uno per ogni ambiente dove sto lavorando.
-
-### Repository
-La repository mantiene:
-- date
-- etichette
-- versioni
-- diramazioni (branches)
-- ecc...
-
-Per risparmiare spazio, le repository salvano solo le differenze tra una versione e l'altra. 
-In realtà, Git non fa così, perché usa link simbolici: fare il _checkout_ di una specifica versione è instantaneo.
-
-Le repository possono essere centralizzate o distribuite.
-
-Nei sistemi di versioning distribuiti c'è il concetto di __hashing__, in modo da identificare file uguali anche se in posizioni diverse.
-Per confrontare storie diverse si utilizzano gli hash dei file e delle directory.
-
-### Accesso concorrente
-
-Quando il repository è condiviso da un gruppo di lavoro, nasce il problema di gestirne l'accesso concorrente. 
-Esistono due modelli:
-- __modello _'pessimistico'___ (RCS): prevedo il possibile conflitto assicurandomi che chi lavora sia l'unico con l'accesso in scrittura. Funziona solo in ambienti centralizzati, nell'open source non può funzionare.
-- __modello _'ottimistico'___ (CVS): il sistema si disinteressa del problema e fornisce supporto per le attività di _merge_ di change-set paralleli potenzialmente conflittuali. 
-    
-Il modello ottimistico può essere regolato con i branch: l'attività di _merge_ è quindi fondamentale.
-CVS/Subversion scoraggiava i branch, Git li rende facili e li incoraggia.
-In Git, l'uso dei branch è talmente comune che a volte è necessario introdurre delle politiche (come GitFlow) sul loro utilizzo.
-
-# Git 
-
-{% responsive_image path: 'assets/05_git-schema.png' %}
-
-In figura, possiamo osservare 4 _livelli_ ordinati:
-- __working directory__ (WD): rappresenta la _configurazione_ della directory di lavoro sul filesystem - esiste indipendentemente da Git.
-Può essere vista come l'unione dei _tracked_ and _untracked_ files; 
-- __index__ (o __area di staging__): insieme dei _tracked_ files da Git.
-- ($$n$$?) __local repository__: insieme delle modifiche committate e relativo storico. 
-- ($$n$$) __remote repository__: branch remoto; è possibile avere sia più branch per progetto remoto che più progetti remoti configurati.
-
-Il termine ___repository___ è abbastanza _misleading_, perché è comunemente associato ad un progetto mentre in questa astrazione a livelli corrisponde di fatto a un __branch__.
-
-Il passaggio tra un livello e l'altro non è mai automatico, ma è sempre esplicitato da un'operazione. 
-
-## Operazioni di base
-
-È consigliata la lettura di [Git Cheatsheet](http://ndpsoftware.com/git-cheatsheet.html).
-
-{% responsive_image path: 'assets/05_git-commits.png' %}
-
-Per ogni branch c'è un puntatore all'ultimo commit di tale branch.
-L'HEAD punta all'ultimo commit in cui siamo: normalmente corrisponde al puntatore del branch corrente; quando non è così siamo in una situazione di _HEAD scollegato_. 
-È utile potersi spostare tra i commit per controllare revisioni precedenti, ma in caso di nuovi commit è importante creare un nuovo branch per poterci riferire ad esse.
-
-### git commit - record changes to the repository
-
-Il comando git commit ci permette di _salvare_ del contenuto dall'index al branch locale.
-
-Dopo aver creato il commit, l'`HEAD` e il puntatore al branch corrente puntano al nuovo commit.
-Anche il contenuto dell'index equivale al contenuto del commit.
-
-{% responsive_image path: 'assets/05_git-commit.png' %}
-
-#### -\-amend
-
-Con l'opzione -\-amend è possibile rimpiazziare facilmente l'ultimo commit con uno nuovo. 
-
-{% responsive_image path: 'assets/05_git-commit-amend.png' %}
-
-### git switch - switch branches
-
-Il comando git switch ha un sottoinsieme delle funzionalità del comando git checkout ed è più semplice da utilizzare.
-
-Permette di passare a un nuovo branch semplicemente modificando l'HEAD (e di conseguenza il contenuto dell'index e dei file). 
-
-{% responsive_image path: 'assets/05_git-checkout.png' %}
-
-### git merge - join two or more development histories together
-
-Il comando git merge è utile per unire branch (o più in generale alberi) insieme.
-
-Se i due branch non sono divergenti, il merge avviene in modo banale con un _fast-forward_: nessun ulteriore commit verrà cambiato, verrà solo modificato il puntatore del branch e l'HEAD. 
-Per forzare la creazione di un merge di commit (in GitFlow è apprezzato) occorre utilizzare l'opzione `--no-ff`.
-
-In tutti gli altri casi, il merge può concludersi con successo oppure possono avvenire conflitti. 
-Per risolverli, Git ci proporrà un'interfaccia simile alla seguente.
-
-    <<<<<<< yours:sample.txt
-    Conflict resolution is hard;
-    let's go shopping.
-    =======
-    Git makes conflict resolution easy.
-    >>>>>>> theirs:sample.txt
-
-Una volta risolti tutti i conflitti è sufficiente commitare le modifiche concludendo quindi il merge.
-
-{% responsive_image path: 'assets/05_git-merge.png' %}
-
-### git reset - reset current HEAD to the specified state
-
-Il comando git reset reimposta il contenuto dei file nell'_index_ (e, opzionalmente con l'opzione `--hard` nella WD) all'ultimo commit puntato da HEAD o ad un altro commit.
-
-{% responsive_image path: 'assets/05_git-reset.png' %}
diff --git a/_posts/2022-10-19-git-workflow.md b/_posts/2022-10-19-git-workflow.md
deleted file mode 100644
index 5984113a325b83b23e5ffbd406bc7384611f561c..0000000000000000000000000000000000000000
--- a/_posts/2022-10-19-git-workflow.md
+++ /dev/null
@@ -1,455 +0,0 @@
----
-layout: post
-title: "[06] Git workflow"
-date:   2022-10-19 14:45:00 +0200
-toc: true
----
-
-# Git Workflow
-
-In Git, l'utilizzo dei branch è fortemente incentivato dal suo design e dalle sue funzionalità, rendendo praticamente impossibile lavorare senza utilizzarli. I branch consentono di creare versioni separate del codice, permettendo di lavorare su diverse funzionalità o correzioni di bug in modo indipendente e senza interferire con il codice principale. 
-C'è __libertà completa__ sul loro utilizzo: tutti i branch hanno pari importanza, chiunque può crearli e nominarli in qualunque modo.
-
-Lavorando in un team, è quindi necessario stabilire delle politiche sui tipi e i nomi di branch, in modo da organizzare il lavoro al meglio. 
-
-## GitFlow
-
-GitFlow è una tecnica di organizzazione dei branch e delle repository che prevede la creazione sia di diversi tipi di branch a vita limitata che il loro _merge_ guidato.
-
-È disponibile online una [cheatsheet](https://danielkummer.github.io/git-flow-cheatsheet/) che fornisce una panoramica veloce delle principali operazioni e dei comandi di GitFlow. 
-Si tratta di uno strumento utile per chi è alle prime armi con questa tecnica di organizzazione dei branch, ma non esaustivo: per una comprensione più approfondita del metodo, è meglio integrarlo con altre risorse.
-
-I branch e i tipi di branch previsti da GitFlow sono:
-- branch master;
-- branch develop;
-- feature branches;
-- release branches;
-- hotfix branches.
-
-### `develop` e `master`
-
-{% responsive_image path: 'assets/06_gitflow-develop-master.png' %}
-
-In GitFlow, ci sono due branch che hanno una durata più lunga rispetto ai branch temporanei utilizzati per lavorare su specifiche funzionalità o correzioni di bug:
-- __`master`__: contiene le versioni stabili del codice sorgente, pronte per essere consegnate o rilasciate al cliente o al pubblico. Queste versioni sono considerate _affidabili_ e _testate_, quindi possono essere utilizzate in produzione;
-- __`develop`__: è il ramo di integrazione in cui vengono integrati i contribuiti di tutti i gruppi; è anche il punto di partenza degli altri tipi di branch.
-
-Al termine di ogni rilascio, il contenuto del branch `develop` viene integrato nel branch `master`, che rappresenta la versione stabile del codice. Le _versioni notturne_, invece, sono versioni di sviluppo che vengono rilasciate periodicamente e contengono le ultime modifiche apportate al codice. Esse vengono create partendo dal branch `develop`, che rappresenta il punto di integrazione di tutti i contributi dei gruppi di lavoro.
-
-### Feature
-
-{% responsive_image path: 'assets/06_gitflow-feature.png' width: "400" %}
-
-I __*feature branch*__ sono branch temporanei utilizzati per sviluppare nuove funzionalità o per correggere bug. __Possono essere creati solo a partire dal branch `develop`__ e vengono utilizzati per isolare il lavoro su una specifica funzionalità o problema dal resto del codice. 
-Quando il lavoro è completato, il feature branch viene integrato di nuovo nel `develop` tramite un merge. 
-In questo modo, è possibile lavorare in modo organizzato su diverse funzionalità o problemi senza interferire tra loro.
-Per integrare il lavoro svolto in un feature branch nel branch `develop`, è necessario eseguire un merge del feature branch nel `develop`. 
-Ci sono diversi modi di fare ciò, a seconda delle preferenze e delle esigenze specifiche.
-Un modo semplice di fare il merge è utilizzare il comando `git merge` dalla riga di comando. 
-Se il merge non è possibile a causa di conflitti, sarà necessario risolverli manualmente prima di poter completare l'operazione. 
-Una volta risolti i conflitti, sarà necessario creare un nuovo commit per finalizzare il merge.
-
-Per iniziare una feature...
-```bash
-$ git checkout develop                  # entra nel branch develop
-$ git branch feature/myFeature          # crea un branch di feature
-$ git checkout feature/myFeature        # entra nel branch appena creato
-```
-
-Al termine della feature, integro:
-
-```bash
-$ git checkout develop                  # entra nel branch develop
-$ git merge --no-ff feature/myFeature   # mergia il branch di feature
-$ git branch -d feature/myFeature       # elimina il branch di feature
-```
-#### `--no-fast-forward`
-
-{% responsive_image path: 'assets/06_gitflow-feature-ff.png' %}
-
-Di default, Git risolve il merge di due branch con la stessa storia banalmente eseguendo il _fast forward_, ovvero spostando il puntatore del branch di destinazione all'ultimo commit del branch entrante.
-
-In GitFlow è preferibile esplicitamente __disabilitare il fast forward__ (con l'opzione `--no-ff`) durante il merge in `develop` in quanto è più facile distinguere il punto di inizio e il punto di fine di una feature.
-
-### Squashing
-
-Usando Git è anche possibile eseguire in fase di merge lo _squashing_ dei commit, ovvero la fusione di tutti i commit del branch entrante in uno solo. 
-Questa operazione è utile per migliorare la leggibilità della history dei commit su branch grossi (come `develop` o `master`) ma il suo uso in GitFlow è opinabile: il prof. Bellettini consiglia di non utilizzarlo, in modo da mantenere i commit originali.
-
-### Release
-
-{% responsive_image path: 'assets/06_gitflow-release.png' %}
-
-Lo scopo di creare una release è __cristalizzare l'insieme delle funzionalità__ presente sul branch `develop` all'inizio di essa dedicandosi solo alla sistemazione degli errori o alle attività necessarie per il deploy (modifica del numero di versione, ...). 
-L'insieme delle funzionalità rilasciate è quello presente sul branch `develop` al momento di inizio di una release. 
-
-I bug fix possono essere ri-mergiati in `develop`, anche utilizzando la funzionalità __cherry-pick__ di Git; essa permette di selezionare un commit specifico da un ramo e applicarlo in un altro ramo. 
-Ad esempio, se si ha un ramo di sviluppo ("`develop`") e un ramo di release ("`release`"), è possibile utilizzare il cherry-pick per selezionare solo i commit che contengono bugfix e applicarli al ramo di release, senza dover fare un merge di tutto il ramo di sviluppo. 
-Ciò può essere utile in casi in cui si vuole mantenere la stabilità del ramo di release, includendo solo i bugfix considerati essenziali per la release.
-
-Per iniziare una nuova release è sufficiente creare un nuovo branch da `develop`:
-```bash
-$ git checkout -b release/v1.2.3 develop
-```
-
-Al termine della creazione della release, è necessario fare il merge della release nel branch `master` e nel branch `develop`. 
-Il merge in `master` rappresenta il rilascio della nuova versione del codice, che diventa disponibile per il pubblico o per il cliente. 
-Il merge in `develop`, invece, integra le modifiche apportate durante la creazione della release nel branch di sviluppo, in modo che siano disponibili per le release future. 
-In questo modo, è possibile gestire in modo organizzato il ciclo di vita del codice e il flusso di lavoro.
-```bash
-$ git checkout master               # entra nel branch master
-$ git merge --no-ff release/v1.2.3  # mergia la feature
-$ git tag -a v1.2.3                 # tagga sul branch master il rilascio
-$ git checkout develop              # entra nel branch develop
-$ git merge --no-ff release/v1.2.3  # mergia la feature
-$ git branch -d release/v1.2.3      # elimina il branch della feature
-```
-
-In Git, i tag sono etichette che possono essere applicate a un commit per segnalarne l'importanza o per marcare un punto specifico dello storico del repository. Un __tag è un puntatore costante al commit__ a cui è stato applicato, quindi non cambia mai e permette di fare riferimento in modo stabile a una versione specifica del codice. Al contrario, i branch sono puntatori dinamici che vanno avanti nel tempo, seguendo l'evoluzione del codice attraverso i nuovi commit
-
-In GitFlow, le release sono versioni stabili del codice che vengono rilasciate al pubblico o al cliente. 
-Ogni release viene creata partendo dal branch `develop` e viene gestita come un branch a sé stante, che viene chiuso una volta che tutte le modifiche previste sono state integrate. 
-Al contrario, le feature sono branch temporanei utilizzati per sviluppare nuove funzionalità o per correggere bug. 
-È possibile avere più feature aperte contemporaneamente, ma solo una release rimanere aperta in un dato istante.
-
-### Hotfix
-
-Un _hotfix_ è una riparazione veloce di difetti urgenti senza dover aspettare la prossima release.
-È l'unico caso per cui non si parte da `develop`, ma dall'ultima - o una particolare - versione rilasciata su `master`.
-
-```bash
-$ git checkout -b hotfix/CVE-123 master # crea un branch di hotfix
-```
-
-Quando lo chiudo:
-```bash
-$ git checkout master                   # entra nel branch master
-$ git merge --no-ff hotfix/CVE-123      # mergia l'hotfix
-$ git tag -a hotfix/CVE-123             # tagga sul branch master il rilascio
-$ git checkout develop                  # entra nel branch develop
-$ git merge --no-ff hotfix/CVE-123      # mergia l'hotfix
-$ git branch -d hotfix/CVE-123          # elimina il branch di hotfix
-```
-
-## Limiti
-
-Quali sono i limiti di git presentato così?
-
-Git e GitFlow come sono stati esposti presentano numerosi vincoli, tra cui:
-- la __mancanza di un sistema di autorizzazione granulare__, ovvero la possibilità di assegnare permessi in modo specifico e mirato a diverse funzionalità o risorse. Inoltre, non esiste una distinzione tra diversi livelli di accesso, quindi o si ha accesso completo a tutte le funzionalità o non si ha accesso a niente;
-- l'__assenza di code review__, ovvero il processo di revisione del codice sorgente da parte di altri sviluppatori prima che venga integrato nel codice base.
-
-### git request-pull - generates a summary of pending changes
-
-Il tool `git request-pull` è un comando di Git che serve per formattare e inviare una proposta di modifiche a un repository tramite una mailing list. 
-Il comando crea un messaggio di posta elettronica che chiede al maintainer del repository di "pullare" le modifiche, ovvero di integrarle nel codice base. 
-Oggi, questa pratica è stata in molti progetti sostituita dalle pull request, che sono richieste di integrazione delle modifiche presentate attraverso un'interfaccia web. 
-Le pull request offrono una serie di vantaggi rispetto alle richieste via email, come una maggiore trasparenza del processo di integrazione, una maggiore efficienza e una maggiore facilità di utilizzo.
-
-La sintassi del comando è la seguente:
-
-    git request-pull [-p] <start> <URL> [<end>]
-
-Per esempio, i contributori Linux usano questo strumento per chiedere a Linus Torvalds di unire le modifiche nella sua versione.
-L'output generato mostra file per file le modifiche fatte e i commenti dei commit creati, raggruppati per autore.
-
-```
-$ git request-pull master git@gitlab.di.unimi.it:silab-gang/sweng.git lezioni/09
-The following changes since commit a16f3a0488c062d7b61dc4af15c2ba8081166040:
-
-  Handle '/sweng' path (2022-11-25 19:14:40 +0100)
-
-are available in the Git repository at:
-
-  git@gitlab.di.unimi.it:silab-gang/sweng.git lezioni/09
-
-for you to fetch changes up to 4ac534bcd31c8c0353070c3f42eea09737b497b5:
-
-  Refactor notes on Factory method pattern (2022-12-19 09:58:32 +0100)
-
-----------------------------------------------------------------
-Daniele Ceribelli (6):
-      Add pattern Adapter
-      Add notes until decorator
-      Finish notes
-      Add notes until factory method pattern
-      Add notes until Abstract Factory pattern
-      Finish notes with all patterns
-
-Marco Aceti (14):
-      Add lesson notes
-      Add flyweight pattern
-      Add observer pattern
-      Add more patterns
-      Fix typo
-      Merge branch 'master' into lezioni/09
-      Replace hook UMLs images with PlantUML
-      Merge branch 'master' into lezioni/09
-      Fix GitLab CI (take 2)
-      Refactor diagrams until 'Strategy'
-      Refactor all remaining diagrams
-      Merge remote-tracking branch 'gitlab/master' into lezioni/09
-      Merge remote-tracking branch 'gitlab/master' into lezioni/09
-      Make 'Decorator' code samples *readable*
-
-Matteo Mangioni (18):
-      Add introduction
-      Integrate Singleton notes
-      Add images for first patterns
-      Integreate Iterator notes
-      Integrate Chain of Responsibility notes
-      Integrates FlyWeight notes
-      Add NullObject pattern
-      Merge branch 'lezioni/09' of gitlab.com:silab-gang/sweng into lezioni/09
-      Merge branch 'mangio/patterns-appunti' into lezioni/09
-      Integrate Strategy section
-      Integrate Observer notes
-      Refactor notes on pattern Adapter
-      Refactor notes on pattern Facade
-      Make already reviewed notes follow new style guide
-      Refactor notes on pattern Composite
-      Refactor notes on pattern Decorator
-      Refactor notes on pattern State
-      Refactor notes on Factory method pattern
-
- _posts/2022-10-26-Patterns.md               | 1301 ++++++++++++++++++++++++++
- assets/09_esempio-abstract-factory.png      |  Bin 0 -> 2362937 bytes
- assets/09_facade.png                        |  Bin 0 -> 73981 bytes
- assets/09_model-view-controller.png         |  Bin 0 -> 2751267 bytes
- assets/09_model-view-presenter.png          |  Bin 0 -> 2118448 bytes
- assets/09_nullObject-valori-non-assenti.png |  Bin 0 -> 67792 bytes
- 6 files changed, 1301 insertions(+)
- create mode 100644 _posts/2022-10-26-Patterns.md
- create mode 100644 assets/09_esempio-abstract-factory.png
- create mode 100644 assets/09_facade.png
- create mode 100644 assets/09_model-view-controller.png
- create mode 100644 assets/09_model-view-presenter.png
- create mode 100644 assets/09_nullObject-valori-non-assenti.png
-```
-
-Questo modello è molto più _peer to peer_ delle pull request proposte dai sistemi semi-centralizzati spiegati in seguito.
-
-# Hosting centralizzato
-
-Un hosting centralizzato Git è un servizio che fornisce una repository centrale per i progetti Git dove i contributi vengono integrati e gestiti, garantendo una maggiore trasparenza e controllo del processo di sviluppo e mantenendo molti vantaggi della decentralizzazione, come la possibilità di lavorare in modo asincrono e autonomo.
-
-Gli hosting centralizzati come GitHub e GitLab, nella loro costante evoluzione, spesso inventano nuovi meccanismi e provano a imporre nuovi workflow, come il GitHub Flow o il GitLab Flow, per semplificare e ottimizzare il processo di sviluppo. Tuttavia, è importante valutare attentamente questi nuovi approcci e verificare se si adattano alle esigenze specifiche del progetto e della squadra di sviluppo.
-Inoltre, molti servizi di hosting centralizzati offrono funzionalità aggiuntive, come la possibilità di eseguire il "fork" di un repository, inviare _pull request_ per le modifiche e di utilizzare strumenti di continuous integration (CI) per testare automaticamente le modifiche apportate al codice.
-
-## Fork
-
-Il "fork" di un repository Git è una __copia del repository originale__ che viene creata su un account di hosting diverso dal proprietario originale. 
-Questo permette a un altro sviluppatore di creare una copia del repository e di lavorare su di essa senza influire sul lavoro del proprietario originale e __senza la sua autorizzazione__. 
-È possibile quindi mantenere una _connessione_ tra i due repository e condividere facilmente le modifiche apportate.
-
-La maggioranza delle piattaforme di hosting centralizzato __ottimizza la condivisione dello spazio degli oggetti__, utilizzando un'unica _repository fisica_ per tutti i fork.
-Tuttavia, questo può comportare alcune problematiche di sicurezza, come ad esempio la difficoltà per la piattaforma di stabilire in quale fork si trova un determinato oggetto in caso di conflitto o la possibilità che un utente malintenzionato possa modificare o eliminare accidentalmente oggetti di altri fork. 
-Per questo motivo, è importante che le piattaforme implementino __misure di sicurezza adeguate__ per proteggere i dati dei fork e garantire la tracciabilità delle modifiche ([esempio sul kernel Linux](https://github.com/torvalds/linux/commit/b4061a10fc29010a610ff2b5b20160d7335e69bf)). 
-
-## Review / Pull request
-
-Tra la creazione di una pull request e il suo _merge_, specialmente nei progetti open source (dove chiunque può proporre qualsiasi patch) è fondamentale prevedere un processo di __review__.
-
-{% responsive_image path: 'assets/06_pull-request.png' %}
-
-La funzionalità di _review/pull request_ permette di facilitare le interazioni tra gli sviluppatori utilizzando il sito di hosting come luogo comune per la discussione informale e la revisione delle modifiche.
-
-## Continous integration (CI)
-
-Come accennato in precedenza, molti servizi di hosting centralizzati offrono strumenti di __continuous integration__ (CI) che possono essere utilizzati per testare automaticamente le modifiche proposte nella pull request. 
-Questi strumenti consentono di verificare che le modifiche non introducano errori o vulnerabilità e di garantire che il codice sia pronto per essere integrato nel repository principale.
-Possono essere utilizzati anche per eseguire automaticamente la _suite di test_ o automatizzare il deployment.
-
-{% responsive_image path: 'assets/06_ci-cd.png' %}
-
-# Gerrit
-
-Gerrit è un __sistema di review__ del codice sviluppato internamente da Google per gestire i progetti interni; si basa sul concetto di "peer review": tutti gli sviluppatori sono autorizzati a fare review delle proposte di modifica di qualsiasi zona di codice.
-
-Nel processo di review di Gerrit, i __developer__ possono sottoporre proposte di cambiamento utilizzando un sistema di "patch" che descrive le modifiche apportate al codice. 
-I __reviewer__, ovvero gli altri sviluppatori del progetto, possono quindi esaminare le patch e decidere se accettarle o rifiutarle. 
-Una volta che una patch ha ricevuto un numero sufficiente di review positivi, viene automaticamente integrata nel __repository principale autoritativo__   in cui tutti hanno accesso in sola lettura.
-
-Gerrit obbliga a strutturare le modifiche (_changeset_) in un unico commit (tecnica _squash_) al momento dell'accettazione. 
-Ciò significa che tutte le modifiche apportate devono essere fuse in un unico commit, in modo da rendere più facile la gestione del repository. 
-Al momento della review, invece, le modifiche rimangono separate in versioni singole, ovvero ogni modifica viene presentata come un commit separato, in modo che i reviewer possano esaminarle più facilmente.
-
-## Verifier
-
-Il verifier è uno strumento o un processo che viene utilizzato in Gerrit per verificare che le modifiche proposte siano corrette e funzionino come dovrebbero. 
-In particolare, il verifier scarica la patch, la compila, esegue i test e controlla che ci siano tutte le funzioni necessarie. 
-Se il verifier rileva dei problemi, può segnalarli al team di sviluppo perché vengano corretti prima che la patch venga accettata.
-
-Una volta terminato il proprio processo, approva le modifiche votandole positivamente.
-Solitamente sono necessari 1 o 2 voti per procedere.
-
-## Approver
-
-Una volta verificata, una proposta di modifiche deve essere anche approvata. 
-L'approvatore deve determinare la risposta alle seguenti domande riguardo la proposta di modifiche:
-- _è valida per lo scopo del progetto?_
-- _è valida per l'architettura del progetto?_
-- _introduce nuove falle nel design che potrebbero causare problemi in futuro?_
-- _segue le best practices stabilite dal progetto?_
-- _è un buon modo per implementare la propria funzione?_
-- _introduce rischi per la sicurezza o la stabilità?_
-
-Se l'approver ritiene che la proposta di modifiche sia valida, può approvarla scrivendo "LGTM" (acronimo di _"Looks Good To Me"_) nei commenti della pull request.
-
-# Strumenti dell'open source
-
-Gli strumenti dell'open source sono una serie di programmi, librerie e servizi che vengono utilizzati per sviluppare progetti open source. 
-Questi strumenti sono pensati per semplificare il processo di sviluppo e gestione di progetti open source, rendendoli accessibili a una comunità di sviluppatori e contribuenti.
-
-## Build automation
-
-La build automation è un processo fondamentale nello sviluppo di software open source, che consiste nel creare un sistema automatizzato per compilare il codice sorgente in un eseguibile. 
-Questo processo è importante perché consente di risparmiare tempo e risorse, evitando di dover compilare manualmente il codice ogni volta che si apportano modifiche. 
-Inoltre, la build automation garantisce una maggiore qualità e coerenza del software, poiché il processo di compilazione viene eseguito in modo uniforme ogni volta.
-
-### make
-
-`make` è uno strumento di build automation che viene utilizzato per automatizzare il processo di compilazione di un progetto. 
-In particolare, `make` viene utilizzato per specificare come ottenere determinati _targets_ (obiettivi), ovvero file o azioni che devono essere eseguite, partendo dal codice sorgente. 
-Ad esempio, in un progetto di sviluppo software, un _target_ potrebbe essere il file eseguibile del programma, che viene ottenuto compilando il codice sorgente. 
-`make` segue la filosofia _pipeline_, ovvero prevede l'utilizzo di singoli comandi semplici concatenati per svolgere compiti più complessi.
-
-È supportata la _compilazione incrementale_, ovvero il fatto di compilare solo le parti del progetto che sono state modificate dall'ultima volta, al fine di velocizzare il processo. 
-Inoltre, vengono gestite le _dipendenze_ tra file, ovvero le relazioni tra i diversi file che compongono il progetto: se un file sorgente dipende da un altro file, make assicura che il file dipendente venga compilato solo dopo che il file da cui dipende è stato compilato. 
-Ciò garantisce che il progetto venga compilato in modo coerente e che le modifiche apportate a un file siano considerate correttamente nella compilazione dei file dipendenti.
-
-```make
-CC=gcc
-CFLAGS=-I.
-
-%.o: %.c $(DEPS)
-  $(CC) -c -o $@ $< $(CFLAGS)
-
-hellomake: hellomake.c hellofunc.o
-  $(CC) -o hellomake hellomake.o hellofunc.o $< $(CFLAGS)
-```
-
-Nell'esempio, se il _target_ hellomake (definito dai file `hellomake.c` e `hellofunc.o`) è stato aggiornato, occorre ricompilarlo utilizzando i comandi sotto.
-
-Tuttavia, make lavora a un livello molto basso, il che può rendere facile commettere errori durante la sua configurazione e utilizzo.
-
-Non c'è portabilità tra macchine (ambienti) diverse.
-
-#### Makefile
-
-Un _Makefile_ è un file di testo che contiene le istruzioni per il programma make su come compilare e linkare i file sorgente di un progetto. 
-Ogni riga del Makefile definisce un obiettivo o una dipendenza, insieme ai comandi che devono essere eseguiti per raggiungerlo. 
-L'utilizzo del Makefile permette di automatizzare la compilazione e il linkaggio dei file sorgente, semplificando il processo di sviluppo di un progetto. 
-Nell'esempio menzionato, il Makefile definisce il target `hellomake`, che dipende dai file `hellomake.c` e `hellofunc.o`, e fornisce i comandi per compilarli e linkarli insieme.
-
-#### Generazione automatica
-
-Sono stati creati dei tool (`automake`, `autoconf`, `imake`, ...) che _generano_ `Makefile` ad-hoc per l'ambiente attuale.
-
-Il _mantra_:
-```bash
-$ ./configure
-$ make all
-$ sudo make install
-```
-era largamente utilizzato per generare un Makefile ad-hoc per l'ambiente attuale e installare il software sulla macchina in modo automatico. 
-`automake`, `autoconf`, e `imake` sono strumenti che aiutano a questo scopo, generando Makefile che possono essere utilizzati per compilare e installare il software in modo automatico.
-
-### Ant 
-Ant nasce in Apache per supportare il progetto Tomcat.
-Data una __definizione in XML__ della struttura del progetto e delle dipendenze invocava comandi programmati tramite classi Java per compilare il progetto.
-
-Il vantaggio è che Java offre un livello d'astrazione sufficiente a rendere il sistema di build portabile su tutte le piattaforme.
-
-Nella versione base supporta integrazioni con altri tool come CVS, Junit, FTP, JavaDOCS, JAR, ecc...
-Non solo compila, ma fa anche deployment.
-Il deployment consiste nell'installare e configurare un'applicazione o un sistema su uno o più server o ambienti di esecuzione. 
-Nel contesto di Ant, il deployment può includere l'invocazione di comandi per copiare i file del progetto sui server di destinazione, configurare le impostazioni di sistema o dell'applicazione, avviare o fermare servizi o processi, e così via. 
-In questo modo, Ant può essere utilizzato non solo per compilare il progetto, ma anche per distribuirlo e rendere disponibile l'applicazione o il sistema ai suoi utenti.
-
-I target possono avere dipendenze da altri target.
-I target contengono task che fanno effettivamente il lavoro; si possono aggiungere nuovi tipi di task definendo nuove classi Java.
-
-Esempio di un build file:
-
-```xml
-<?xml version="1.0"?>
-<project name="Hello" default="compile">
-  <target name="clean" description="remove intermediate files">
-    <delete dir="classes" />
-  </target>
-  <target name="clobber" depends="clean" description="remove all artifact files">
-    <delete file="hello.jar">
-  </target>
-  <target name="compile" description="compile the Java source code to class files">
-    <mkdir dir="classes" />
-    <javac srcdir="." destdir="classes" />
-  </target>
-  <target name="jar" depends="compile" description="create a Jar file for the application">
-    <jar destfile="hello.jar">
-      <fileset dir="classes" includes="**/*.class" />
-      <manifest>
-        <attribute name="Main-Class" value="HelloProgram" />
-      </manifest>
-    </jar>
-  </target>
-</project>
-```
-
-### Gradle
-
-Gradle è uno strumento di build automation che utilizza le repository Maven come punto di accesso alle librerie di terze parti. 
-Maven è una piattaforma di gestione delle dipendenze e della build automation per il linguaggio di programmazione Java. 
-Le repository Maven sono archivi online che contengono librerie Java, plugin e altri componenti utilizzati nella build di progetti Java. 
-Gradle utilizza queste repository per cercare e scaricare le librerie di cui ha bisogno per eseguire la build del progetto.
-
-Gradle, che supporta Groovy o Kotlin come linguaggi di scripting, adotta un approccio dichiarativo e fortemente basato su convenzioni. 
-Ciò significa che tutto ciò che è già stato definito come standard non deve essere ridichiarato. 
-Inoltre, Gradle definisce un linguaggio specifico per la gestione delle dipendenze e permette di creare build multi-progetto.
-
-Gradle scala bene in complessità: permette di fare cose semplici senza usare le funzioni complesse. 
-È estendibile tramite plugin che servono per trattare tool, situazioni, linguaggi legati solitamente al mondo Java.
-
-#### Plugin
-
-I plugin servono per trattare tool, situazioni, linguaggi definendo task e regole per lavorare più facilmente.
-
-Il plugin _Java_ definisce:
-- una serie di __sourceSet__, ovvero dove è presente il codice e le risorse. Principalmente sono:
-  - `src/main/java`: sorgenti Java di produzione;
-  - `src/main/resources`: risorse di produzione;
-  - `src/test/java`: sorgenti Java di test;
-  - `src/test/resources`: risorse di test.
-- dei __task__, anche con dipendenze tra loro.
-
-{% responsive_image path: 'assets/06_gradle-tasks.png' %}
-
-#### Altri plugin
-- application, per l'esecuzione;
-- FindBugs, jacoco: per la verifica e la convalida;
-- eclipse, idea: per integrazione con gli IDE;
-
-## Bug tracking
-
-Il bug tracking è stato reso necessario nel mondo open source per via della numerosità dei contributi e della alta probabilità di avere segnalazioni duplicate.
-
-Inoltre, per gestire le segnalazioni di bug nell'ambito dello sviluppo open source, esistono diversi strumenti come git-bug, BugZilla, Scarab, GNATS, BugManager e Mantis.
-
-### Bug workflow
-
-{% responsive_image path: 'assets/06_bug-workflow.png' %}
-
-L'obiettivo del bug tracking è avere più informazioni possibili su ogni bug per saperli riprodurre e quindi arrivare a una soluzione.
-
-È importante verificare i bug una volta che l'_issue_ è stato aperto, in modo da poter confermare la sua esistenza e la completezza delle informazioni fornite.
-
-Un _issue_ è un problema o una richiesta di funzionalità segnalata all'interno di un progetto di software. 
-Gli issue vengono solitamente utilizzati per tenere traccia dei problemi noti o delle richieste di nuove funzionalità all'interno di un progetto, e possono essere gestiti attraverso un sistema di bug tracking o gestione delle richieste. 
-Gli issue possono essere aperti da qualsiasi membro del team o dalla comunità, e possono essere risolti o chiusi da un membro del team responsabile.
-
-Ci sono diversi modi per cui può essere chiuso un bug:
-- __duplicate__: quando è stato già segnalato in precedenza e quindi non rappresenta un problema nuovo. In questo caso, viene solitamente fatto riferimento al numero del bug originale che ha già ricevuto una risoluzione;
-- __wontfix__: il bug viene chiuso come "non risolvibile" perché o rappresenta una funzionalità voluta dal progetto o è troppo complesso da risolvere per essere considerato conveniente farlo dal punto di vista dei progettisti;
-- __can't reproduce__: non è stato possibile riprodurre il bug, ovvero che non è stato possibile ottenere lo stesso risultato o il comportamento segnalato dal bug. Ciò può essere dovuto a una mancanza di dettagli o a un errore nella segnalazione del bug stesso;
-- __fixed__: il bug è stato fixato;
-vs __fix verified__: il fix è stato integrato in una release passando tutti gli step di verifica.
diff --git a/_posts/2022-10-24-Progettazione.md b/_posts/2022-10-24-Progettazione.md
deleted file mode 100644
index 75847d3c25757f82ebaf3f15a608343b98ece682..0000000000000000000000000000000000000000
--- a/_posts/2022-10-24-Progettazione.md
+++ /dev/null
@@ -1,555 +0,0 @@
----
-layout: post
-title: "[07] Progettazione"
-date:   2022-10-24 14:30:00 +0200
-toc: true
----
-
-# Progettazione
-
-## Introduzione
-
-Durante le lezioni, per discutere di progettazione siamo partiti da un esempio di programma in C che stampa una canzone.
-Il [codice considerato](https://paste.studentiunimi.it/paste/JS+VOUKO#Rj3CMN-TmbNu3zeTleTTM0JdjjROWeVySXTgzG6aWKY) è talmente illeggibile che Jekyll rifiuta di compilare se lo si prova ad includere in un file Markdown.
-
-Successivamente abbiamo scomposto il codice per renderlo logicamente più sensato e facilmente modificabile, sono state __estratte le parti comuni__ e spostate in una funzione apposita, mentre le __parti mutabili sono state salvate in alcune strutture dati__; la canzone viene così stampata tramite un ciclo. 
-In questo modo scrivendo un codice più semplice siamo stati in grado di creare una soluzione più generale e più aperta ai cambiamenti.
-
-```java
-public class TwelveDaysOfChristmas {
-    static String[] days = {"first", "second", ..., "twelfth"};
-    static String[] gifts = { "a partdrige in a pear tree", "two turtle doves", ... };
-
-    static String firstLine(int day) {
-        return "On the " + days[day] +
-               " day of Christmas my true love gave to me:\n";
-    }
-
-    static String allGifts(int day) {
-        if (day == 0) {
-            return "and " + gifts[0];
-        } else {
-            return gifts[day] + "\n" + allGifts(day-1);
-        }
-    }
-
-    public static void main(String[] args) {
-        System.out.println(firstLine(0));
-        System.out.println(gifts[0]);
-        for (int day == 1; day < 12; day++) {
-            System.out.println(firstLine(day));
-            System.out.println(allGifts(day));
-        }
-    }
-}
-```
-
-È importante quindi __adottare la soluzione più semplice__ (che __non è quella più stupida__!) e una misura convenzionale per dire quanto una cosa è semplice - almeno in Università - si esprime in termini del tempo dedicato dal programmatore all'implementazione.
-Tale misura si sposa bene con il __TDD__, che richiede __brevi iterazioni__ di circa 10 minuti: se la feature attuale richiede più tempo è opportuno ridurre la portata scomponendo il problema.
-
-## Refactoring
-Durante il refactoring è opportuno rispettare le seguenti regole:
-- le __modifiche al codice non devono modificare le funzionalità__:
-il refactoring DEVE essere invisibile al cliente;
-- __non possono essere aggiunti test aggiuntivi__ rispetto alla fase verde appena raggiunta.
-
-Se la fase di refactoring sta richiedendo troppo tempo allora è possibile fare rollback all'ultima versione verde e __pianificare meglio__ l'attività di refactoring, per esempio scomponendolo in più step.
-Vale la regola del _"do it twice"_: il secondo approccio a un problema è solitamente più veloce e migliore.
-
-### Motivazioni
-
-Spesso le motivazioni dietro un refactoring sono:
-- precedente __design molto complesso e poco leggibile__, a causa della velocità del passare ad uno _scenario verde_;
-- __preparare il design di una funzionalità__ che non si integra bene in quello esistente; dopo aver raggiunto uno _scenario verde_ in una feature, è possibile che la feature successiva sia difficile da integrare. 
-In questo caso, se il _refactoring_ non è banale è bene fermarsi, tornare indietro e evolvere il codice per facilitare l'iterazione successiva (__design for change__). 
-- presenza di __debito tecnico__ su lavoro fatto in precendenza, ovvero debolezze e "scorciatoie" che ostacolano notevolmente evoluzioni future: _"ogni debito tecnico lo si ripaga con gli interessi"_.
-
-## Design knowledge
-
-La design knowledge è la __conoscenza del design__ architetturale di un progetto. 
-È possibile utilizzare:
-- la __memoria__: non è efficace perché nel tempo si erode, specialmente in coppia;
-- i __documenti di design__ (linguaggio naturale o diagrammi): se non viene aggiornato di pari passo con il codice rimane disallineato, risultando più dannoso che d'aiuto.
-- le __piattaforme di discussione__ (version control, issue management): possono aiutare ma le informazioni sono sparse in luoghi diversi e di conseguenza difficili da reperire e rimane il problema di mantenere aggiornate queste informazioni.
-- gli __UML__: tramite diagrammi UML si è cercato di sfruttare l'approccio ___generative programming___, ovvero la generazione automatica del codice a partire da specificazioni di diagrammi.
-Con l'esperienza si è visto che non funziona.
-- il __codice__ stesso: tramite la lettura del codice è possibile capire il design ma è difficile rappresentare le ragioni della scelta.
-
-È bene sfruttare tutte le tecniche sopra proposte __combinandole__, partendo dal codice. \
-È inoltre importante scrivere documentazione per spiegare le ragioni dietro le scelte effettuate e non le scelte in sé, che si possono dedurre dal codice.
-
-### Condivisione
-
-Per condividere tali scelte di design (il _know how_) è possibile sfruttare:
-- __metodi__: con pratiche (come Agile) o addirittura l'object orientation stessa, che può essere un metodo astratto per condividere scelte di design.
-- __design pattern__: fondamentali per condividere scelte di design, sono utili anche per generare un vocabolario comune (sfruttiamo dei nomi riconosciuti da tutti per descrivere i ruoli dei componenti) e aiutano l'implementazione (i pattern hanno delle metodologie note per essere implementati). 
-I pattern non si concentrano sulle prestazioni di un particolare sistema ma sulla generalità e la riusabilità di soluzioni a problemi comuni;
-- __principi__: per esempio i principi SOLID.
-
-## Conoscenze preliminari di concetti e termini
-
-Prima di proseguire è bene richiamare concetti e termini fondamentali presumibilmente visti durante il corso di Programmazione II.
-
-### Object orientation
-
-Per essere definito _object oriented_, un linguaggio di programmazione deve soddisfare tre proprietà:
-- __ereditarietà__: ovvero la possibilità di poter definire una classe ereditando proprietà e comportamenti di un'altra classe.
-- __polimorfismo__: quando una classe può assumere diverse forme in base alle interfacce che implementa. 
-Il prof fa l'esempio del _tennista scacchista_: in un torneo di tennis è poco utile sostituire una persona che gioca a tennis ed è brava con gli scacchi (quindi una classe che implementa entrambe le interfacce) con una che gioca a scacchi.
-Il collegamento tra capacità e oggetto è fatto __a tempo di compilazione__: non è importante quindi se la capacità non è ancora definita;
-- __collegamento dinamico__: in Java il tipo concreto degli oggetti e quindi il problema di stabilire _quale metodo chiamare_ viene risolto durante l'esecuzione. 
-In C++ occorre esplicitare questo comportamento utilizzando la keyword `virtual`.
-
-### <a style="color: darkgreen">SOLID</a> principles
-
-Ci sono 5 parti che compongono questo principio:
-1. __<span style="color: darkgreen"><big>S</big></span>INGLE RESPONSIBILITY__: una classe, un solo scopo.
-Così facendo, le classi rimangono semplici e si agevola la riusabilità.
-2. __<span style="color: darkgreen"><big>O</big></span>PEN-CLOSE PRINCIPLE__:
-le classi devono essere aperte ai cambiamenti (_opened_) ma senza modificare le parti già consegnate e in produzione (_closed_).
-Il refactoring è comunque possibile, ma deve essere preferibile estendere la classe attuale.
-3. __<span style="color: darkgreen"><big>L</big></span>ISKOV SUBSTITUTION PRINCIPLE__:
-c'è la garanzia che le caratteristiche eredidate dalla classe padre continuinino ad esistere nelle classi figlie.
-Questo concetto si collega all'aspetto __contract-based__ del metodo Agile: le _precondizioni_ di un metodo di una classe figlia devono essere ugualmente o meno restrittive del metodo della classe padre.
-Al contrario, le _postcondizioni_ di un metodo della classe figlia non possono garantire più di quello che garantiva il metodo nella classe padre.
-Fare _casting_ bypassa queste regole.
-4. __<span style="color: darkgreen"><big>I</big></span>NTERFACE SEGREGATION__:
-più le capacità e competenze di una classe sono frammentate in tante interfacce più è facile utilizzarla in contesti differenti.
-In questo modo un client non dipende da metodi che non usa. 
-Meglio quindi avere __tante interfacce specifiche__ e piccole (composte da pochi metodi), piuttosto che poche, grandi e generali. 
-5. __<span style="color: darkgreen"><big>D</big></span>EPENDENCY INVERSION__:
-il codice dal quale una classe dipende non deve essere più __concreto__ di tale classe.
-Per esempio, se il _telaio della FIAT 500_ dipende da uno specifico motore, è possibile utilizzarlo solo per quel specifico motore.
-Se invece il telaio dipende da _un_ concetto di motore, non c'è questa limitazione.
-In conlusione, le classi concrete devono tendenzialmente dipendere da classi astratte e non da altre classi concrete.
-
-### Reference escaping
-
-Il _reference escaping_ è una violazione dell'incapsulamento. 
-
-Può capitare, per esempio: 
-- quando un getter ritorna un riferimento a un segreto;
-```java
-public Deck {
-    private List<Card> cards;
-        
-    public List<Card> getCards() {
-        return this.cards;
-    }
-}
-```
-- quando un setter assegna a un segreto un riferimento che gli viene passato;
-```java
-public Deck {
-    private List<Card> cards;
-
-    public setCards(List<Card> cards) {
-        this.cards = cards;
-    }
-}
-```
-
-- quando il costruttore assegna al segreto un riferimento che gli viene passato;
-```java
-public Deck {
-    private List<Card> cards;
-
-    public Deck(List<Card> cards) {
-        this.cards = cards;
-    }
-}
-```
-
-### Encapsulation e information hiding
-
-__Legge di Parnas (L8)__.
-> Solo ciò che è nascosto può essere cambiato liberamente e senza pericoli.
-
-Lo stato mostrato all'esterno non può essere modificato, mentre quello nascosto sì.
-
-Questo principio serve per __facilitare la comprensione del codice__ e renderne più facile la modifica parziale senza fare danni.
-
-### Immutabilità
-
-Una classe è immutabile quando non c'è modo di modificare lo stato di ogni suo oggetto dopo la creazione.
-
-Per assicurare tale proprietà è necessario:
-- __non fornire metodi di modifica__ allo stato;
-- avere tutti gli __attributi privati__ per i tipi potenzialmente mutabili (come `List<T>`);
-- avere tutti gli __attributi final__ se non già privati;
-- assicurare l'__accesso esclusivo__ a tutte le parti non mutabili, ovvero non avere reference escaping.
-
-## Code smell
-
-I _code smell_ sono dei segnali che suggeriscono problemi nella progettazione del codice. 
-Di seguito ne sono elencati alcuni:
-- __codice duplicato__: si può fare per arrivare velocemente al verde ma è da togliere con il refactoring. 
-Le parti di codice in comune possono quindi essere fattorizzate.
-- __metodi troppo lunghi__: sono poco leggibili e poco riusabili;
-- __troppi livelli di indentazione__: scarsa leggibilità e riusabilità, è bene fattorizzare il codice;
-- __troppi attributi__: suggerisce che la classe non rispetta la single responsability, ovvero fa troppe cose;
-- __lunghe sequenze di _if-else_ o _switch___;
-- __classe troppo grande__;
-- __lista parametri troppo lunga__;
-- __numeri _magici___: è importante assegnare alle costanti numeriche all'interno del codice un nome per comprendere meglio il loro scopo;
-- __commenti che spiegano cosa fa il codice__: indicazione che il codice non è abbastanza chiaro;
-- __nomi oscuri o inconsistenti__;
-- __codice morto__: nel programma non deve essere presente del codice irraggiungibile o commentato. Utilizzando strumenti di versioning è possibile riaccedere a codice precedentemente scritto con facilità.
-- __getter e setter__: vedi principio di __tell don't ask__.
-
-## <a href="https://martinfowler.com/bliki/TellDontAsk.html">Principio Tell-Don't-Ask</a>
-
-{% responsive_image path: 'assets/07_tell-dont-ask.png' %}
-
-> Non chiedere i dati, ma dì cosa vuoi che si faccia sui dati
-
-Il responsabile di un'informazione è anche responsabile di tutte le operazioni su quell'informazione.
-
-Il principio _Tell-Don't-Ask_ sancisce che piuttosto di __chiedere__ ad un oggetto dei dati e fare delle operazioni con quei dati è meglio __dire__ a questo oggetto cosa fare con i dati che contiene. 
-
-#### Esempio
-
-Se desideriamo stampare il contenuto di tutte le carte in un mazzo possiamo partire da questo codice.
-
-```java
-class Main {
-    public static void main(String[] args) {
-        Deck deck = new Deck();
-        Card card = new Card();
-
-        card.setSuit(Suit.DIAMONDS);
-        card.setRank(Rank.THREE);
-        deck.getCards().add(card);
-        deck.getCards().add(new Card());    // <-- !!!
-
-        System.out.println("There are " + deck.getCards().size() + " cards:");
-        for (Card currentCard : deck.getCards()) {
-            System.out.println(
-                currentCard.getRank() + 
-                " of " + 
-                currentCard.getSuit()
-            );
-        }
-    }
-}
-```
-
-All'interno del ciclo reperiamo gli attributi della carta e li utilizziamo per stampare le sue informazioni.
-Inoltre, nella riga evidenziata viene aggiunta una carta senza settare i suoi attributi. 
-La responsabilità della gestione dell'informazione della carta è quindi __erroneamente delegata__ alla classe chiamante.
-
-Per risolvere, è possibile trasformare la classe `Card` nel seguente modo:
-
-```java
-class Card {
-    private Suit suit;
-    private Rank rank;
-
-    public Card(@NotNull Suit s, @NotNull Rank r) {
-        suit = s;
-        rank = r;
-    }
-
-    @Override
-    public String toString() {
-        return rank + " of " + suit;
-    }
-}
-```
-
-l'informazione viene ora interamente gestita dalla classe `Card`, che la gestisce nel metodo `toString()` per ritornare la sua rappresentazione testuale.
-
-## Interface segregation
-
-Le interfacce possono _"nascere"_ tramite due approcci: 
-- __up front__: scrivere direttamente l'interfaccia;
-- __down front__: scrivere il codice e quindi tentare di estrarne un'interfaccia.
-
-L'approccio down-front si adatta meglio al TDD ed è illustrato nel seguente esempio.
-
-## Esempio con gerarchia Card / Deck
-
-In questo esempio sono trattati numerosi principi, come l'_interface segreagation_, _linking dinamico/statico_, _implementazione di interfacce multiple_ e il _contract based design_ vs la _programmazione difensiva_.
-
-### Interface segregation all'opera
-
-```java
-public static List<Card> drawCards(Deck deck, int number) {
-    List<Card> result = new ArrayList<>();
-    for (int i = 0; i < number && !deck.isEmpty(); i++) {
-        result.add(deck.draw());
-    }
-    return result;
-}
-```
-
-Consideriamo il metodo `drawCards` che prende come parametri un `Deck` e un intero. \\
-Le __uniche competenze__ riconosciute a `Deck` sono l'indicazione _se è vuoto_ (`isEmpty()`) e il _pescare una carta_ dal mazzo (`draw()`).
-`Deck` può quindi __implementare un'interfaccia__ che mette a disposizione queste capacità.
-
-È possibile modificare il metodo in modo da accettare un qualunque oggetto in grado di eseguire le operazioni sopra elencate, ovvero che implementi l'interfaccia __`CardSource`__. 
-
-<a id="cardsource" style="display:none;"></a>
-
-```java
-public interface CardSource {
-    /**
-     * @return The next available card.
-     * @pre !isEmpty()
-     */
-    Card draw();
-
-    /**
-     * @return True if there is no card in the source
-     */
-    boolean isEmpty();
-}
-```
-
-```java
-public class Deck implements CardSource { ... }
-```
-
-```java
-public static List<Card> drawCards(CardSource deck, int number) {
-    List<Card> result = new ArrayList<>();
-    for (int i = 0; i < number && !deck.isEmpty(); i++) {
-        result.add(deck.draw());
-    }
-    return result;
-}
-```
-
-### Collegamento statico e dinamico
-
-Notare come è necessario specificare __staticamente__ che `Deck` implementi `CardSource`, ovvero occorre forzare la dichiarazione del fatto che `Deck` sia un _sottotipo_ di `CardSource` (Java è strong typed) e quindi sia possibile mettere un oggetto `Deck` ovunque sia richiesto un oggetto `CardSource`. \\
-In altri linguaggi come __Go__ c'è una __maggiore dinamicità__ perché non c'è bisogno di specificare nel codice che un oggetto è sottotipo di qualcos'altro, è sufficiente solo che implementi un metodo con la stessa signature.
-Il controllo che l'oggetto passato ad una funzione abbia le capacità necessarie avviene a runtime e non prima.
-
-Un problema della troppa dinamicità (__duck typing__) è che se i metodi di un oggetto non hanno dei nomi abbastanza specifici si possono avere dei problemi. 
-Per esempio, in un programma per il gioco del tennis se una funzione richiede un oggetto che implementa il metodo `play()`, e riceve in input un oggetto che non c'entra nulla con il tennis (per esempio un oggetto di tipo `GiocatoreDiScacchi`) che ha il metodo `play()`, si possono avere degli effetti indesiderati.
-
-### _Loose coupling_
-Il _loose coupling_ è la capacità di una variabile o un parametro di accettare l'assegnamento di oggetti aventi tipo diverso da quello della variabile o parametro, a patto che sia un sottotipo.
-
-```java
-Deck deck = new Deck();
-CardSource source = deck;
-List<Card> cards = drawCards(deck, 5);
-```
-
-### Interfacce multiple
-
-Tornando all'esempio, la classe `Deck` (che implementa `CardSource`) __può implementare anche altre interfacce__, come `Shuffable` o `Iterable<Card>`. 
-Al metodo precedente interessa solo che Deck abbia le capacità specificate in `CardSource`, se poi implementa anche altre interfaccie è ininfluente.
-
-{% plantuml style="width:100%" %}
-
-class Client1
-class Client2
-class Client3
-
-interface Iterable<Card> << interface >> {
-    + {abstract} Iterator<Card> iterator()
-}
-
-class Deck implements Iterable, Shuffable, CardSource {
-    + void shuffle()
-    + Card draw()
-    + boolean isEmpty()
-    + Iterator<Card> iterator()
-}
-
-interface Shuffable << interface >> {
-    + {abstract} void shuffle()
-}
-
-interface CardSource << interface >> {
-    + {abstract} Card draw()
-    + {abstract} boolean isEmpty()
-}
-
-Client2 ..> Iterable
-Client1 ..> Shuffable
-Client3 ..> Iterable
-Client3 ..> CardSource
-
-hide empty fields
-hide empty methods
-
-{% endplantuml %}
-
-### _Contract-based_ design vs programmazione difensiva
-
-Tornando alla <a href="#cardsource">specificazione</a> dell'interfaccia di `CardSource`, è possibile notare dei commenti in formato Javadoc che specificano le __precondizioni__ e le __postcondizioni__ (il valore di ritorno) del metodo. Secondo il ___contract-based_ design__, esiste un _"contratto"_ tra chi implementa un metodo e chi lo chiama.
-
-Per esempio, considerando il metodo `draw()`, __è responsabilità del chiamante__ verificare il soddisfacimento delle precondizioni (_"il mazzo non è vuoto"_) prima di invocare il metodo.
-Se `draw()` viene chiamato quando il mazzo è vuoto ci troviamo in una situazione di __violazione di contratto__ e può anche esplodere la centrale nucleare.
-
-Per specificare il contratto si possono utilizzare delle __asserzioni__ o il `@pre` nei __commenti__. 
-Le prime sono particolarmenti utili in fase di sviluppo perché interrompono l'esecuzione del programma in caso di violazione, ma vengono solitamente rimosse in favore delle seconde nella fase di deployment.
-
-Un'altro approccio è la __programmazione difensiva__ che al contrario delega la responsabilità del soddisfacimento delle precondizioni al _chiamato_, e non al chiamante.
-
-### Classi astratte
-
-Una classe astratta che implementa un'interfaccia __non deve necessariamente implementarne__ tutti i metodi, ma può delegarne l'implementazione alle sottoclassi impedendo l'istanziamento di oggetti del suo tipo.
-
-Le interfacce diminuiscono leggermente le performance, però migliorano estremamente la generalità (che aiutano l'espandibilità ed evolvibilità del programma), quindi vale la pena di utilizzarle.
-
-È possibile utilizzare le __classi astratte__ anche per classi complete, ma che __non ha senso che siano istanziate__.
-Un buon esempio sono le classi _utility_ della libreria standard di Java.
-
-#### Classe utility della libreria standard di Java
-
-Un esempio è __`Collections.shuffle(List<?> list)`__ che accetta una lista omogenea di elementi e la mischia.
-Il _tipo_ degli elementi è volutamente ignorato in quanto non è necessario conoscerlo per mischiarli.
-
-Per l'__ordinamento__, invece, è necessario conoscere il tipo degli oggetti in quanto bisogna confrontarli tra loro per poterli ordinare.
-La responsabilità della comparazione è però delegata all'oggetto, che deve aderire all'interfaccia `Comparable<T>`.
-
-__`Collections.sort(...)`__ ha, infatti, la seguente signature:
-```java
-public static <T extends Comparable<? super T>> void sort(List<T> list)
-```
-
-La notazione di generico __aggiunge dei vincoli__ su `T`, ovvero il tipo degli elementi contenuti nella lista:
-- `T extends Comparable<...>` significa che `T` deve estendere - e quindi implementare - l'interfaccia `Comparable<...>`;
-- `Comparable<? super T>` significa che tale interfaccia può essere implementata su un antenato di `T` (o anche `T` stesso).
-
-`Comparable` è un altro esempio di _interface segregation_: serve per specificare che un oggetto ha bisogno della caratteristica di essere comparabile.
-
-__Digressione__: la classe Collections era l'unico modo per definire dei metodi sulle interfacce (es: dare la possibilità di avere dei metodi sulle collezioni, ovvero liste, mappe, ecc), ma ora si possono utilizzare i metodi di default.
-
-# Analisi del testo naturale
-
-Come organizzare la partenza del design suddividendo in classi e responsabilità?
-
-I due approcci principali sono:
-- __pattern__: riconoscere una situazione comune da una data;
-- __TDD__: partendo dalla soluzione più semplice si definiscono classi solo all'occorrenza.
-
-Un'altra tecnica che vedremo è l'__estrazione dei nomi__ (noun extraction), per un certo senso _naive_ ma adatta in caso di storie complesse.
-
-## Noun extraction
-
-Basandosi sulle specifiche - come i commenti esplicativi delle User Stories - si parte dai sostantivi (o frasi sostantivizzate), si _sfolsticono_ con dei criteri, si cercano le relazioni tra loro e quindi si produce la gerarchia delle classi.
-
-Per spiegare il procedimento considereremo il seguente esempio:
-
-> - The __library__ contains __books__ and __journals__.
-> It may have several __copies__ of a given book. 
-> Some of the books are for __short term loans__ only.
-> All other books may be borrowed by any __library member__ for three __weeks__.
-> - __Memebers of the library__ can normally borrow up to six __items__ at a time, but __members of staff__ may borrow up to 12 items at one time.
-> Only member of staff may borrow journals.
-> - The __system__ must keep track of when books and journals are borrowed and returned, enforcing the __rules__ described above.
-
-Nell'esempio sopra sono stati evidenziati i sostantivi e le frasi sostantivizzate.
-
-### Criteri di _sfoltimento_
-
-I criteri di _sfoltimento_ servono per diminuire il numero di sostantivi considerando solo quelli rilevanti per risolvere il problema.
-In questa fase, in caso di dubbi è possibile rimandare la decisione a un momento successivo.
-
-Di seguito ne sono riportati alcuni:
-- __Ridondanza__: sinonimi, termini diversi per indicare lo stesso concetto. Anche se è stata utilizzata una locuzione diversa potrebbe essere comunque ridondante, sopratutto in lingue diverse dall'inglese in cui ci sono molti sinonimi. \\
-Nell'esempio: _library member_ e _member of the library_, _loan_ e _short term loan_.
-- __Vaghezza__: nomi generici, comuni a qualunque specifica; potrebbero essere sintomo di una _classe comune astratta_. \\
-Nell'esempio: _items_.
-- __Nomi di eventi e operazioni__: nomi che indicano azioni e non hanno un concetto di _stato_. \\
-Nell'esempio: _loan_.
-- __Metalinguaggio__: parti _statiche_ che fanno parte della logica del programma e che quindi non necessitano di essere rappresentati come classi. \\
-Nell'esempio: _system_, _rules_.
-- __Esterne al sistema__: concetti esterni o verità _"assolute"_ al di fuori del controllo del programma. \\
-Esempio: _library_, _week_ (una settimana ha 7 giorni).
-- __Attributi__: informazioni atomiche e primitive (stringhe, interi, ...) relative a una classe, che quindi non necessitano la creazione di una classe di per sé. \\
-Esempio: _name of the member_ (se ci fosse stato).
-
-Al termine di questa fase, si avrà una lista di classi _"certe"_ e _"incerte"_.
-In questo esempio, sono soppravvisuti i termini _journal_, _book_, _copy_ (of _book_), _library member_ e _member of staff_. 
-
-### Relazioni tra classi
-
-Il prossimo passo è definire le relazioni tra le classi.
-
-Inizialmente, si collegano con delle _linee_ (non frecce) senza specificare la direzione dell'associazione.
-Parliamo di __associazioni__ e non __attributi__ perché non è necessariamente vero che tutte le associazioni si trasformino in attributi.
-
-{% plantuml style="width: 90%" %}
-class Book
-class CopyOfBook
-class LibraryMember
-class StaffMember
-class Journal
-
-Journal -- StaffMember : borrows/returns
-Book -- CopyOfBook : is a copy
-CopyOfBook -- LibraryMember : borrows/returns
-CopyOfBook -- StaffMember : borrows/returns
-
-hide empty fields
-hide empty methods
-{% endplantuml %}
-
-Il prossimo passo è specificare le __cardinalità__ delle relazioni, come specificato dal linguaggio UML (opposto in questo aspetto al diagramma ER).
-La precisione richiesta in questo punto è soggettiva: da una parte, specificare puntualmente il numero massimo di elementi di una associazione può aiutare ad ottimizzare successivamente, dall'altra porta confusione.
-
-{% plantuml style="width: 90%" %}
-class Book
-class CopyOfBook
-class LibraryMember
-class StaffMember
-class Journal
-
-Journal "0..*" -- "0..1" StaffMember : borrows/returns
-Book "1" -- "1..*" CopyOfBook : is a copy
-CopyOfBook "0..*" -- "0..1" LibraryMember : borrows/returns
-CopyOfBook "0..*" -- "0..1" StaffMember : borrows/returns
-
-hide empty fields
-hide empty methods
-{% endplantuml %}
-
-Dopo aver ragionato sulle cardinalità, si iniziano a cercare __generalizzazioni__ e fattorizzazioni. 
-In questo caso, notiamo che:
-- `StaffMember` _è un_ `LibraryMember` con in più la possibilità di prendere `Journal`. 
-Inoltre, un altro indicatore è che hanno la stesso tipo di relazioni con gli altri oggetti.
-- `Items` è un termine generico per indicare `CopyOfBook` e `Journal`.
-
-{% plantuml style="width: 90%" %}
-class BorrowableItem
-class LibraryMember
-class Book
-class CopyOfBook extends BorrowableItem
-class StaffMember extends LibraryMember
-class Journal extends BorrowableItem
-
-Book "1" -- "1..*" CopyOfBook : is a copy
-BorrowableItem "0..*" -- "0..1" LibraryMember : borrows/returns
-
-hide empty fields
-hide empty methods
-{% endplantuml %}
-
-Distinguere `CopyOfBook` e `Journal` è inutile, perché di fatto un `Journal` _è una copia di_ un giornale.
-Si può quindi fattorizzare __rimuovendo la generalizzazione__, come mostrato di seguito.
-
-{% plantuml style="width: 90%" %}
-class Book
-class BorrowableItem
-class LibraryMember
-class StaffMember extends LibraryMember
-class Journal
-
-Book "0..1" - "1..*" BorrowableItem : is a copy
-BorrowableItem "0..*" -- "0..1" LibraryMember : borrows/returns
-BorrowableItem "1..*" - "0..1" Journal : is a copy
-
-hide empty fields
-hide empty methods
-{% endplantuml %}
-
-È imporante però preoccuparsi delle __cardinalità__ delle relazioni: è sì vero che un `BorrowableItem` può non _essere una copia di_ un `Book` e di un `Journal`, ma deve essere copia di _esattamente_ una delle due opzioni.
-UML prevede un __linguaggio OCL__ ([Object Constraint Language](https://en.wikipedia.org/wiki/Object_Constraint_Language)) per esprimere vincoli divesamente impossibili da esprimere in un diagramma.
-È anche possibile scrivere il _constraint_ in linguaggio naturale come __nota__.
diff --git a/_posts/2022-10-26-Patterns.md b/_posts/2022-10-26-Patterns.md
deleted file mode 100644
index d88751b9bcbb28c5964f9bde26caedab0ddd48ff..0000000000000000000000000000000000000000
--- a/_posts/2022-10-26-Patterns.md
+++ /dev/null
@@ -1,1337 +0,0 @@
----
-layout: post
-title: "[09] Patterns"
-date:   2022-10-26 14:30:00 +0200
-toc: true
----
-
-# [Patterns](https://refactoring.guru/)
-
-Parlando di progettazione del software e di buone pratiche è impossibile non parlare di __design patterns__, soluzioni universalmente riconosciute valide a problemi di design ricorrenti: si tratta cioè di strumenti concettuali di progettazione che esprimono un'architettura vincente del software catturando la soluzione ad una famiglia di problemi.
-
-Ad ogni pattern sono associati una serie di __idiomi__, implementazioni del pattern specifiche per un certo linguaggio di programmazione che sfruttano i costrutti del linguaggio per realizzare l'architettura dettata dal pattern.
-Durante questa discussione vedremo alcuni idiomi per Java, che talvolta si discosteranno fortemente dalla struttura descritta dai diagrammi UML dei pattern.
-
-Ma attenzione, esistono anche degli __anti-pattern__, soluzioni che _sembrano_ buone ma sono dimostratamente problematiche: dovremo assicurarci di tenerci lontani da questi design truffaldini!
-
-#### Discutere di pattern: i meta-pattern
-
-Prima di iniziare a parlare dei principali pattern che un informatico dovrebbe conoscere, possiamo chiederci come possiamo parlare di pattern: semplice, con dei _meta-patterns_, pattern con cui costruire altri pattern!
-
-Nello specifico, i meta-patterns identificano due elementi base su cui ragionare quando si trattano i pattern:
-
-- __HookMethod__: un "metodo astratto" che, implementato, determina il comportamento specifico nelle sottoclassi; è il _punto caldo_ su cui interveniamo per adattare lo schema alla situazione.
-
-- __TemplateMethod__: metodo che coordina generalmente più HookMethod per realizzare il design voluto; è l'_elemento freddo_ di invariabilità del pattern che ne realizza la rigida struttura.
-
-Ovviamente i metodi _template_ devono avere un modo per accedere ai metodi _hook_ se intendono utilizzarli per realizzare i pattern.
-Tale collegamento può essere fatto in tre modi differenti:
-
-- __Unification__: _hook_ e _template_ si trovano nella stessa classe astratta, classe da cui erediteranno le classi concrete per implementare i metodi _hook_ e, di conseguenza, il pattern; i metodi _template_ sono invece già implementati in quanto la loro struttura non si deve adattare alla specifica applicazione.
-
-{% plantuml style="width: 30%" %}
-class TemplateHookClass {
-  {abstract} hookMethod()
-  templateMethod()
-}
-{% endplantuml %}
-
-- __Connection__: _hook_ e _template_ sono in classi separate, indicate rispettivamente come _hook class_ (astratta) e _template class_ (concreta), collegate tra di loro da un'aggregazione: la classe template contiene cioè un'istanza della classe hook, in realtà un'istanza della classe concreta che realizza i metodi hook usati per implementare il pattern.
-
-{% plantuml style="width: 25%" %}
-class TemplateClass {
-  templateMethod()
-}
-class HookClass {
-  {abstract} hookMethod()
-}
-TemplateClass o--> HookClass 
-{% endplantuml %}
-
-- __Recursive connection__: come nel caso precedente _hook_ e _template_ sono in classi separate, ma oltre all'aggregazione tali classi sono qui legate anche da una relazione di generalizzazione: la classe template dipende infatti dalla classe hook.
-
-{% plantuml style="width: 30%" %}
-class TemplateClass {
-  templateHookMethod()
-}
-class HookClass {
-  {abstract} templateHookMethod()
-}
-TemplateClass --|> HookClass
-TemplateClass o--> HookClass
-{% endplantuml %}
-
-Vedremo a quale meta-pattern aderiranno i pattern che vediamo. 
-A tal proposito,i pattern che vedremo fanno parte dei cosiddetti "__Gang Of Four patterns__", una serie di 23 pattern definiti da Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides; oltre ad averli definiti, questi signori hanno diviso i pattern in tre categorie:
-
-- __Creazionali__: legati alla creazione di oggetti
-- __Comportamentali__: legati all'interazione tra oggetti
-- __Strutturali__: legati alla composizioni di classi e oggetti
-
-## <big>S</big>INGLETON
-
-Talvolta vorremmo che di un certo oggetto esistesse __una sola istanza__ perché logicamente di tale oggetto non ha senso esistano diverse copie all'interno dell'applicazione (es. diverse istanze della classe Gioco in un sistema che gestisce un solo gioco alla volta).
-Tuttavia i linguaggi Object-Oriented gestiscono solo classi con istanze multiple, per cui la realizzazione di questa unicità può rivelarsi più complessa del previsto.
-
-La soluzione consiste nel rendere la classe stessa responsabile del fatto che non può esistere più di una sua istanza: per fare ciò il primo passo è ovviamente quello di _rendere privato il costruttore_, o se non privato comunque non pubblico (conviene metterlo protected in modo da poter creare sottotipi). \
-Bisogna però garantire comunque un modo per recuperare l'unica istanza disponibile della classe: si crea dunque il _metodo statico_ `getInstance` che restituisce a chi lo chiama l'unica istanza della classe, creandola tramite il costruttore privato se questa non è già presente.
-Tale istanza è infatti memorizzata in un _attributo statico_ della classe stessa, in modo così da poterla restituire a chiunque ne abbia bisogno.
-
-{% plantuml %}
-class Singleton {
-    + {static} instance : Singleton
-    # Singleton()
-    + {static} Singleton getInstance()
-    + sampleOp()
-}
-{% endplantuml %}
-
-Con queste accortezze è possibile creare una classe Singleton simile a questa:
-
-```java
-public class Singleton {
-    /* costruttore privato o comunque non pubblico */
-    protected Singleton() { ... }
-
-    /* salvo l'istanza per usarla dopo */
-    private static Singleton instance = null;
-
-    /* metodo statico */
-    public static Singleton getInstance() {
-        if (instance == null) {
-            instance = new Singleton();
-        }
-        return instance;
-    }
-
-    public void metodoIstanza() { ... }
-}
-```
-
-Tuttavia, per come lo abbiamo scritto questa classe non assicura di non creare più di un'istanza di sé stessa, in quanto non prende in considerazione la __concorrenza__.
-Se due processi accedono in modo concorrente al metodo `getInstance`, entrambi potrebbero eseguire il controllo sul valore nullo dell'istanza ed ottenere un successo in quanto l'istanza non è ancora stata assegnata al relativo attributo statico nell'altro processo: si ottiene dunque che uno dei due processi ha accesso ad una propria istanza privata, cosa che distrugge completamente il nostro pattern!
-
-Una prima soluzione sarebbe di mettere un lock sull'esecuzione del metodo anteponendovi la direttiva `@Synchronized`: tuttavia, tale approccio comporterebbe un notevole calo di prestazioni del sistema portando vantaggi unicamente alla prima chiamata. \
-Una soluzione molto più efficiente (non possibile però fino a Java 5) è invece quella che prevede di avere un _blocco sincronizzato_ di istruzioni posto all'interno del ramo in cui si pensa che l'istanza sia nulla in cui ci si chiede se effettivamente l'istanza è nulla e solo allora si esegue il costruttore; la presenza del doppio controllo assicura che non vi siano squilibri dovuti alla concorrenza, mentre sincronizzare solamente un blocco e non l'intero metodo fa sì che il calo di prestazioni sia sentito solamente durante le prime chiamate concorrenti.
-
-### Idioma Java
-
-Fortunatamente si è sviluppato per il linguaggio Java un idioma molto semplice per il Singleton, in cui al posto di usare una classe per definire l'oggetto si usa un __enumerativo__ con un unico valore, l'istanza.
-Ciascun valore di tali oggetti è infatti trattato nativamente da Java proprio come un Singleton: viene creato al momento del suo primo uso, non ne esiste più di una copia, e chiunque vi acceda accede sempre alla medesima istanza.
-La possibilità di creare attributi e metodi all'interno degli `enum` completa il quadro.
-
-```java
-public enum MySingleton {
-    INSTANCE;
-
-    public void metodoIstanza() { ... }
-}
-
-MySingleton.INSTANCE.sampleOp();
-```
-
-Si tratta inoltre di un approccio "thread safe", ovvero che lavora già bene con la concorrenza; l'unico svantaggio è che, se non si conosce l'idioma, a prima vista questa soluzione risulta molto meno chiara rispetto all'approccio precedente.
-
-## <big>I</big>TERATOR
-
-Talvolta gli oggetti che definiamo fanno da __aggregatori__ di altri oggetti, contenendo cioè una collezione di questi su cui poi fare particolari operazioni: in questi casi è molto probabile che vorremo poter iterare sui singoli elementi aggregati, ma senza esporre la rappresentazione interna usata per contenerli.
-
-Proprio per risolvere questo tipo di problematiche nasce il pattern Iterator: esso consiste nella creazione di una classe `ConcreteIterator` che abbia accesso alla rappresentazione interna del nostro oggetto e esponga i suoi elementi in modo sequenziale tramite i metodi `next()` e `hasNext()`; dovendo accedere alla rappresentazione, molto spesso tale iteratore si realizza come una _classe interna anonima_.
-
-Java supporta largamente il pattern Iterator, a tal punto che nella libreria standard esiste un'interfaccia generica per gli iteratori, `Iterator<E>`: all'interno di tale interfaccia sono definiti, oltre ai metodi di cui sopra, il metodo `remove()`, normalmente non supportato in quanto permetterebbe di modificare la collezione contenuta dalla classe, e il metodo `forEachRemaining()`, che esegue una data azione su tutti gli elementi ancora non estratti dell'iteratore.
-
-```java
-public interface Iterator<E> {
-    boolean hasNext();
-    E next();
-
-    default void remove() {
-        throw new UnsupportedOperationException("remove");
-    }
-
-    /* aggiunta funzionale opzionale */
-    default void forEachRemaining(Consumer<? super E> action) {
-        Objects.requireNonNull(action);
-        while (hasNext())
-            action.accept(next());
-    }
-}
-```
-
-Esiste inoltre un'interfaccia che l'oggetto iterabile può implementare, `Iterable<E>`: essa richiede solamente la presenza di un metodo `iterator()` che restituisca l'iteratore concreto, e una volta implementata permette di utilizzare il proprio oggetto aggregatore all'interno di un costrutto foreach.
-
-{% plantuml style="width:60%" %}
-interface Iterable<T> << interface >> {
-    + {abstract} Iterator<T> iterator()
-}
-interface Iterator<T> << interface >> {
-    + {abstract} boolean hasNext()
-    + {abstract} T next()
-    + void remove()
-    {method} ...
-}
-Iterable .> Iterator
-class ConcreteIterable implements Iterable {
-    + Iterator<T> iterator()
-}
-class ConcreteIterator implements Iterator {
-    + boolean hasNext()
-    + T next()
-}
-ConcreteIterable .> ConcreteIterator
-hide empty fields
-{% endplantuml %}
-
-Così, per esempio, possiamo passare dal seguente codice:
-
-```java
-Iterator<Card> cardIterator = deck.getCards();
-while (cardIterator.hasNext()) {
-    Card card = cardIterator.next();
-    System.out.println(card.getSuit());
-}
-```
-
-... a quest'altro:
-
-```java
-for (Card card : deck) {
-    System.out.println(card.getSuit());
-}
-```
-
-Oltre ad essere più stringato il codice è significativamente più chiaro, rendendo palese che la singola `card` sia read-only.
-
-## <big>C</big>HAIN OF <big>R</big>ESPONSIBILITY
-
-Talvolta nei nostri programmi vorremmo definire una gestione "a cascata" di una certa richiesta. Pensiamo per esempio a una serie di regole anti-spam: all'arrivo di una mail la prima regola la esamina e si chiede se sia applicabile o meno; in caso affermativo contrassegna la mail come spam, altrimenti _la passa alla prossima regola_, che a sua volta farà lo stesso test passando il controllo alla terza in caso negativo, e così via.
-Abbiamo cioè un _client_ in grado di fare una richiesta, e una __catena di potenziali gestori__ di cui non sappiamo a priori chi sarà in grado di gestirla effettivamente.
-
-Il pattern Chain of Responsibility risolve il disaccoppiamento tra client e gestore _concatenando i gestori_.
-Esso prescrive la creazione di un'interfaccia a cui tutti i gestori devono aderire, contenente solo la dichiarazione di un metodo `evaluate` che implementa la logica descritta prima: si stabilisce se si può gestire la richiesta, e se non si può si chiama lo stesso metodo su _un altro gestore_ ottenuto come parametro al momento della creazione.
-
-{% plantuml style="width: 100%" %}
-class PokerHand {
-    + HandRank getRank()
-}
-interface ChainedHandEvaluator << interface >> {
-    + {abstract} handEvaluator()
-}
-PokerHand -> ChainedHandEvaluator
-ChainedHandEvaluator "0..1" o-> "0..1" ChainedHandEvaluator : next
-class HighCardEvaluator implements ChainedHandEvaluator {
-    + HighCardEvaluator(ChainedHandEvaluator)
-    + handEvaluator()
-}
-note left of HighCardEvaluator::"handEvaluator()" 
-    if (can_handle) { 
-        return do_it(); 
-    } else { 
-        return next.handEvaluator(); 
-    }
-end note
-hide empty fields
-{% endplantuml %}
-
-In questo modo all'interno del client è sufficiente creare una vera e propria catena di gestori e chiamare il metodo `evaluate` del primo: si noti che l'ordine in cui vengono assemblati tali gestori conta, in quanto la valutazione procede sequenzialmente.
-
-```java
-public interface Gestore {
-
-    /* Il tipo di ritorno dipende dal campo applicativo */
-    public ??? evaluate(); 
-
-}
-
-public class Client {
-
-    private Gestore evaluator = 
-        new GestoreConcreto1(
-            new GestoreConcreto2(
-                new GestoreConcreto3(null)));
-
-    public void richiesta() {
-        evaluator.evaluate();
-    }
-
-}
-```
-
-## <big>F</big>LY<big>W</big>EIGHT
-
-Talvolta ci troviamo in una situazione simile a quella che aveva ispirato il pattern Singleton: abbiamo una __serie di oggetti immutabili fortemente condivisi__ all'interno del programma e per motivi di performance e risparmio di memoria vorremmo che _non esistano istanze diverse a parità di stato_.
-Se due client devono usare un'istanza con lo stesso stato vorremmo cioè non usino ciascuno un'istanza duplicata ma proprio la _stessa istanza_: essendo le istanze immutabili, tale condivisione non dovrebbe infatti creare alcun tipo di problema.
-
-Il pattern FlyWeight serve a gestire una collezione di oggetti immutabili assicurandone l'unicità: esso consiste nel rendere privato il costruttore e __costruire tutte le istanze a priori con un costruttore statico__, salvandole in una lista privata.
-I client possono dunque richiedere una certa istanza con un metodo `get` specificando lo stato dell'istanza desiderata: in questo modo, a parità di richiesta verranno restituite le stesse identiche istanze.
-
-Abbiamo visto un'applicazione di questo pattern durante i laboratori parlando di `Card`:
-
-```java
-public class Card {
-    private static final Card[][] CARDS = new Card[Suit.values().length][Rank.values().length];
-
-    static {
-        for (Suit suit : Suit.values()) {
-            for (Rank rank : Rank.values()) {
-                CARDS[suit.ordinal()][rank.ordinal()] = new Card(rank, suit);
-            }
-        }
-    }
-
-    public static Card get(Rank pRank, Suit pSuit) {
-        return CARDS[pSuit.ordinal()][pRank.ordinal()];
-    }
-}
-```
-
-A differenza del pattern Singleton è difficile definire a priori quante istanze ci sono: abbiamo un'istanza per ogni possibile combinazione dei valori degli attributi che compongono lo stato.
-Proprio per questo motivo il pattern può risultare un po' inefficiente per oggetti con rappresentazioni grandi: alla prima computazione vengono infatti inizializzati _tutti_ gli oggetti, perdendo un po' di tempo e sprecando potenzialmente spazio se non tutte le istanze saranno accedute.
-
-## <big>N</big>ULL<big>O</big>BJECT
-
-Spesso nei nostri programmi avremo bisogno di utilizzare valori "nulli": pensiamo per esempio al termine di una Chain of Responsibilities, dove per fermare la catena di chiamate dobbiamo dare un valore nullo al `next` dell'ultimo gestore.
-In generale, a una variabile che indica un riferimento ad un oggetto possiamo assegnare il valore speciale `null` per indicare che essa _non punta a nulla_.
-
-Il problema sorge però quando a runtime si prova a dereferenziare tale valore e viene sollevata un'eccezione (`NullPointerException` in Java): questa possibilità ci costringe nel codice ad essere sempre molto titubanti sui valori che ci vengono passati, in quanto non possiamo mai assumere che essi puntino ad un valore reale e dunque dobbiamo sempre controllare che non siano nulli.
-
-C'è però da dire che anche con tali accortezze l'utilizzo di `null` è poco carino, in quanto un valore nullo può indicare cose anche molto diverse:
-
-- un errore a runtime
-- uno stato temporaneamente inconsistente
-- un valore assente o non valido
-
-Ogni volta che si utilizza `null` il codice diventa un po' meno chiaro, e sarebbe necessario disambiguare con commenti o documentazione per spiegare con che accezione tale valore viene usato.
-Anche le strategie di gestione del `null` variano drasticamente a seconda del significato assegnato a tale valore: quando non ci sono valori "assenti" e dunque il `null` indica solo un errore è sufficiente controllare che i dati passati non siano nulli con condizioni, asserzioni o l'annotazione `@NotNull`.
-
-{% responsive_image path: assets/09_nullObject-valori-non-assenti.png %}
-
-Quando invece ci sono __valori "assenti"__, ovvero che indicano situazioni particolari (es. il Joker in un mazzo di carte, che non ha né Rank né Suit), la gestione è più complicata.
-Se non vogliamo trattarli come `null` per l'ambiguità che tale valore introduce, un'altra opzione è creare un metodo booleano nella classe che restituisce se l'istanza ha il valore nullo (es. `isJoker()`): tuttavia, questo apre le porte a errori da parte dell'utente, che potrebbe dimenticarsi di fare tale controllo e usare l'oggetto come fosse qualunque altro.
-
-Per creare un oggetto che corrisponda al __concetto di nessun valore__ o __valore neutro__ nasce allora il pattern NullObject: si crea all'interno della classe o dell'interfaccia un _oggetto statico_ chiamato `NULL` che fornisce _particolari implementazioni dei metodi_ della stessa per realizzare l'idea di valore nullo a livello di dominio.
-In questo modo tale oggetto mantiene l'identità della classe rimanendo però sufficientemente separato dagli altri valori; inoltre, la presenza di implementazioni specifiche dei metodi evita il lancio di eccezioni ambigue.
-
-```java
-public interface CardSource {
-    Card draw();
-    boolean isEmpty();
-
-    public static CardSource NULL = new CardSource() {
-        public boolean isEmpty() { 
-            return true; 
-        }
-        public Card draw() {
-            assert !isEmpty();
-            return null;
-        }
-    }
-}
-```
-
-Quindi possiamo notare che il concetto del NullObject pattern è quello di creare un oggetto in cui viene definito un comportamento specifico per ogni metodo che rispecchia ciò che accadrebbe nel caso in cui il metodo venisse chiamato su null nel normale flusso di istruzioni.
-
-## <big>S</big>TRATEGY / <big>D</big>ELEGATION
-
-Talvolta nelle nostre classi vogliamo definire __comportamenti diversi per diverse istanze__: la soluzione classica dei linguaggi Object-Oriented è la creazione di una gerarchia di classi in cui le classi figlie sovrascrivano i metodi della classe genitore.
-Tuttavia, questo espone a delle problematiche: cosa fare se per esempio la classe genitore cambia aggiungendo un metodo che una delle classi figlie non dovrebbe poter implementare (es. `RubberDuck` come figlia di `Duck`, che aggiunge il metodo `fly()`)?
-
-Non volendo violare il principio Open-Close, non siamo intenzionati a rimuovere il metodo incriminato, per cui dobbiamo cercare altre soluzioni. Una prima idea sarebbe quella di sopperire al fatto che la classe genitore non sappia chi sono i suoi figli con costrutti proprietari del linguaggio:
-
-- una classe `Final` non permette di ereditare, ma questo non ci permetterebbe di differenziare il comportamento;
-- una classe `Sealed` (aggiunta di Java 17) sceglie esplicitamente chi possano essere i suoi figli: in questo modo si può evitare che la classe figlia problematica non possa ereditare, ma si tratta comunque di una soluzione parziale.
-
-Non si può neanche pensare di fare semplicemente l'override nella classe figlia del metodo aggiunto facendo in modo che lanci un'eccezione: si avrebbe infatti una inaccettabile violazione del principio di sostituzione di Liskov, che afferma sostanzialmente che un'istanza di una sottoclasse deve poter essere usata senza problemi al posto dell'istanza di una classe genitore.
-
-Una soluzione migliore si basa invece sul concetto di __delega__, che sostituisce all'ereditarietà la _composizione_.
-Fondamentalmente si tratta di individuare ciò che cambia nell'applicazione e separarlo da ciò che rimane fisso: si creano delle _interfacce per i comportamenti da diversificare_ e una _classe concreta che implementa ogni diverso comportamento_ possibile.
-All'interno della classe originale si introducono dunque degli __attributi di comportamento__, impostati al momento della costruzione o con dei setter a seconda della dinamicità che vogliamo permettere: quando viene richiesto il comportamento a tale classe essa si limiterà a chiamare il proprio "oggetto di comportamento".
-Nell'esempio delle `Duck`, per esempio, la struttura è la seguente:
-
-{% plantuml style="width: 100%" %}
-interface FlyBehavior << interface >> {
-    + {abstract} fly()
-}
-class Duck {
-    + performQuack()
-    + performFly()
-    + swim()
-    + {abstract} display()
-}
-FlyBehavior <-o Duck
-interface QuackBehavior << interface >> {
-    + {abstract} quack()
-}
-Duck o-> QuackBehavior
-class FlyWithWings implements FlyBehavior {
-    + fly()
-}
-class FlyNoWay implements FlyBehavior {
-    + fly()
-} 
-class Quack implements QuackBehavior {
-    + quack()
-}
-class Mute implements QuackBehavior {
-    + quack()
-}
-class Squeak implements QuackBehavior {
-    + quack()
-}
-hide empty fields
-{% endplantuml %}
-
-Come si vede, qui non c'è scritto da nessuna parte che una `Duck` deve volare, ma solo che deve definire la sua "politica di volo" incorporando un `FlyBehaviour`.
-
-La differenziazione dei comportamenti si fa dunque _a livello d'istanza_ e non di classe: il pattern definisce una famiglia algoritmi e li rende tra di loro intercambiabili tramite _encapsulation_.
-Per questo motivo tale pattern è usato in situazioni anche molto diversa da quella con cui l'abbiamo introdotto: un altro esempio presente in Java è l'interfaccia `Comparator`.
-
-{% plantuml style="width: 70%" %}
-class Client {}
-interface AbstractStrategy << interface >> {
-    + {abstract} void doSomething()
-}
-Client .> AbstractStrategy
-class ConcreteStrategy1 implements AbstractStrategy {
-    + void doSomething()
-}
-class ConcreteStrategy2 implements AbstractStrategy {
-    + void doSomething()
-}
-hide empty fields
-{% endplantuml %}
-
-## <big>O</big>BSERVER
-
-Molto spesso capita di avere nei nostri programmi una serie di elementi che vanno tenuti sincronizzati: pensiamo per esempio ad una ruota dei colori che deve aggiornare i valori RGB quando l'utente seleziona un punto con il mouse. Abbiamo cioè uno __stato comune__ che va mantenuto coerente in tutti gli elementi che lo manipolano.
-
-Nella realizzazione di questa funzionalità si rischia di cadere nell'anti-pattern delle _pairwise dependencies_ in cui ogni vista dello stato deve conoscere tutte le altre: si ha cioè un forte accoppiamento e una bassissima espandibilità, in quanto per aggiungere una vista dobbiamo modificare tutte le altre.
-Ovviamente basta avere poco più di due diverse viste perché il numero di dipendenze (e dunque di errori) cresca esponenzialmente: questo anti-pattern è proprio tutto il contrario del principio di separazione, che predicava forte coesione interna e pochi accoppiamenti esterni.
-
-{% plantuml style="width: 50%" %}
-entity IntegerPanel {}
-entity SliderPanel {}
-entity TextPanel {}
-IntegerPanel ..> SliderPanel
-IntegerPanel ..> TextPanel
-SliderPanel ..> IntegerPanel
-SliderPanel .> TextPanel
-TextPanel ..> IntegerPanel
-TextPanel .> SliderPanel
-hide members
-{% endplantuml %}
-
-La soluzione proposta dal pattern Observer è dunque quella di estrarre la parte comune (lo _stato_) e isolarlo in un oggetto a parte, detto __Subject__: tale oggetto verrà osservato da tutte le viste, le cui classi prendono ora il nome di __Observer__.
-Si sta cioè __centralizzando__ la gestione dello stato: abbiamo cioè $$n$$ classi che osservano una classe centrale e reagiscono ad ogni cambiamento di stato di quest'ultima. \
-Si tratta una situazione talmente comune che in Java erano presenti delle classi (ora deprecate in quanto non _thread-safe_) per realizzare questo pattern: `java.util.Observer` e `java.util.Observable`.
-
-Ma come fanno gli Observer a sapere che il Subject è cambiato?
-L'idea di fare un continuo _polling_ (chiedo "Sei cambiato?" al Subject), non è ovviamente sensata, in quanto bloccherebbe l'esecuzione sprecando tantissime risorse.
-Invertiamo invece la responsabilità con un'architettura __event-driven__: gli Observer si _registrano_ al Subject, che li informerà quando avvengono cambiamenti di stato.
-
-{% plantuml style="width: 100%" %}
-class Observable {
-    + addObserver(Observer)
-    + removeObserver(Observer)
-    + notifyObservers()
-}
-interface Observer << interface >> {
-    + {abstract} update(Observable, Object)
-}
-Observable o-> Observer
-class ConcreteObserver implements Observer {
-    + update(Observable, Object)
-}
-class ConcreteObservable extends Observable {
-    + getState() : State
-    + setState(State)
-}
-ConcreteObservable <. ConcreteObserver
-hide empty fields
-{% endplantuml %}
-
-Restano però da capire un paio di cose.
-Bisogna innanzitutto spiegare _come colleghiamo Observer e Subject_: come si vede in figura, esiste una classe `Observable` che funge da base da cui ereditare per ogni Subject; vi è poi un'interfaccia `Observer` che gli Observer concreti devono ovviamente implementare.
-
-A questo punto gli Observer si possono sottoscrivere al Subject semplicemente attraverso l'uso delle sue funzioni `addObserver()` e `removeObserver()`, venendo così sostanzialmente inseriti o rimossi nella lista interna degli Observer interessati. \
-Una volta che lo stato del Subject viene cambiato, solitamente attraverso una serie di metodi pubblici che permettano a tutti di modificarlo (`setState()`), esso chiama dunque il suo metodo `notifyObservers()`: questo altro non fa che ciclare su tutti gli Observer sottoscritti chiamandone il metodo `update(Observable, Object)`, dove:
-
-- `Observable` è il Subject di cui è stato modificato lo stato (l'uso di interfacce permette di sottoscrivere un Observer a più Subject tra cui disambiguare al momento dell'update)
-- `Object` è la parte di stato che è cambiata (_Object_ perché il tipo dipende ovviamente dal Subject in questione)
-
-Sul metodo di notifica del cambiamento di stato esistono però due diverse filosofie, __push__ e __pull__, ciascuna con i suoi campi applicativi prediletti: vediamole dunque singolarmente, evidenziando quando e come esse sono utilizzate.
-
-### push
-
-In questo caso l'argomento Observable di `update` viene messo nullo, mentre __nell'Object viene passata la totalità dello stato__ del Subject:
-
-```java
-// Observable
-@Override
-public void notifyObservers() {
-
-    for (Observer observer : observers) {
-        observer.update(null, state);
-    }
-
-}
-
-// Observer
-@Override
-public void update(Observable model, Object state) {
-
-    if (state instanceof Integer intValue) {
-        doSomethingOn(intValue);
-    }
-
-}
-```
-
-Come si vede, dovendo definire come reagire al cambiamento di stato in `update` l'Observer dovrà innanzitutto fare un down-casting per ottenere un oggetto della classe corretta.
-Avendo la responsabilità di tale casting l'Observer dovrà conoscere precisamente la struttura dello stato del Subject, creando una _forte dipendenza_ che potrebbe creare problemi di manutenibilità.
-
-Un altro problema di questo approccio è che gli Observer sono solitamente interessati a una piccola porzione dello stato del Subject, quindi passarlo tutto come parametro potrebbe sovraccaricare inutilmente la memoria.
-
-### pull
-
-Con questo approccio, invece di mandare lo stato all'`update` __viene passato il Subject stesso__, il quale conterrà uno o più metodi per accedere allo stato (`getState`):
-
-```java
-// Observable
-@Override
-public void notifyObservers() {
-
-    for (Observer observer : observers) {
-        observer.update(this, null);
-    }
-
-}
-
-// Observer
-@Override
-public void update(Observable model, Object state) {
-
-    if (model instanceof ConcreteObservable cModel) {
-        doSomethingOn(cModel.getState());
-    }
-
-}
-```
-
-Sebbene comporti un passaggio in più poiché l'Observer deve chiamare un metodo del Subject quando riceve la notifica, questo cambio di prospettiva offre due vantaggi: in primo luogo non viene passato tutto lo stato, il che fa risparmiare molta memoria; inoltre, il Subject potrebbe decidere di rendere disponibili sottoinsiemi diversi dello stato con getter diversi, mostrando così ad ogni Observer solo le informazioni per esso rilevanti.
-
-Inoltre, sebbene anche in questo caso sia richiesto un casting (da Observable al Subject), questo approccio rende meno dipendenti dalla rappresentazione interna del Subject: fintanto che la firma dei getter non cambia lo stato interno del Setter può cambiare senza problemi.
-
-### Approccio ibrido e dipendenze
-
-Partiamo col dire che molto spesso nei casi reali gli approcci _push_ e _pull_ sono ibridati tra di loro: ad `update` viene passato sia il Subject che quella parte di stato utile a tutti gli Observer, mentre qualora gli serva qualcosa di più specifico essi se lo andranno a prendere con il getter.
-
-Il vero problema di entrambi gli approcci è però quello delle dipendenze: nel caso _push_ dipendiamo dalla rappresentazione interna del Subject, mentre nel caso _pull_ dalla sua classe concreta.
-Poiché tale dipendenza non è facilmente eliminabile, piuttosto che lasciarla nascosta nel casting conviene __esplicitarla__:
-
-- all'interno dell'Observer salvo l'istanza di Observable a cui mi sono sottoscritto, così al momento dell'`update` posso verificare direttamente che l'istanza sia quella al posto di fare un casting;
-
-- creiamo una classe `State` e l'aggreghiamo sia nell'Observer che nell'Observable concreto in modo che essa nasconda la rappresentazione reale dello stato.
-
-Otteniamo dunque un codice simile al seguente:
-
-```java
-public class State { /* rappresentazione interna dello stato */ }
-
-public class Observable {
-    private State stato;
-    private List<Observer> observers = new ArrayList<>();
-
-    public void addObserver(@NotNull Observer obs) { observers.add(obs); }
-    public void removeObserver(@NotNull Observer obs) { observers.remove(obs); }
-    public void notifyObservers() {
-        for (Observer obs: observers) update(this, stato);
-    }
-}
-
-public class Subject extends Observable {
-
-    public void setState(State nuovoStato) { ... }
-    public State getState() { return super.stato; }
-    /* Opzionale: altri metodi getter */
-}
-
-public interface Observer {
-    public void update(Observable subject, Object stato);
-}
-
-public class ConcreteObserver {
-    private Observable mySubject;
-
-    @Override
-    public void update(Observable subject, Object stato) {
-        if (subject == mySubject) {
-            ...
-        }
-    }
-}
-```
-
-## <big>A</big>DAPTER
-
-Spesso nei programmi che scriviamo capita di dover __far collaborare interfacce diverse__ di componenti non originariamente sviluppati per lavorare insieme.
-Questo capita in una miriade di situazioni, ma volendone citare alcune:
-
-- in un ambito di sviluppo COTS (_Component Off The Shelf: sviluppiamo solo ciò che non è disponibile tramite librerie o codice open-source_) riutilizziamo tanti componenti presi dal mercato, non pensati per essere compatibili;
-- sviluppando ed evolvendo un programma in modo _incrementale_ capita di dover integrare componenti nuovi con componenti vecchi (_legacy_) per garantire una certa continuità nell'esperienza utente.
-
-Da tutta una serie di situazioni simili è nato il bisogno di creare delle strutture che permettessero di rendere compatibili componenti già esistenti, ovvero creare della _"colla"_ in grado di legare i componenti tra loro per soddisfare le specifiche del sistema.
-È così ben presto scaturito il pattern __Adapter__, un pattern ormai molto diffuso che consiste nel creare vari moduli che possano essere incollati o adattati ad altre strutture in modo da renderle utilizzabili incrementalmente e in modo controllato. 
-
-Sebbene sia già utilizzato molto spesso, talvolta anche inconsciamente, approfondiamo il pattern in questa sede non solo per imparare a usarlo con più criterio, ma anche perché di esso esistono due "versioni":
-
-- __Class Adapter__: adatta una classe.
-- __Object Adapter__: adatta un oggetto di una classe.
-
-Come vedremo, questi due pattern sono molto simili a livello di schema UML ma abbastanza differenti da rendere importante capire quale usare in quali contesti, comprendendo vantaggi e svantaggi di entrambi.
-
-### Class Adapter
-
-{% plantuml style="width: 80%" %}
-class Client {}
-interface Target << interface >> {
-    + {abstract} request()
-}
-Client .> Target
-class Adaptee {
-    + oldRequest()
-}
-class ClassAdapter implements Target {
-    + request()
-}
-Adaptee <|-- ClassAdapter
-hide empty fields
-{% endplantuml %}
-
-Come si vede dallo schema UML, per permettere a un _Client_ di comunicare tramite un'interfaccia _Target_ con un componente concreto vecchio detto _Adaptee_ il Class Adapter utilizza una classe concreta che __implementa l'interfaccia Target__ e __estende la classe Adaptee__, ereditandone così i metodi e la vecchia interfaccia: all'interno di tale classe potremo dunque limitarci a _rimappare le funzionalità_ richieste dalla nuova interfaccia su quella vecchia, implementando qualcosa solo se strettamente necessario e comunque sfruttando la logica già presente della classe estesa.
-
-```java
-public class Adapter extends Adaptee implements Target {
-    @Override
-    public void request() {
-        this.oldRequest();
-    }
-}
-```
-
-In questo modo il client utilizzerà l'adapter come se fosse l'oggetto completo, non accorgendosi che quando ne chiama un metodo in realtà il codice eseguito è quello appartenente alla vecchia classe già esistente: in un __unica istanza__ si sono dunque riunte l'interfaccia vecchia e quella nuova.
-
-Vediamo dunque quali sono i pro e i contro di questo approccio. È utile innanzitutto notare che estendendo l'Adaptee la classe Adapter ha parziale accesso alla sua rappresentazione interna, un vantaggio non da poco quando si considera quanto questo faciliti l'eventuale modifica di funzionalità; inoltre, essa ne eredita le definizioni dei metodi, e se questi non devono cambiare tra la vecchia interfaccia e la nuova si può evitare di ridefinirli totalmente, risparmiando così parecchio codice.
-
-Inoltre, un'istanza della classe Adapter può essere utilizzata attraverso __entrambe le interfacce__ in quanto implementa quella nuova ed eredita quella vecchia; questo aspetto può essere considerato sia un vantaggio che uno svantaggio: se infatti da un lato ciò è molto utile in sistemi che evolvono incrementalmente e in cui dunque alcune componenti potrebbero volersi riferire ancora alla vecchia interfaccia, d'altro canto questo aspetto impedisce di imporre tassativamente che l'oggetto sia utilizzato solo tramite l'interfaccia nuova.
-
-Va poi notato che questo approccio perde un po' di senso nel caso in cui si debba adattare un'_interfaccia_ e non una classe, in quanto implementare entrambe le interfacce non permette di ereditare codice o funzionalità da quella vecchia.
-Inoltre, il Class Adapter potrebbe presentare problemi relativi all'ereditarietà multipla, non supportata da alcuni linguaggi a oggetti (es. Java).
-
-### Object Adapter
-
-{% plantuml style="width: 80%" %}
-class Client {}
-interface Target << interface >> {
-    + {abstract} request()
-}
-Client .> Target
-class ObjectAdapter implements Target {
-    + request()
-}
-class Adaptee {
-    + oldRequest()
-}
-ObjectAdapter o-> Adaptee
-hide empty fields
-{% endplantuml %}
-
-Come abbiamo già detto più volte, spesso conviene prediligere la _composizione_ rispetto all'ereditarietà: al pattern del Class Adapter si contrappone dunque l'Object Adapter, che invece di estendere la classe Adaptee __contiene una sua istanza__ e __delega__ ad essa tramite la vecchia interfaccia le chiamate ai metodi dell'interfaccia nuova, eventualmente operando i necessari rimaneggiamenti.
-
-```java
-public class Adapter implements Target {
-    private final Adaptee adaptee;
-
-    public Adapter(Adaptee adaptee) {
-        assert adaptee != null;
-        this.adaptee = adaptee;
-    }
-
-    @Override
-    public void request() {
-        adaptee.oldRequest();
-    }
-}
-```
-
-Anche in questo caso il client non si accorge di nulla, e in particolare non sarebbe nemmeno in grado di dire con certezza se l'Adapter utilizzato sia un Class Adapter o un Object Adapter: a lui la scelta del paradigma è del tutto trasparente.
-
-Rispetto al Class Adapter l'Object Adapter presenta differenti punti di forza e di debolezza, e il primo di questi ultimi è rappresentato dal fatto che invece di avere un'unica istanza che racchiuda entrambe le interfacce con questo pattern abbiamo invece _due istanze_ (Adapter e Adaptee contenuto), cosa che può costituire un notevole spreco di memoria in certe situazioni. 
-
-Inoltre, aver sostituito l'ereditarietà con la composizione ha lo sgradevole effetto di non permettere all'Adapter di vedere in alcun modo la rappresentazione protetta dell'Adaptee, che esso dovrà invece manipolare unicamente tramite la sua interfaccia pubblica.
-Si è poi costretti a _reimplementare ogni metodo_ anche se questo non è cambiato dall'interfaccia vecchia a quella nuova, in quanto è comunque necessario operare la delega all'Adaptee.
-
-Tuttavia, l'Object Adapter si rivela particolarmente utile nel caso ad essere adattata debba essere un'_interfaccia_: non soffrendo di problemi di ereditarietà, un Object Adapter ha la peculiarità di poter adattare chiunque implementi la vecchia interfaccia, ovvero un'intera _gerarchia_ di classi potenzialmente non ancora esistenti!
-
-#### Class Adapter vs Object Adapter
-
-Class Adapter e Object Adapter hanno ciascuno i propri vantaggi e svantaggi che li rendono più adatti ad essere utilizzati in diverse situazioni.
-Volendo fare un confronto tra i due approcci proponiamo dunque la seguente tabella:
-
-| Aspetto | Class Adapter | Object Adapter |
-|---------|---------------|----------------|
-| Accesso all'Adaptee | <span style="color:green">L'Adapter può accedere ad attribuiti e metodi protetti dell'Adaptee</span> | <span style="color:red">L'Adapter può interagire con l'Adaptee solo tramite la sua interfaccia pubblica</span>|
-| Riuso del codice | <span style="color:green">Non richiede di reimplementare i metodi che non cambiano</span> | <span style="color:red">Qualunque metodo va reimplementato per fare la delega</span> |
-| Uso della memoria | <span style="color:green">Un'unica istanza</span> | <span style="color:red">Due istanze obbligatorie</span> |
-| Adozione delle interfacce | <span style="color:#ff9900">L'istanza può essere usata con entrambe le interfacce</span> | <span style="color:#ff9900">L'istanza può essere usata solo tramite la nuova interfaccia</span> |
-| Problemi di ereditarietà multipla | <span style="color:red">Possibili</span> | <span style="color:green">No</span>|
-| Adattamento delle interfacce | <span style="color:red">Non è indicato</span> | <span style="color:green">Adattando un'interfaccia può adattare un'intera gerarchia di classi</span>|
-
-<span> <!-- ugly spacer --> </span>
-
-## <big>F</big>ACADE
-
-Costruendo un sistema complesso può capitare di dover definire una serie di interfacce molto specifiche e dettagliate per i propri componenti in modo che questi possano lavorare correttamente in concerto tra di loro.
-Il problema sorge però quando un Client, dovendo accedere al sistema, si ritrova costretto a dover interagire direttamente con i sottosistemi che lo compongono, cosa che lo obbliga a sviscerare i funzionamenti interni dello stesso per ottenere un comportamento tutto sommato semplice.
-
-Lo scopo del pattern Facade è allora quello di __fornire un'interfaccia unificata e semplificata a un insieme di interfacce separate__: spesso infatti l'uso comune di un sistema si riduce un paio di operazioni ottenibili combinando varie funzionalità fornite dal package; invece di richiedere al Client di operare tale composizione facciamo ricadere sulle nostre spalle tale compito costruendo una _classe_ che faccia da _interfaccia standard_ al sistema.
-
-{% responsive_image path: assets/09_facade.png %}
-
-Si noti come questo non impedisca al Client di usare anche le funzionalità più complesse, ma metta solo ulteriormente a disposizione un'interfaccia che gli permetta di sfruttare facilmente quelle più frequentemente utilizzate.
-Volendo fornire un esempio nella vita reale, un telecomando fornisce un'interfaccia semplice ai controlli della televisione, permettendo di regolare il volume e cambiare canale con semplicità: aprendo però uno sportellino ecco che ci vengono forniti tutti i comandi più specifici.
-
-## <big>C</big>OMPOSITE
-
-Immaginiamo di dover modellare un file system in un'applicazione: esso sarà composto di File e Directory, le quali dovranno essere in grado di contenere al loro interno File e ulteriori Directory; dovremo cioè ottenere una struttura ad albero di Directory avente dei File come foglie.
-Se però molte funzionalità del file system operano in modo analogo sia sui File che sulle Directory (_es. creazione, cancellazione, ottenimento della dimensione etc._), come possiamo gestire queste due classi in modo uniforme per evitare di duplicare il codice?
-
-Per gestire simili strutture ad albero che rappresentano _insiemi e gerarchie di parti_ viene introdotto il pattern __Composite__: esso mira a gestire oggetti singoli, gruppi e persino gruppi di gruppi in maniera uniforme e trasparente in modo che un client non interessato alla struttura gerarchica possa utilizzarli senza accorgersi delle differenze.
-
-{% plantuml style="width: 80%" %}
-interface Component << interface >> {
-    + {abstract} sampleOperation()
-}
-class Leaf implements Component {
-    + sampleOperation()
-}
-class Composite {
-    + sampleOperation()
-    + add(Component)
-    + remove(Component)
-}
-Composite .|> Component
-Composite "0..1" o-> "0..n" Component
-hide empty fields
-{% endplantuml %}
-
-Abbiamo quindi gli oggetti singoli, rappresentati dalla classe _Leaf_, e gli oggetti composti rappresentati dalla classe _Composite_.
-Per realizzare l'uniformità di gestione dobbiamo introdurre un livello di astrazione, quindi Leaf e Composite implementano una stessa __interfaccia Component__ contenente la definizione delle operazioni comuni. \
-L'uso dell'interfaccia comune permette di definire all'interno di Composite le operazioni di aggiunta e rimozione di oggetti al gruppo in modo generale, permettendo cioè che un _Composite aggreghi sia Leaf che altri Composite_.
-
-A proposito di tale aggregazione, dallo schema UML possiamo notare le relative cardinalità: "0..n" dal lato del Composite e "0..1" da quello del Component.
-Esse indicano che:
-
-- Un'istanza di Composite aggrega 0 più istanze di Component al suo interno: in questo modo si permette che al momento della creazione il Composite sia totalmente vuoto; se questo non ha alcun senso logico nell'applicazione si può invece modificare la cardinalità in "1..n" imponendo che al costruttore di Composite venga passato un Component iniziale da contenere;
-
-- Un'istanza di Component può essere contenuta in al più un'istanza di Composite: può cioè essere libero o aggregato in un gruppo, ma non può appartenere contemporaneamente a più gruppi, cosa che forza una struttura strettamente ad albero.
-
-Nella maggior parte dei casi un'istanza Composite utilizzerà gli oggetti aggregati per implementare effettivamente i metodi descritti dall'interfaccia comune, delegando a loro l'esecuzione effettiva e limitandosi ad elaborare i risultati.
-Riprendendo l'esempio di prima, per conoscere la dimensione di una Directory sarà sufficiente sommare le dimensioni dei File e delle altre Directory in essa contenuti.
-
-Il patter Composite presenta numerosi vantaggi, ma non è nemmeno esente da criticità.
-L'uso di un'interfaccia comune per Leaf e Composite permette al client di non preoccuparsi del tipo dell'oggetto con cui sta interagendo, in quanto ogni Component è in grado di eseguire le operazioni descritte nell'interfaccia in modo indistinguibile; tuttavia, questo implica che non è possibile distinguere tra oggetti singoli e composti. \
-Inoltre, l'uso dell'interfaccia per l'aggregazione nei Composite rende impossibile imporre dei controlli su cosa possa contenere un certo tipo di Composite: non si può per esempio forzare che raggruppi solo certi tipi di elementi, o che l'albero di composizione abbia profondità al più pari a tre.
-
-Un "dialetto" del pattern tenta di risolvere il problema dell'indistinguibilità tra Leaf e Composite introducendo nell'interfaccia Component un metodo `getComposite` che in un Composite restituisca `this` e in una Leaf restituisca `null`.
-L'uso di valori nulli e la necessità di strani casting rende però pericolosa l'adozione di questa versione del pattern.
-
-## <big>D</big>ECORATOR
-
-Immaginiamo di voler modellare con degli oggetti una grande varietà di pizze differenti sia per la base (_es. normale, integrale, senza glutine..._) che per gli ingredienti che vi si trovano sopra.
-Per ogni diversa varietà di pizza vorremmo ottenere un oggetto aderente a un'interfaccia comune `Pizza` il cui metodo `toString()` elenchi la base e gli ingredienti che la compongono.
-
-Un primo approccio _statico_ a questo problema consiste nel creare una gerarchia di classi che contenga una classe per ogni possibile combinazione di base e ingredienti, che d'ora in avanti chiameremo "__decorazioni__".
-
-```java
-public interface Pizza {}
-
-public class BaseNormale implements Pizza {
-    public String toString() { 
-        return "Sono una pizza con: base normale"; 
-    }
-}
-
-public class BaseIntegrale implements Pizza {
-    public String toString() { 
-        return "Sono una pizza con: base integrale"; 
-    }
-}
-
-public class BaseNormaleSalame extends BaseNormale {
-    public String toString() { 
-        return "Sono una pizza con: base normale, salame"; 
-    }
-}
-
-public class BaseNormaleSalamePeperoni extends BaseNormaleSalame {
-    public String toString() {
-        return "Sono una pizza con: base normale, salame, peperoni"; 
-    }
-}
-
-...
-```
-
-Come è subito ovvio, però, questo approccio risulta assolutamente da evitare per una serie di motivi: in primo luogo l'esplosione combinatoria dovuta all'accoppiamento di ogni possibile base e insieme di decorazioni, e in secondo luogo l'estrema difficoltà che comporterebbe una futura aggiunta di decorazioni.
-
-L'ideale sarebbe invece poter __aggiungere funzionalità e caratteristiche dinamicamente__, restringendo la gerarchia ad un'unica classe le cui istanze possano essere "decorate" su richiesta al momento dell'esecuzione. \
-La soluzione più semplice a questo nuovo problema parrebbe quella che viene definita una <big>G</big>OD CLASS (o _fat class_), ovvero un'unica classe in cui tramite attributi booleani e `switch` vengono attivate o disattivate diverse decorazioni.
-
-```java
-public class GodPizza {
-
-    boolean baseNormale = false;
-    boolean baseIntegrale = false;
-    ...
-
-    boolean salame = false;
-    boolean pancetta = false;
-    boolean peperoni = false;
-    ...
-
-    public void setBaseNormale(boolean status) { baseNormale = status; }
-    public void setBaseIntegrale(boolean status) { baseIntegrale = status; }
-    ...
-
-    public void setSalame(boolean status) { salame = status; }
-    public void setPancetta(boolean status) { pancetta = status; }
-    public void setPeperoni(boolean status) { peperoni = status; }
-    ...
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder("Sono una pizza con: ");
-        if (baseNormale) sb.append("base normale, ");
-        if (baseIntegrale) sb.append("base integrale, ");
-        ...
-        if (salame) sb.append("salame, ");
-        if (pancetta) sb.append("pancetta, ");
-        if (peperoni) sb.append("peperoni, ");
-        ...
-        sb.removeCharAt(sb.length() - 1);
-        sb.removeCharAt(sb.length() - 1);
-        return sb.toString();
-    }
-}
-```
-
-Si tratta però questo di un chiaro anti-pattern, una soluzione che sebbene invitante e semplice in un primo momento da realizzare nasconde delle criticità non trascurabili.
-Si tratta infatti di una chiara violazione dell'Open-Close Principle, in quanto per aggiungere un decoratore è necessario modificare la God Class; inoltre, tale classe diventa molto velocemente gigantesca, zeppa di funzionalità tra loro molto diverse (_scarsa separazione delle responsabilità_) e decisamente infernale da leggere, gestire e debuggare in caso di errori.
-
-Introduciamo dunque il pattern __Decorator__, la soluzione più universalmente riconosciuta per questo tipo di situazioni.
-
-{% plantuml style="width: 80%" %}
-    class Client 
-    interface Component << interface >> {
-        + {abstract} sampleOperation()
-    }
-    Client .> Component
-    class BaseComponent implements Component {
-        + sampleOperation()
-    }
-    abstract class Decorator implements Component {
-        + Decorator(Component)
-    }
-    Decorator "0..1" o--> "1" Component
-    class ConcreteDecorator implements Decorator {
-        + ConcreteDecorator(Component)
-        + sampleOperation()
-    }
-
-    hide empty fields
-{% endplantuml %}
-
-A prima vista lo schema UML ricorda molto quello del pattern Composite: abbiamo un'interfaccia _Component_ implementata sia da un _ConcreteComponent_, ovvero una base della pizza nel nostro esempio, sia da una __classe astratta Decorator__, la quale è poi estesa da una serie di _ConcreteDecorator_.
-A differenza del Composite, tuttavia, qui ciascun Decorator aggrega __una e una sola istanza di Component__: tali decoratori sono infatti dei "wrapper", degli oggetti che _ricoprono_ altri per aumentarne dinamicamente le funzionalità. \
-È importante notare che i Decorator ricevono come oggetto da ricoprire al momento della costruzione un _generico Component_, in quanto questo permette ai decoratori di decorare oggetti già decorati.
-Questo approccio "ricorsivo" permette di creare una catena di decoratori che definisca a runtime in modo semplice e pulito oggetti dotati di moltissime funzionalità aggiunte.
-I decoratori esporranno infatti i metodi definiti dall'interfaccia __delegando__ al Component contenuto l'esecuzione del comportamento principale e aggiungendo la propria funzionalità a posteriori: in questo modo la "base" concreta eseguirà il proprio metodo che verrà successivamente arricchito dai decoratori in maniera del tutto trasparente al Client.
-
-```java
-public interface Pizza { String toString(); }
-
-public class BaseNormale implements Pizza {
-    public String toString() { 
-        return "Io sono una pizza con: base normale"; 
-    }
-}
-
-public class BaseIntegrale implements Pizza {
-    public String toString() { 
-        return "Io sono una pizza con: base integrale"; 
-    }
-}
-
-public abstract class IngredienteDecorator implements Pizza {
-    private Pizza base;
-
-    public IngredienteDecorator(Pizza base) { this.base = base; }
-
-    public String toString() {
-        return base.toString();
-    }
-}
-
-public class IngredienteSalame extends IngredienteDecorator {
-    public IngredienteSalame(Pizza base) { super(base); }
-
-    @Override
-    public String toString() { return super.toString() + ", salame"; }
-}
-
-public class IngredientePeperoni extends IngredienteDecorator {
-    public IngredientePeperoni(Pizza base) { super(base); }
-
-    @Override
-    public String toString() { return super.toString() + ", peperoni"; }
-}
-```
-
-```java
-public class Client {
-    public static void Main() {
-        // Voglio una pizza con salame, peperoni e base integrale
-        Pizza salamePeperoni = 
-            new IngredientePeperoni(
-                new IngredienteSalame(
-                    new BaseIntegrale()
-                )
-            );
-    }
-}
-
-```
-
-Vista la somiglianza, inoltre, pattern Decorator e Composite sono facilmente combinabili: si può per esempio immaginare di creare gruppi di oggetti decorati o decorare in un solo colpo gruppi di oggetti semplicemente facendo in modo che Composite, Decorator e classi concrete condividano la stessa interfaccia Component.
-
-Possiamo poi notare una cosa: al momento della costruzione un Decorator salva al proprio interno l'istanza del Component da decorare.
-Come sappiamo questo darebbe luogo ad un'_escaping reference_, ma in questo caso il comportamento è assolutamente voluto: dovendo decorare un oggetto è infatti sensato pensare che a quest'ultimo debba essere lasciata la possibilità di cambiare e che debba essere il decoratore ad adattarsi a tale cambiamento.
-
-
-È interessante poi osservare la classe astratta Decorator: in essa viene infatti inserita tutta la logica di composizione, permettendo così di creare nuovi decoratori con estrema facilità.
-Spesso, inoltre, se i decoratori condividono una certa parte di funzionalità aggiunte queste vengono anch'esse estratte nella classe astratta creando invece un metodo vuoto protetto che i decoratori reimplementeranno per operare la loro funzionalità aggiuntiva.
-
-```java
-public abstract class IngredienteDecorator implements Pizza {
-    private Pizza base;
-
-    public IngredienteDecorator(Pizza base) { this.base = base; }
-
-    public String toString() {
-        return base.toString() + nomeIngrediente();
-    }
-
-    protected String nomeIngrediente() { return ""; }
-}
-
-public class IngredienteSalame extends IngredienteDecorator {
-    public IngredienteSalame(Pizza base) {super(base);}
-
-    @Override
-    public String nomeIngrediente() { return ", salame"; }
-}
-
-public class IngredientePeperoni extends IngredienteDecorator {
-    public IngredientePeperoni(Pizza base) {super(base);}
-
-    @Override
-    public String nomeIngrediente() { return ", peperoni"; }
-}
-```
-
-Si noti come l'uso della visibilità `protected` renda l'override del metodo possibile anche al di fuori del package, aumentando così la facilità di aggiunta di nuovi decoratori.
-
-Volendo vedere un esempio concreto di utilizzo di questo pattern è sufficiente guardare alla libreria standard di Java: in essa infatti gli `InputStream` sono realizzati seguendo tale schema.
-
-## <big>S</big>TATE
-
-Come sappiamo, le macchine a stati finiti sono uno dei fondamenti teorici dell'informatica: si tratta di oggetti matematici che modellano sistemi in grado di evolvere, ovvero il cui __comportamento varia in base allo stato__ in cui si trovano.
-
-Volendo rappresentare un oggetto di questo tipo la prima idea potrebbe essere quella di realizzare il cambio di comportamento con una serie di `if` e `switch`, un approccio che come abbiamo già visto numerose volte diventa presto difficilmente sostenibile. \
-In alternativa ad esso si introduce invece lo __State pattern__ che mantenendo l'astrazione delle macchine a stati finiti permette di modellare facilmente il cambiamento di comportamento di un oggetto al modificarsi dello stato.
-Si noti che rimanendo legato al concetto di automa a stati finiti uno dei punti di forza di questo pattern è la semplicità di apportare delle modifiche al codice quando le specifiche di ciò che è stato modellato tramite una macchina a stati finiti cambiano.
-
-Un esempio di utilizzo di questo pattern potrebbe essere un software di editing di foto, in cui l'utente ha a disposizione una toolbar con diversi strumenti che gli permettono di compiere operazioni diverse sullo stesso piano di lavoro (_comportamenti diversi dell'azione "tasto sinistro sullo schermo" in base al tool selezionato_).
-
-{% plantuml style="width: 100%" %}
-class Context {
-    - state: State
-    + sampleOperation()
-    + setState(State)
-}
-interface State << interface >> {
-    + {abstract} sampleOperation(Context)
-}
-class ConcreteState implements State {
-    + sampleOperation(Context)
-}
-Context <..  ConcreteState
-Context "   1" o-> State
-hide empty fields
-note left of Context::"sampleOperation()"
-this.state.sampleOperation(this)
-end note
-{% endplantuml %}
-
-In un automa a stati finiti le componenti fondamentali sono tre:
-
-- gli _stati_, tra cui si distingue lo _stato corrente_;
-- le _azioni_ che si possono intraprendere in qualunque stato;
-- le _transizioni_ da uno stato all'altro come effetto ulteriore di un'azione (_es. vim che con 'i' entra in modalità inserimento se era in modalità controllo_).
-
-Come si vede dallo schema UML, il pattern State cerca di modellare ciascuna di queste componenti: un'__interfaccia State__ raggruppa la definizione di tutte le _azioni_, rappresentate da metodi, mentre __una classe concreta per ogni stato__ definisce che effetto hanno tali azioni quando ci si trova al suo interno con l'implementazione dei suddetti metodi. \
-Infine, una classe __Context__ contiene un riferimento ad uno stato che rappresenta lo _stato corrente_ e delega ad esso la risposta alle azioni (che possono essere viste come degli "eventi"); essa espone inoltre un metodo `setState(State)` che permette di modificare lo stato corrente.
-
-```java
-public class Context {
-    private State state;
-
-    public void setState(@NotNull State s) {
-        state = s;
-    }
-
-    public void sampleOperation() {
-        state.sampleOperation(this)
-    }
-}
-```
-
-Rimane dunque solo da definire come si realizzano le _transizioni_ di stato: chi ha la responsabilità di cambiare lo stato corrente?
-Esistono due diversi approcci, ciascuno dei quali presenta delle criticità:
-
-- __gli State realizzano le transizioni__: volendo rimanere aderenti al modello degli automi a stati finiti, possiamo permettere che gli stati concreti chiamino il metodo `setState` del Context all'interno della loro implementazione dei metodi se come effetto di un'azione lo stato corrente cambia.
-Tuttavia, poiché `setState` chiede in input lo stato a cui transizionare questo approccio richiede che _gli stati si conoscano tra di loro_: si introduce così una _dipendenza tra stati_ non chiaramente visibile nello schema UML e si ha uno _sparpagliamento della conoscenza_ sulle transizioni che rende questo metodo un po' "sporco".
-
-- __il Context realizza le transizioni__: con questa seconda strategia è compito del contesto eseguire le transizioni di stato, evitando così che gli stati si debbano conoscere; l'unico depositaria della conoscenza sulle transizioni è la classe Context.
-Ciascuna azione viene dunque intrapresa in due step: il Context richiama il corrispondente metodo dello stato corrente e successivamente ne __intercetta il risultato__; può dunque decidere tramite esso se cambiare stato e eventualmente a quale stato transizionare. \
-Si tratta tuttavia di un ritorno al _table-driven design_ fatto di `if` e `switch` da cui ci eravamo voluti allontanare: come in quel caso, l'approccio risulta fattibile soltanto finché ci sono poche possibili transizioni.
-Inoltre, se una transizione non dipende dal risultato di un'azione ma da _come_ questa è stata eseguita questo approccio è totalmente impossibile in quanto tale tipo di conoscenza non è presente nella classe Context.
-
-Per via delle difficoltà poste dal secondo approccio si sceglie spesso di effettuare le transizioni all'interno degli stati: questo permette di rendere esplicito e atomico il passaggio di stato. \
-A tal proposito, è interessante notare come le istanze degli stati concreti non posseggano alcuna informazione di stato in quanto il Context a cui si riferiscono viene passato loro al momento della chiamata dei rispettivi metodi: al di là della loro identità essi sono completamente __stateless__.
-Si tratta di un approccio molto utile in caso si debbano modellare più macchine a stati finiti dello stesso tipo, in quanto l'assenza di stato rende le stesse istanze degli stati concreti __condivisibili__ tra diversi Context, in una sorta di pattern Singleton.
-
-Volendo trovare ulteriori analogie con altri pattern, il pattern State ricorda nello schema il pattern Strategy: la differenza sta però nel fatto che i diversi stati concreti sono a conoscenza l'uno dell'altro, mentre le strategie erano tra di loro completamente indipendenti.
-
-## <big>F</big>ACTORY METHOD
-
-Talvolta capita che un certo Client sia interessato a creare un oggetto non in base al suo tipo quanto all'_interfaccia_ che esso implementa: ad esso non importa conoscere la classe di cui l'oggetto è un'istanza perché essa non ha alcuna rilevanza nel suo contesto.
-Tuttavia, la normale creazione di un oggetto tramite la keyword `new` richiede di esplicitare la classe a cui esso appartiene, costringendo così il Client ad approfondire inutilmente la sua conoscenza sui tipi che implementano l'interfaccia a cui è interessato.
-
-Per evitare questo tipo di situazione introduciamo uno dei cosiddetti __pattern creazionali__, ovvero legati alla creazione di oggetti: stiamo parlando del pattern dei __Factory methods__.
-Esso definisce una classe astratta _Creator_ dotata di _metodi fabbrica_ astratti che restituiscono un'istanza di un tipo aderente all'interfaccia _Product_ a cui il Client è interessato: a quale classe appartenga effettivamente tale istanza (_Product concreto_) è però lasciato ad un _Creator concreto_ tra i tanti che estendono la classe astratta; idealmente dovrebbe esserci un creatore concreto per ogni tipo di prodotto concreto che implementa l'interfaccia Product.
-
-{% plantuml style="width: 100%" %}
-interface Product << interface >> {
-}
-abstract class Creator {
-    + {abstract} factoryMethod()
-    + anOperation()
-}
-class ConcreteCreator implements Creator {
-    + factoryMethod()
-}
-class ConcreteProduct implements Product {
-}
-ConcreteProduct <. ConcreteCreator
-hide empty fields
-{% endplantuml %}
-
-Questo pattern definisce dunque un'__interfaccia per creare un Product ma lascia al Creator concreto la scelta di cosa creare effettivamente__: in questo modo all'interno della classe astratta Creator è possibile scrivere dei metodi che richiedono la creazione di un Product pur senza sapere di preciso il tipo dell'oggetto che verrà creato, in quanto questo sarà determinato dall'implementazione di `factoryMethod` del creatore concreto.
-Si sfruttano dunque al massimo grado __polimorfismo__ e __collegamento dinamico__, in quanto il tipo dell'oggetto da creare viene deciso a runtime: poiché nemmeno il Creator conosce il tipo concreto dei Product creati risulta dunque subito chiaro perché i factory methods non possano essere metodi statici di tale classe. \
-I factory methods rappresentano un esempio dell'utilità delle astrazioni permesse dai linguaggi ad oggetti: in un contesto in cui normalmente non è possibile fare overriding, come un costruttore, la soluzione è quella di virtualizzare il tutto con la creazione di metodi che possono essere esportati in classi concrete.
-Per questo motivo i factory method vengono talvolta detti anche _virtual constructors_, "costruttori virtuali".
-
-Per capire meglio il funzionamento del pattern, vediamo un esempio di come esso può essere utilizzato.
-Consideriamo un software capace di aprire contemporaneamente più documenti di tipo differente in diverse pagine, come per esempio Microsoft Word o Excel: al loro interno, quando viene creato un nuovo file vengono fatte una serie di operazioni generiche (creare la nuova pagina, mostrare vari popup...), ma ad un certo punto è necessario creare un oggetto che rappresenti il nuovo documento e il cui tipo dipende dunque dal documento creato.
-Il codice di creazione del nuovo oggetto `Documento` non può dunque trovarsi in un metodo della classe astratta `Application` (Creator) insieme con il resto delle operazioni generiche in quanto specifico della tipologia di documento creato: è dunque necessario virtualizzare la creazione dell'oggetto in un metodo `createDocument()` implementato da una serie di sottoclassi concrete `MyApplication` (ConcreteCreator) ciascuna specifica per un tipo di documento.
-
-{% plantuml style="width: 100%" %}
-interface Document << interface >> {
-    {abstract} Open()
-    {abstract} Close()
-    Save()
-    Revers()
-}
-abstract class Application {
-    + {abstract} CreateDocument()
-    + NewDocument()
-    + OpenDocument()
-}
-class MyApplication implements Application {
-    + CreateDocument()
-}
-class MyDocument implements Document {
-}
-MyDocument <. MyApplication
-Document <-o "     docs" Application
-note right of Application::"NewDocument()"
-Document doc = new CreateDocument();
-docs.add(doc)
-doc.Open()
-end note
-
-note right of MyApplication::"CreateDocument()"
-return new MyDocument
-end note
-hide empty fields
-{% endplantuml %}
-
-## <big>A</big>BSTRACT FACTORY
-
-Vediamo ora una generalizzazione del Factory method pattern che si utilizza quando, al posto di creare un solo oggetto aderente ad un'interfaccia, è necessario creare _più oggetti aderenti a varie interfacce i cui tipi concreti siano però compatibili tra di loro_.
-
-Immaginiamo per esempio di aver progettato un'applicazione cross-platform e di doverne creare la User Interface: essa dovrà avere stili diversi in base al sistema operativo sui cui si sta eseguendo.
-Non conoscendo su quale os si starà operando, il resto dell'applicazione gestirà gli elementi dell'UI tramite delle opportune interfacce che nascondano il tipo concreto delle istanze, il quale determinerà lo stile con cui esse verranno rappresentate: sarà però fondamentale che _tutti gli elementi dell'UI condividano lo stesso stile_ in modo da non creare un'orrendo arlecchino.
-
-Ecco dunque che introduciamo il pattern delle __Abstract Factory__, un metodo in grado di fornire un'__interfaccia per creare famiglie di oggetti compatibili tra loro senza specificare la loro classe concreta__ così da garantire una certa __omogeneità__ all'insieme. \
-Per fare ciò il pattern propone di creare un'interfaccia _AbstractFactory_ contenente la definizione di un factory method per ogni tipo di prodotto astratto (_Product_) e una serie di _ConcreteFactory_ che restituiranno dei _ConcreteProduct_ in uno specifico stile: in questo modo, interagendo con una Factory concreta un Client potrà ottenere in modo a lui trasparente una serie di prodotti concreti coerenti in stile tra di loro.
-
-{% plantuml style="width: 100%" %}
-class client {
-}
-interface AbstractFactory {
-    + createProductA()
-    + createProductA()
-}
-interface AbstractProduct <<interface>> {
-}
-client --> AbstractFactory
-client --> AbstractProduct
-class ConcreteFactory implements AbstractFactory {
-    + createProductA()
-    + createProductA()
-}
-class ConcreteProduct implements AbstractProduct {
-}
-hide empty fields
-{% endplantuml %}
-
-Tornando al problema della User Interface, volendo sfruttare l'Abstract Factory pattern dobbiamo creare un'interfaccia `GUIFactory` che contenga la dichiarazione di due metodi creazionali, `createButton()` e `createCheckbox()`: questi permetteranno al client di creare un bottone e una checkbox nello stile specificato dalla classe concreta della factory; per ciascuno di tali elementi dell'UI dobbiamo dunque creare un'interfaccia prodotto, ovvero rispettivamente le interfacce `Button` e `Checkbox`.
-All'interno delle classi factory concrete tali metodi creazionali restituiranno però dei prodotti concreti nello stile specifico della factory da cui sono prodotti: così, per esempio, una `MacFactory` (per lo stile di MacOs) creerà `MacButton` e `MacCheckbox`, mentre una `WinFactory` (per lo stile di Windows) creerà `WindowsButton` e `WinCheckbox`. \
-In questo modo la nostra applicazione dovrà possedere al suo interno unicamente un riferimento alla factory adatta al sistema operativo su cui sta girando e potrà creare tramite essa tutti gli elementi di UI di cui avrà bisogno senza preoccuparsi di specificare ogni volta lo stile: la factory concreta glielo restituirà sempre nello stile selezionato inizialmente.
-
-{% responsive_image path: assets/09_esempio-abstract-factory.png %}
-
-## <big>M</big>ODEL VIEW CONTROLLER
-
-Spesso nelle applicazioni capita che uno stesso dato sia riportato tramite diverse __viste__ all'interno dell'interfaccia utente: il colore di un testo, per esempio, potrebbe essere rappresentato contemporaneamente da una terna di valori RGB, dal suo valore esadecimale e da uno slider di colori.
-Si tratta di modi differenti di rappresentare la medesima __informazione condivisa__, che viene replicata più volte per dare all'utente diversi modi in cui visualizzarla. \
-La condivisione di un medesimo valore porta però con sé un problema: se tale dato viene modificato dall'utente interagendo con una delle viste è necessario che tale _modifica venga propagata a tutte le altre viste_ in modo da mantenere l'informazione __coerente__.
-
-Abbiamo dunque bisogno di un framework che ci permetta di mantenere un'informazione condivisa in modo efficiente e pulito e che permetta di rappresentarla facilmente sotto diversi punti di vista: l'invitante soluzione di fare semplicemente sì che le viste comunichino direttamente i cambiamenti del dato l'una con l'altra si rivela infatti velocemente impraticabile.
-Il pattern __Model View Controller__ (MVC) propone invece di suddividere la gestione del dato e dell'interazione con l'utente in tre tipologie di classi:
-
-- __Model__: un'unica classe contenente lo __stato condiviso__; si tratta dell'unico depositario dell'informazione con cui tutte le viste dovranno comunicare per aggiornare i dati mostrati.
-- __View__: una serie di classi che costituiscono l'__interfaccia con l'utente__; esse mostrano il dato secondo il loro specifico punto di vista e permettono all'utente di interagire con l'applicazione.
-- __Controller__: ciascuna vista possiede infine una classe di controllo collegata che si occupa della __logica dell'applicazione__; ogni volta che l'utente interagisce con una vista tale interazione viene passata al relativo Controller, che si occuperà di rispondere all'input eventualmente modificando lo stato condiviso nel Model.
-
-Abbiamo dunque una suddivisione dell'applicazione in tre tipi di componenti differenti che cooperano tra di loro senza però essere _strettamente_ dipendenti l'uno dall'altro.
-Un tipico ciclo di interazione tra le tre componenti funziona infatti come mostrato in figura: 
-
-1. Una View riceve un'interazione da parte dell'utente e comunica tale evento al proprio Controller;
-2. Il Controller gestisce l'interazione e se essa richiede un cambiamento dello stato comune chiede al Model di modificare il proprio contenuto;
-3. Come ulteriore passaggio, il Controller aggiorna il dato mostrato dalla View ad esso associata prima ancora che il modello sia cambiato;
-4. Ricevuta la richiesta, il Model aggiorna l'informazione condivisa e notifica _tutte_ le View del cambiamento: in questo modo esso non avrà effetto solo nella vista che ha ricevuto l'input dell'utente ma in tutte;
-5. Le View ricevono la comunicazione del fatto che il Model è cambiato e aggiornano la propria informazione mostrata recuperando il dato aggiornato dal modello (politica _pull_).
-
-{% responsive_image path: assets/09_model-view-controller.png %}
-
-Questo modello di interazione circolare permette di separare l'interfaccia utente (_view_) dall'interfaccia dello stato comune (_model_) e dalla logica del cambiamento di stato (_controller_): grazie alla mediazione del Controller le View non hanno bisogno di conoscere direttamente la struttura dei dati contenuti nel Model, cosa che ci permette di riutilizzare le stesse View, e dunque le stesse interfacce utente, per dati diversi (es. una casella di testo è una View e non dipende dal dato che ci si inserisce). \
-È inoltre interessante notare come un Controller potrebbe voler comunicare dei _cambiamenti virtuali_ alla View da cui è partito un input prima ancora che al Model venga chiesta un eventuale modifica dello stato.
-Nel caso ci siano errori nell'input inserito dall'utente, infatti, esso va informato in qualche modo: il Controller non cambierà dunque lo stato condiviso ma solo lo stato dalla relativa View in modo da mostrare un qualche messaggio d'errore.
-Similmente, se i dati inseriti sono già presenti nel Model (cosa che il Controller non può sapere a priori) quest'ultimo potrebbe avvisare il Controller di tale evenienza al momento della richiesta di cambiamento: esso dovrà dunque nuovamente notificare l'utente che l'inserimento dei dati non è andato a buon fine aggiornando la propria View.
-
-Portiamo ora attenzione su un altro aspetto: nell'insieme dei meccanismi che realizzano il pattern Model View Controller si possono riscontrare una serie di altri pattern che abbiamo già trattato.
-Per agevolare la comprensione del funzionamento di questo nuovo "mega-pattern", vediamo quindi quali sono i pattern utilizzati al suo interno:
-
-- __Observer__, poiché _le View sono Observer del Model_: ogni vista si registra come Observer del modello in modo che il Model, in pieno stile Observable, le notifichi dei suoi cambiamenti di stato.
-Spesso la strategia di aggiornamento delle viste è qui quella __pull__, ovvero quella secondo cui agli Observer viene passato un riferimento all'oggetto Observable in modo che siano loro stessi a recuperare i dati di cui hanno bisogno tramite opportuni metodi getter: questo permette infatti di memorizzare nello stesso Model i dati di diverse View. \
-Va inoltre fatto notare che se l'interfaccia esposta dalle View è un'_interfaccia a eventi_, come per esempio un'interfaccia grafica (es. un click sullo schermo genera un evento), _anche la comunicazione tra View e Controller può avvenire tramite il pattern Observer_: ciascun Controller si registra infatti come Observer degli eventi che avvengono sulla View.
-- __Strategy__, poiché _i Controller sono Strategy per le View_: poiché ad ogni vista è collegato uno e un solo Controller che regola come la vista reagisca agli input dell'utente, i Controller possono essere visti come strategie di gestione degli eventi generati dalle viste.
-Poiché le viste sono componenti sostanzialmente "stupidi" che risolvono le interazioni dell'utente delegando al proprio Controller la loro gestione, questo approccio permette per esempio di gestire viste identiche in modi diversi semplicemente cambiando il Controller ad esse associato: così, per esempio, è possibile rendere una casella di testo read-only oppure modificabile senza modificare in alcun modo la classe della relativa vista e rispettando così l'Open-Close Principle.
-- __Composite__, poiché _le View sono spesso composte da più Component_: quando le View rappresentano interfacce grafiche (GUI) esse sono spesso realizzate componendo diversi elementi tra di loro (es. aree di testo, bottoni, etc...).
-Per questo motivo è spesso prevalente il pattern Composite nella loro implementazione, utile specialmente per quanto riguarda la creazione su schermo dell'interfaccia, che viene disegnata pezzo per pezzo.
-
-In conclusione, il Model è in grado di interagire con tutte le viste che l'osservano tramite un unico comando (_update_), mentre le View comunicano con il Model passando attraverso il Controller, che fa da una sorta di "Adapter" tra i due.
-Questo permette allo stesso dato di avere interfacce disomogenee senza alcun tipo di problema riguardante la coerenza dello stesso.
-
-Tuttavia, il problema principale del pattern Model View Controller è la _dipendenza circolare_ tra le tre componenti: le view comunicano ai rispettivi controller gli eventi, questi li elaborano e aggiornano il modello il quale a sua volta avvisa le view dei cambiamenti di stato.
-Questa struttura fortemente interconnessa rende difficoltoso lo sviluppo e il testing in quanto non esiste un chiaro punto da cui partire a costruire: si potrebbe pensare di fare mocking delle view e iniziare a sviluppare il resto, ma questo approccio porta comunque a una serie di inutili complicazioni; bisogna inoltre considerare che il testing delle view è spesso particolarmente complesso coinvolgendo varie funzioni di libreria o funzioni grafiche. \
-Come vedremo nel prossimo paragrafo, per ovviare a questo problema si decide spesso di spezzare il circolo vizioso di Model, View e Controller modificando lievemente le rispettive dipendenze.
-
-## <big>M</big>ODEL VIEW PRESENTER
-
-Come preannunciato esiste una variante del Model View Controller chiamata __Model View Presenter__ che fornisce una soluzione al problema del testing delle viste e delle relative interfacce grafiche.
-Questo nuovo pattern eleva il ruolo del Controller, ora chiamato _Presenter_, a completo _intermediario tra View e Model_ in entrambi i sensi di comunicazione: non solo dunque le View delegano ai rispettivi Presenter la gestione delle interazioni con l'utente, ma al momento del cambiamento dell'informazione condivisa il Model notifica non direttamente le viste ma i Presenter stessi, i quali avranno dunque il compito di aggiornare la propria View per mostrare il dato modificato. 
-
-{% responsive_image path: assets/09_model-view-presenter.png %}
-
-Model e View perdono dunque alcun legame diretto, facendo apparire sempre più i Presenter come Adapter tra stato concreto (_model_) e stato virtuale mostrato all'utente (_view_).
-La rottura di tale legame facilita il testing delle viste poiché invece di verificare che una vista e la rispettiva controparte grafica abbiano ricevuto e processato correttamente un aggiornamento del dato da parte del Model è sufficiente verificare che un update del Model provochi nei Presenter un aggiornamento del dato mostrato dalla propria View: siamo dunque riusciti a __isolare l'interfaccia logica da quella grafica__, rendendo più semplice il testing di entrambe e sfoggiando un esempio importante del cosiddetto _design for testing_.
-
-In ultimo, utilizzando questo pattern è importante fare attenzione di mantenere segreta la rappresentazione interna del Model ai Presenter e viceversa, evitando in particolar modo eventuali _escaping reference_: la separazione delle responsabilità costruita con la suddivisione dei dati dalla loro logica di gestione perderebbe infatti alcuna valenza se si legassero troppo strettamente Model e Presenter.
-
-## <big>B</big>UILDER
-
-Può talvolta capitare che l'inizializzazione di un'istanza di una classe richieda un numero molto grande di _parametri_, alcuni dei quali _obbligatori_ e altri _facoltativi_.
-Come si realizzano i costruttori della classe in questo tipo di situazioni?
-
-#### Telescoping constructor pattern
-
-L'approccio più immediato a questo problema è quello dei __costruttori telescopici__ (_telescoping constructor pattern_): all'interno della classe si realizza _un costruttore completo_ che richiede tutti i parametri e una serie di _costruttori secondari_ che invece prendono i parametri obbligatori e _diverse combinazioni dei parametri opzionali_, rimappando poi spesso la propria esecuzione sul costruttore completo tramite l'assegnamento di valori di default ai parametri non ricevuti.
-
-```java
-public class MyClass {
-    private final T0 optionalField1;
-    private final T1 mandatoryField;
-    private final T2 optionalField2;
-
-    public MyClass(T1 mf) {
-        this(defaultValue1, mf, defaultValue2);
-    }
-
-    public MyClass(T1 mf, T0 of) {
-        this(of, mf, defaultValue2);
-    }
-
-    public MyClass(T1 mf, T2 of) {
-        this(defaultValue1, mf, of);
-    }
-
-    public MyClass(T1 mf, T0 of1, T2 of2) {
-        this.optionalField1 = of1;
-        this.optionalField2 = of2;
-        this.mandatoryField = mf;
-    }
-}
-```
-
-Questa tecnica si rivela però presto molto poco funzionale: innanzitutto il numero di costruttori da realizzare cresce esponenzialmente nel numero di parametri opzionali, rendendo la classe estremamente confusionaria. \
-Sorgono inoltre dei problemi nel caso di _parametri opzionali dello stesso tipo_, in quanto è impossibile disambiguare tra di essi al momento della definizione dei costruttori: con due parametri opzionali dello stesso tipo, per esempio, non sarebbe possibile distinguere il costruttore che assegni il primo ma non il secondo e viceversa (si noti come non si può nemmeno distinguere tramite il nome del costruttore in quanto questo deve necessariamente essere lo stesso della classe).
-Se linguaggi come Python risolvono questo problema imponendo che il chiamante di un costruttore espliciti il nome del parametro opzionale che sta assegnando, questo tipo di meccanismo non esiste in Java: ciò ci costringerebbe quindi a far sì che nei costruttori vengano passati o tutti i parametri dello stesso tipo o nessuno di essi.
-
-#### JavaBeans pattern
-
-Per risolvere i problemi appena visti la prossima soluzione che viene in mente è dunque quella di fornire un _unico costruttore_ che prenda in input _solamente i parametri obbligatori_ e creare poi una serie di _setter per i parametri opzionali_: si tratta del cosiddetto __pattern JavaBeans__.
-
-```java
-public class MyClass {
-    private T0 optionalField1;
-    private T1 mandatoryField;
-    private T2 optionalField2;
-
-    public MyClass(T1 mf) {
-        this.mandatoryField = mf;
-    }
-
-    public void setOptionalField1(T0 of) {
-        this.optionalField1 = of;
-    }
-
-    public void setOptionalField2(T2 of) {
-        this.optionalField2 = of;
-    }
-}
-```
-
-Anche questo approccio presenta tuttavia delle notevoli difficoltà.
-In primo luogo, un oggetto costruito con il pattern JavaBeans _non può essere immutabile_ in quanto richiede la presenza di setter per i propri attributi opzionali (che dunque non possono essere `final`): possiamo dunque creare solo oggetti mutabili. \
-Un problema forse più grave è inoltre che questo pattern ammette la presenza di _momenti nella vita di un oggetto in cui esso non è stato ancora costruito completamente_: tra la creazione e l'assegnamento tramite setter dei parametri opzionali, infatti, l'istanza si trova in uno stato non finito e dunque non consistente che potrebbe creare numerosi problemi in sistemi di tipo concorrente o multi-thread.
-
-### Builder pattern
-
-Gli autori del libro Effective Java propongono dunque un nuovo pattern che prende gli aspetti migliori della prima e della seconda soluzione finora proposta risolvendo al tempo stesso i problemi di entrambe: essa permetterà infatti di creare oggetti immutabili (rendendo gli attributi `final`) e di assegnare solo alcuni dei parametri opzionali senza generare problemi di inconsistenza o di sovrapposizione dei tipi degli attributi.
-Questo pattern creazionale prende il nome di __Builder__.
-
-{% plantuml style="width: 100%" %}
-class MyClass {
-    - OptionalField : T0
-    - MandatoryField : T1
-
-    - MyClass(Builder)
-}
-class MyClass.Builder {
-    + Builder(T1)
-    + withOptionalField(T0) : Builder
-    + build() : MyClass
-}
-MyClass +--> MyClass.Builder
-hide empty fields
-{% endplantuml %}
-
-Data una classe da costruire `MyClass` avente parametri obbligatori e opzionali il primo passo è quello di rendere __privato__ il suo costruttore, il quale prenderà in input non più una lista di parametri ma un'istanza di una __nuova classe `Builder`__.
-Tale classe viene definita come una _classe statica, pubblica e interna_ a `MyClass`, con la quale condivide il tipo e il numero di attributi obbligatori e opzionali (questi ultimi subito inizializzati al loro valore di default). \
-Seguendo il pattern JavaBeans, la classe Builder esporrà un costruttore pubblico contenente solo i parametri obbligatori e una serie di setter per i parametri opzionali.
-Ma a che pro costruire un oggetto della classe Builder quando quella che volevamo ottenere era un'istanza di `MyClass`?
-La risposta sta nella definizione __metodo `build()`__: tramite esso, il Builder restituirà un'istanza di MyClass inizializzata con propri i parametri obbligatori e opzionali; essendo una classe interna, infatti, il Builder sarà l'unico in grado di accedere al costruttore privato di `MyClass`.
-
-```java
-public class MyClass {
-    private final T0 optionalField1;
-    private final T1 mandatoryField;
-    private final T2 optionalField2;
-
-    private MyClass(Builder builder) {
-        mandatoryField = builder.mandatoryField;
-        optionalField1 = builder.optionalField1;
-        optionalField2 = builder.optionalField2;
-    }
-
-    public static class Builder {
-        private T1 mandatoryField;
-        private T0 optionalField1 = defaultValue1;
-        private T2 optionalField2 = defaultValue2;
-
-        public Builder(T1 mf) {
-            mandatoryField = mf;
-        }
-
-        public Builder withOptionalField1(T0 of) {
-            optionalField1 = of;
-            return this;
-        }
-
-        public Builder withOptionalField2(T2 of) {
-            optionalField2 = of;
-            return this;
-        }
-
-        public MyClass build() {
-            return new MyClass(this);
-        }
-    }
-}
-```
-
-Questo pattern è particolarmente intelligente per una serie di motivi: innanzitutto, rendendo privato il costruttore di `MyClass` ci si assicura che le sue istanze siano costruite unicamente tramite il Builder.
-A tal proposito, il fatto che `Builder` sia una classe __statica__ è di non poca importanza: questo permette infatti di creare una sua istanza senza prima istanziare la classe che la contiene, cosa che come abbiamo visto sarebbe impossibile essendo il costruttore di MyClass privato.
-Per creare un'istanza di Builder è dunque sufficiente la seguente sintassi:
-
-```java
-MyClass.Builder = new MyClass.Builder(...);
-```
-
-Si potrebbe notare che essendo statica la classe Builder potrà accedere solamente agli elementi statici di `MyClass`, ma questo non costituisce un problema: come abbiamo visto, essa dovrà solamente richiamarne il costruttore, che per sua stessa natura è sempre statico. \
-È importante notare che non vale però il contrario: `MyClass`, una volta ricevuta un'istanza di Builder come parametro del costruttore, può benissimo accedere ai suoi campi privati e sfrutta questa possibilità per copiare i valori dei parametri obbligatori e opzionali che il Builder ha ricevuto all'interno dei propri attributi.
-Assegnando tali valori al momento della creazione, gli attributi di `MyClass` potranno quindi anche essere `final`, permettendo così la creazione di oggetti immutabili.
-
-Un altro particolare da sottolineare è che i setter degli attributi opzionali del Builder sono setter un po' "spuri", in quanto invece di non ritornare nulla _ritornano il Builder stesso_: questo permette infatti di concatenare più setter l'uno con l'altro ottenendo così una notazione più fluente.
-È possibile infatti creare inline un'istanza di Builder, settare direttamente i suoi parametri opzionali e infine richiamare il metodo `build()` per ottenere facilmente un'istanza di `MyClass`:
-
-```java
-MyClass inst = (new MyClass.Builder(mandatoryField).withOptionalField1(optionalField1)).build();
-```
-
-L'utilizzo di un Builder risolve inoltre eventuali problemi dovuti alla concorrenza: quando viene chiamato il metodo `build()` l'istanza di `MyClass` viene restituita già completa, ovvero con tutti i parametri obbligatori e opzionali al valore desiderato (o di default se nessun setter è stato chiamato).
-Abbiamo così eliminato la possibilità di inconsistenze e creazioni parziali delle istanze di `MyClass`.
diff --git a/_posts/2022-11-07-mocking.md b/_posts/2022-11-07-mocking.md
deleted file mode 100644
index bbedef265057b425cd0c3f2c1ca7ca594826f166..0000000000000000000000000000000000000000
--- a/_posts/2022-11-07-mocking.md
+++ /dev/null
@@ -1,660 +0,0 @@
----
-layout: post
-title: "[10] Mocking"
-date: 2022-11-07 14:30:00 +0200
-toc: true
----
-
-_Link consigliato dal prof [http://xunitpatterns.com](http://xunitpatterns.com/)_
-
-# Four-Phase Test
-
-Un aspetto importante da considerare durante la scrittura dei test è la chiarezza del loro _scopo_.
-
-Chiunque li legga deve essere in grado di determinare rapidamente quale comportamento si sta testando.
-Tuttavia, questo può risultare molto difficile se i test non sono strutturati in modo ottimale. 
-L'obiettivo di un test può essere molto confusionario se, ad esempio, vengono invocati senza alcun criterio diversi comportamenti del **system under test** (SUT, ciò che _sta venendo testato_), _e.g._ alcuni per impostare lo stato pre-test (fixture) del SUT, altri per utilizzare il SUT e altri ancora per verificare lo stato post-test del SUT.
-
-Un modo per rendere evidente ciò che si sta testando è strutturare ogni test in modo che abbia quattro fasi distinte, eseguite in sequenza:  
-1. **SET UP**: si inizializza tutto il necessario affinché il SUT _esibisca il comportamento atteso_ e il test, successivamente, sia in grado di _osservare il risultato effettivo_ (ad esempio, creare i vari Test Double).
-2. **EXERCISE**: si interagisce con il SUT, facendo dunque eseguire il codice che si vuole effettivamente testare.
-3. **VERIFY**: si fa tutto il necessario per determinare se il risultato atteso è stato ottenuto o meno (_e.g._ tramite asserzioni di vario tipo).
-4. **TEARDOWN**: fase di pulizia atta a riportare l'ambiente nello stato in cui è stato trovato.
-
-Per aumentare ulteriormente la leggibilità dei nostri test è desiderabile anche fare in modo che ogni metodo di test verifichi una e una sola funzionalità.
-Ciò non significa che un metodo di test che verifica più funzionalità sia scorretto, ma fornirà sicuramente una minore localizzazione delle anomalie rispetto a un gruppo di test che testano singole  funzionalità; in altre parole, sarà meno leggibile e contraddistinto da una logica più complessa.
-
-# Mocking and Test Double
-### Approcci al testing
-<style>
-td, tr, table {
-   border: none!important;
-}
-.border tr {
-   border-top: 1px solid black !important;
-   border-bottom: 1px solid black !important;
-   font-size: 0.9em !important;
-}
-.border td{
-   padding: 5px !important;
-}
-.row {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-  width: 100%;
-}
-
-.column {
-  display: flex;
-  flex-direction: column;
-  flex-basis: 0;
-flex-shrink: 2!important;
-  flex: 1;
-}
-.zoom .plantuml{
-  width: 95%!important;
-}
-.zoom1 .plantuml{
-  width: 70%!important;
-}
-
-</style>
-
-<div class="row">
-<div class="zoom1 column" markdown="1">
-
-{% plantuml %}
-
-hide footbox
-
-participant A
-activate A
-
-A -> SUT: method()
-activate SUT
-
-SUT --> A: result
-deactivate SUT
-deactivate A
-
-{% endplantuml %}
-
-</div>
-<div class="column zoom1" markdown="1">
-
-{% plantuml %}
-
-hide footbox
-
-participant A
-activate A
-
-A -> SUT: method()
-activate SUT
-deactivate SUT
-
-A -> SUT: getStatus()
-activate SUT
-
-SUT --> A: result
-deactivate SUT
-deactivate A
-
-{% endplantuml %}
-
-</div>
-<div class="column zoom" markdown="1">
-
-{% plantuml %}
-scale 200 height
-
-hide footbox
-
-participant A
-activate A
-
-A -> SUT: method()
-activate SUT
-
-SUT -> outputDOC: doOutput()
-activate outputDOC
-
-outputDOC --> SUT
-deactivate outputDOC
-
-SUT --> A
-deactivate SUT
-
-A -> outputDOC: getStatus()
-activate outputDOC
-
-outputDOC --> A: result
-deactivate outputDOC
-deactivate A
-
-{% endplantuml %}
-
-</div>
-<div class="column zoom" markdown="1" style="position: relative;">
-
-{% plantuml %}
-scale 200 height
-
-hide footbox
-
-participant A
-activate A
-
-A -> SUT: method()
-activate SUT
-
-SUT -> inputDOC: getInput()
-activate inputDOC
-
-inputDOC --> SUT: data
-deactivate inputDOC
-
-SUT --> A
-deactivate SUT
-
-A -> SUT: getStatus()
-activate SUT
-
-SUT --> A: result
-deactivate SUT
-deactivate A
-
-{% endplantuml %}
-
-</div>
-</div>
-
-Può risultare assai difficile testare un SUT che dipende da componenti software non utilizzabili per un motivo o per l'altro.
-Questi componenti prendono il nome di **depended-on component** (DOC) e i problemi che questi possono far emergere durante la stesura di un test sono molteplici.
-Ad esempio, i DOC potrebbero non essere disponibili in quel momento, non restituire i risultati che servono a un determinato test (o restituirli solo tramite artifici troppo complessi) oppure perché la loro esecuzione avrebbe effetti collaterali indesiderati.
-In altri casi ancora, la strategia di testing adottata richiede un maggiore controllo o più visibilità sul comportamento interno del SUT e l'utilizzo di un DOC reale rende l'operazione complessa.
-
-Quando si scrive un test in cui non si può (o si sceglie di non) usare il vero componente da cui si è dipendenti, si può sostituire quest'ultimo con un Test Double, durante la fase di set up.
-
-**Test Double** è un termine generico utilizzato per indicare un qualunque oggetto con cui si sostituisce un DOC reale a scopo di test.  
-Ovviamente, a seconda del tipo di test che si sta eseguendo, si può codificare diversamente il comportamento del Test Double.
-Non è necessario che questo si comporti come il DOC reale: il suo scopo è solo quello di fornire le stesse API in modo che la sua presenza risulti essere trasparente al SUT. 
-In altre parole, per il SUT interagire con il DOC reale o con il Test Double deve essere esattamente la stessa cosa.
-L'utilizzo di Test Double rende possibile la scrittura di test che precedentemente risultavano troppo articolati, complicati o dispersivi da realizzare.
-
-Il **mocking** è la tecnica di testing che ci permette di sostituire i DOC reali con i vari Test Double.
-Effettuare mocking permette di ottenere test più efficienti, affidabili e puliti, consentendo agli sviluppatori di isolare il SUT in un ambiente più controllato.
-
-Come si può osservare dall'immagine sottostante, vi sono diversi tipi di Test Double:  
-
-{% responsive_image path: 'assets/10_test-doubles.png' %}
-
-### Dummy Objects
-
-Nella maggior parte dei test è necessario fare in modo che il SUT si trovi in uno stato opportuno prima di quella che è la fase di exercise; a tale scopo, durante la fase di set up, vengono effettuate chiamate ad alcuni suoi metodi.
-Questi possono prendere come argomenti degli oggetti che, a volte, vengono solo memorizzati in variabili d'istanza e non sono di fatto mai utilizzati nel codice testato.
-É dunque necessario crearli unicamente per conformarsi alla firma di qualche metodo che si pianifica di chiamare nella fase di set up.
-La costruzione di questi oggetti può essere non banale e aggiunge una complessità del tutto superflua al test.
-
-In questi casi, si può passare come argomento un **dummy object**.
-Questi oggetti sono una forma degenere di Test Double in quanto esistono solo per poter essere passati da un metodo all'altro e non vengono mai realmente utilizzati.
-La loro utilità risiede nell'eliminare la necessità di costruire gli oggetti reali.
-
-Si noti che un dummy object non è la stessa cosa di un _null object_.
-Un dummy object non viene utilizzato dal SUT, quindi il suo comportamento è irrilevante.
-Al contrario, un null object viene utilizzato dal SUT, ma è progettato per non fare nulla o produrre un risultato sempre "innocuo".
-
-<table>
-<tbody>
-<tr>
-<td>Senza Mockito</td>
-<td>Con Mockito</td>
-</tr>
-<tr>
-<td>
-<div markdown="1">
-
-```java
-@Test 
-public void testDummy() {
-    MyClass dummy = ??;
-
-    List<MyClass> SUT = new ArrayList<MyClass>();
-    
-    SUT.add(dummy);
-
-    assertThat(SUT.size()).isEqualTo(1);
-}
-```
-
-</div>
-</td>
-<td>
-<div markdown="1">
-
-  ```java
-  @Test 
-  public void testDummy() {
-      MyClass dummy = mock(MyClass.class);
-      
-      List<MyClass> SUT = new ArrayList<MyClass>();
-      
-      SUT.add(dummy); 
-      
-      assertThat(SUT.size()).isEqualTo(1);
-  } 
-  ```
-</div>
-
-</td>
-</tr>
-</tbody>
-</table>
-
-
-### Stub Objects
-
-Altre volte risulta difficile testare il SUT perché il suo comportamento dipende dai cosiddetti _input indiretti_: valori restituiti da altri componenti software (DOC) con i quali interagisce.
-Gli **input indiretti** possono essere valori di ritorno dei metodi del DOC, parametri aggiornati, errori o eccezioni sollevate dal DOC.
-
-{% responsive_image path: 'assets/10_stub-object.png' %}
-
-In presenza di input indiretti la verifica del comportamento del SUT richiede di sostituire i DOC reali con Test Double che immettano gli input desiderati nel SUT.
-Test Double con questo scopo prendono il nome di **stub object**: sostituiscono un componente reale, da cui dipende il SUT, e forniscono risposte (input) "preconfezionate" alle sole chiamate fatte durante il testing.
-L'utilizzo di stub consente al test di forzare la realizzazione di determinati scenari particolari o di interesse specifico.
-
-<table>
-<tbody>
-<tr>
-<td>Senza Mockito</td>
-<td>Con Mockito</td>
-</tr>
-<tr>
-<td>
-<div markdown="1">
-
-```java
-@Test
-public void testConStub() {
-    MyClass stub = ??;
-    MyList<int> SUT = new MyList<int>();
-
-    SUT.add(stub.getValue(0));  // deve ritornare 4
-    SUT.add(stub.getValue(1));  // deve ritornare 7
-    SUT.add(stub.getValue(1));  // deve ritornare 3
-        
-    res = SUT.somma();
-    
-    assertThat(res).isEqualTo(14);
-}
-```
-
-</div>
-</td>
-<td>
-<div markdown="1">
-
-```java
-@Test
-public void testConStub() {
-    MyClass stub = mock(MyClass.class);
-    when(stub.getValue(0)).thenReturn(4);
-    when(stub.getValue(1)).thenReturn(7, 3);
-    
-    MyList<int> SUT = new MyList<int>();
-    SUT.add(stub.getValue(0));  // deve ritornare 4
-    SUT.add(stub.getValue(1));  // deve ritornare 7
-    SUT.add(stub.getValue(1));  // deve ritornare 3
-        
-    res = SUT.somma();
-    
-    assertThat(res).isEqualTo(14);
-}
-```
-
-</div>
-</td>
-</tr>
-</tbody>
-</table>
-
-### Mock Objects
-
-Il comportamento del SUT può includere azioni che non possono essere osservate attraverso la sua API pubblica, ma che sono osservate o sperimentate da altri sistemi o componenti dell'applicazione.
-Tali attività ricadono sotto il nome di **output indiretti** del SUT.
-Gli output indiretti possono includere chiamate a metodi di un altro componente, record inseriti in un database, record scritti su un file _etc_.
-
-{% responsive_image path: 'assets/10_mock-object.png' %}
-
-Testare il comportamento del SUT può voler dire anche verificare che gli output indiretti siano quelli corretti e a tale scopo servono punti di osservazione appropriati.
-Un **punto di osservazione** è un modo con cui il test può ispezionare lo stato post-exercise del SUT. 
-I punti di osservazione utili a verificare gli output indiretti sono costituiti da Test Double che prendono il nome di **mock object**.
-Questi intercettano gli output indiretti del SUT nella fase di exercise e permettono di confrontarli con gli output attesi in un momento successivo (_i.e._ la fase di verifying).
-
-Un mock object è dunque utilizzato per instrumentare e controllare le chiamate fatte dal SUT.
-In genere, l'oggetto Mock include anche la funzionalità di uno Stub; deve infatti essere in grado di restituire valori al SUT, anche se l'enfasi è posta sulla _verifica_ delle chiamate effettuate e non dal loro risultato.
-
-<table>
-<tbody>
-<tr>
-<td>Senza Mockito</td>
-<td>Con Mockito</td>
-</tr>
-<tr>
-<td>
-<div markdown="1">
-
-```java
-@Test
-public void testConMock() {
-    MyClass mock = ??;
-    
-    MyList<int> SUT = new MyList<int>();
-    
-    res = SUT.somma(mock);
-    
-    assertThat(res).isEqualTo(14);
-    // assert che getValue è stata chiamata 3 volte
-    // prima una volta con parametro 0 e poi...
-}
-```
-
-</div>
-</td>
-<td>
-<div markdown="1">
-
-```java
-@Test 
-public void testConMock() {
-     MyClass mock = mock(MyClass.class);
-     
-     when(mock.getValue(0)).thenReturn(4); 
-     when(mock.getValue(1)).thenReturn(7,3);
-
-     MyList<int> SUT = new MyList<int>();
-
-     res = SUT.somma(mock);
-     
-     assertThat(res).isEqualTo(14); 
-     InOrder io = inOrder(mock); 
-     io.verify(mock).getValue(0); 
-     io.verify(mock, times(2)).getValue(1);
-} 
-```
-
-</div>
-</td>
-</tr>
-</tbody>
-</table>
-
-### Spy objects
-
-{% responsive_image path: 'assets/10_spy-object.png' %}
-
-Un altro modo per implementare punti di osservazione che controllino e instrumentino le chiamate effettuate dal SUT su determinati DOC sono gli **spy object**.
-A differenza dei mock, questi sono costruiti a partire da oggetti reali.  
-Successivamente alla fase d'interazione con il SUT (exercise), durante la fase di verifica dei risultati (verify), il test confronta le chiamate effettuate dal SUT sul Test Spy con il comportamento desiderato (expected).
-
-<table>
-<tbody>
-<tr>
-<td>Senza Mockito</td>
-<td>Con Mockito</td>
-</tr>
-<tr>
-<td>
-<div markdown="1">
-
-```java
-@Test
-public void testConSpy() {
-        MyClass spy = ??;
-
-        MyList<int> SUT = new MyList<int>();
-
-        res = SUT.somma(spy);
-
-        assertThat(set).isEqualTo(14);
-        // assert che getValue è stata chiamata 3 volte
-        // prima una volta con parametro 0 e poi...    
-        }
-```
-
-</div>
-</td>
-<td>
-<div markdown="1">
-
-```java
-@Test 
-public void testConSpy() {
-     MyClass spy = spy(new MyClass());
-
-     MyList<int> SUT = new MyList<int>();
-
-     res = SUT.somma(spy);
-     
-     assertThat(res).isEqualTo(14); 
-     InOrder io = inOrder(spy); 
-     io.verify(spy).getValue(0); 
-     io.verify(spy, times(2)).getValue(1);
-} 
-```
-
-</div>
-</td>
-</tr>
-</tbody>
-</table>
-
-
-### Fake Objects
-
-{% responsive_image path: 'assets/10_fake-object.png' %}
-
-Un **fake object** è un oggetto reale che implementa a tutti gli effetti le funzionalità del DOC, ma per farlo impiega una qualche "scorciatoia" in una maniera che non risulterebbe applicabile ad un contesto di produzione _e.g._ database in memoria invece di un database reale, soluzioni inefficienti, parti di codice open source utilizzabili solo in fase di testing.
-
-### Riepilogo
-La tabella sottostante fornisce un riepilogo di ciò che rappresenta ciascuna variante dei Test Double.
-
-<table class="border">
-    <tr>
-        <td style="width: 15%!important"><b>Test Double</b></td>
-        <td style="width: 20%!important"><b>Purpose</b></td>
-        <td><b>Has behavior?</b></td>
-        <td style="width: 20%!important"><b>Injects Indirect <br>Inputs into SUT</b></td>
-        <td style="width: 20%!important"><b>Handles Indirect <br>Outputs of SUT</b></td>
-        <td style="width: 15%!important"><b>Values Provided <br>by Test(er)</b></td>
-    </tr>
-    <tr>
-        <td>Dummy Object</td>
-        <td>Utilizzato come segnaposto quando è necessario passare un argomento a un metodo</td>
-        <td>NO</td>
-        <td>NO, mai usato</td>
-        <td>NO, mai usato</td>
-        <td>Nessuno</td>
-    </tr>
-    <tr>
-        <td>Stub Object</td>
-        <td>Fornisce risposte preconfezionate alle sole chiamate fatte durante il testing</td>
-        <td>SI</td>
-        <td>SI</td>
-        <td>NO, li ignora</td>
-        <td>Input indiretti per il SUT</td>
-    </tr>
-    <tr>
-        <td>Mock Object</td>
-        <td>Instrumentare e controllare le chiamate</td>
-        <td>SI</td>
-        <td>Opzionale</td>
-        <td>Verifica la correttezza rispetto alle aspettative.</td>
-        <td>Input indiretti per il SUT (opzionali) e output indiretti attesi dal SUT</td>
-    </tr>
-    <tr>
-        <td>Spy Object</td>
-        <td>Instrumentare e controllare le chiamate ad oggetti reali</td>
-        <td>SI</td>
-        <td>Opzionale</td>
-        <td>Li cattura per una verifica successiva</td>
-        <td>Input indiretti per il SUT (opzionali)</td>
-    </tr>
-    <tr>
-        <td>Fake Object</td>
-        <td>Permette di eseguire test che altrimenti sarebbero impossibili o avrebbero effetti collaterali indesiderati (es test molto lenti)</td>
-        <td>SI</td>
-        <td>NO</td>
-        <td>Li utilizza</td>
-        <td>Nessuno</td>
-    </tr>
-</table>
-
-
-
-# Mockito
-
-**Mockito** è un framework di testing open source per Java rilasciato sotto la licenza MIT. 
-Il framework facilita di gran lunga la creazione di mock objects e in generale di tutti i tipi di Test Double, permettendo quindi di concentrarsi sulla scrittura della logica di testing.   
-Inoltre, l'impiego di mockito aumenta notevolmente la leggibilità dei test.  
-
-#### Creare Test Double
-
-Mockito mette a disposizione principalmente due metodi per creare Test Double: il metodo `mock()` e il metodo `spy()`.
-
-Il metodo `mock()` è usato per creare **Test Double** (dummy, stub o mock objects) a partire da una determinata classe o interfaccia: l'oggetto creato si presenterà con la stessa interfaccia (_metodi e firme di questi ultimi_) del tipo specificato in fase di costruzione.
-Di default, per ogni metodo dell'oggetto reale, il Test Double creato fornisce **un'implementazione minimale**.
-Questo si limiterà a restituirà dei valori di default per il tipo di ritorno del metodo oppure a non fare nulla se il metodo ritorna `void`.  
-Ad esempio, se si crea un oggetto con `mock()` a partire da una classe che ha un metodo `getValue()` che restituisce un `int`, il metodo `getValue()` del Test Double restituirà 0, che è il valore predefinito per un `int` in Java.  
-Il Test Double può essere configurato, mediante opportuna operazione di _stubbing_ anche per restituire valori specifici o lanciare eccezioni qualora vengano chiamati determinati metodi.
-
-Il metodo `spy()` viene utilizzato per creare spy objects a partire da oggetti reali.
-Quello che si ottiene è un oggetto che ha le stesse funzionalità dell'oggetto originale, ma che può essere utilizzato per fare il "tracciamento" delle chiamate ai suoi metodi e per verificare che esse vengano portate a termine come previsto.
-A differenza degli oggetti creati con il metodo `mock()`, uno spy continuerà a chiamare il metodo reale, a meno che non si specifichi il contrario.
-
-#### Stubbing
-
-```java
-when(mockedObj.methodname(args)).thenXXX(values);
-```
-- args: values, matchers, argumentCaptor
-- matchers: anyInt(), argThat(is(closeTo(1.0, 0.001)))
-- thenXXX: thenReturn, thenThrows, thenAnswer, thenCallRealMethod
-
-Il metodo `when()` insieme ai vari `thenXXX()` (es `thenReturn()`, `thenThrow()`) è usato per specificare il comportamento di un Test Double (stub mock o spy obj) quando viene chiamato un suo determinato metodo.
-Si supponga, ad esempio, di avere una classe _Foo_ con un metodo `getValue()` che restituisce un `int`.
-Per fare in modo che restituisca un valore diverso dallo 0 (default per int), è possibile scrivere:
-```java
-Foo foo = mock(Foo.class);
-when(foo.getValue()).thenReturn(42);
-```
-Da questo momento in poi, il metodo `getValue()` del Test Double restituirà l'intero 42 ogni volta che verrà chiamato.  
-Ovviamente il metodo `when()` può essere usato per specificare il comportamento di qualsiasi metodo del Test Double.
-Se quindi viene scritto:
-```java
-Foo foo = mock(Foo.class);
-when(foo.someMethod()).thenThrow(new SomeException());
-```
-il Test Double lancerà un'eccezione di tipo `SomeException` quando viene chiamato il suo metodo `someMethod()`. 
-Questo permette, per esempio, di testare il comportamento del nostro codice quando viene lanciata un'eccezione senza dover implementare l'oggetto reale.
-
-Anche i metodi del tipo `doXXX()` (es. `doReturn()`, `doThrow()`, `doAnswer()`, `doNothing()`) sono usati per specificare il comportamento del Test Double quando viene chiamato un suo metodo.
-Tuttavia, a differenza del metodo `when()`, questi possono essere usati anche per specificare il comportamento di un metodo che ha come tipo di ritorno `void`; è consigliabile usarli solo in questo caso, oppure in tutti i casi in cui utilizzare il metodo `when()` risulterebbe difficile (_e.g._ metodi con tipi di ritorno non banali come `Optional`).
-Questi metodi si utilizzano come segue:
-  ```java
-doXXX(values).when(mockedObj).methodname(args)
-```
-
-#### Verifying
-
-Con oggetti di tipo mock o spy si desidera spesso verificare l'occorrenza di una chiamata con certi parametri. 
-Mockito permette di farlo con il metodo `verify()`: è possibile verificare che un metodo sia stato chiamato, con quali parametri e per quante volte.
-```java
-verify(mockedclass, howMany).methodname(args)
-```
-Il parametro _howMany_ del metodo verify specifica il numero di volte che il metodo associato all'oggetto mockato deve essere chiamato durante l'esecuzione del test.
-
-Le possibili opzioni sono:
-
--   `times(n)`: verifica che `methodname()` sia stato chiamato esattamente `n` volte;
--   `never()`: verifica che `methodname()` non sia mai stato chiamato;
--   `atLeastOnce()`: verifica che `methodname()` sia stato chiamato almeno una volta;
--   `atLeast(n)`: verifica che `methodname()` sia stato chiamato almeno `n` volte;
--   `atMost(n)`: verifica che `methodname()` sia stato chiamato al massimo `n` volte.
-
-Se si desidera verificare l'ordine delle occorrenze delle chiamate ai metodi di un oggetto, si può utilizzare il metodo `inOrder()`:
-```java
-InOrder inO = inOrder(mock1, mock2, ...)
-inO.verify...
-```
-
-È possibile anche catturare un parametro per farci delle asserzioni.
-```java
-ArgumentCaptor<Person> arg = ArgumentCaptor.forClass(Person.class);
-verify(mock).doSomething(arg.capture());
-assertEquals("John", arg.getValue().getName());
-```
-
-#### Argument Matchers
-
-Quando si esegue un'operazione di stubbing oppure quando si verifica la chiamata a un metodo, al posto di specificare i valori precisi (values) si può utilizzare quello che è un **argument matcher**. \\
-Questo agisce come un segnaposto che corrisponde a qualsiasi valore corretto (_i.e._ che soddisfa la condizione di match), consentendo di specificare il comportamento senza dover conoscere il valore esatto dell'argomento che sarà passato al metodo.
-Alcuni possibili matcher sono:
-
-* `any()`, `anyInt()`, `anyString()`, etc.: questi metodi sono usati per creare degli argument matcher, che permettono di specificare che un particolare argomento del metodo può essere qualsiasi valore di un particolare tipo.
-Per esempio, si può utilizzare `anyInt()` per specificare che un argomento può essere un qualsiasi valore int.
-Questo risulta utile qualora si desideri fare lo stub di un metodo che restituisca un valore indipendentemente dagli argomenti passatigli.
-
-* `eq()`: questo metodo viene utilizzato per creare un argument matcher che corrisponde a un valore specifico.
-Per esempio, si può utilizzare `eq(42)` per specificare che un argomento deve avere il valore 42 per poter essere confrontato.
-Ciò è utile quando si vuole fare lo stub di un metodo in modo che questo restituisca un valore solo quando viene chiamato con argomenti specifici.
-
-* Il metodo `argThat()` è un modo più generale per specificare argument matchers.
-Permette di creare matcher personalizzati implementando l'interfaccia `ArgumentMatcher`.
-Questa definisce un metodo `matches()` che può essere utilizzato per determinare se un particolare argomento corrisponde al matcher.
-Tale metodologia risulta utile quando si vuole abbinare gli argomenti in maniere più complesse o articolate rispetto ai matcher di argomenti di tipo `any()`.
-
-
-### Reset a Test Double
-
-Infine, il metodo `reset()` è usato per ripristinare un Test Double al suo stato iniziale, cancellando qualsiasi metodo che era stato precedentemente ridefinito.
-È utile quando si vuole riutilizzare un Test Double in più test.
-
-
-### Esempio di testing con pattern <big>O</big>BSERVER (PULL)
-
-```java
-@Test
-void modelTest {
-    // setup
-    Model model = new Model();
-    Observer obs = mock(Observer.class);
-    Observer obs1 = mock(Observer.class);
-
-    // exercise
-    model.addObserver(obs);
-    model.addObserver(obs1);
-    model.setTemp(42.0, scale);
-
-    // verify
-    verify(obs).update(eq(model), eq("42.0"));
-    verify(obs1).update(eq(model), eq("42.0"));
-}
-```
-
-Test di un observer con un modello non generico, ma di cui si ha solo interfaccia di cui viene fornita una versione dummy:
-
-```java
-@Test
-void observerTest {
-    abstract class MockObservableIModel extends Observable implements Model {};
-    MockOBservableIModel model = mock(MockObservableIModel.class);
-    when(model.getTemp()).thenReturn(42.42);
-
-    observer.update(model, null);
-
-    verify(model).getTemp();
-    assertThat(val).isCloseTo(42.42, Offset.offset(.01));
-}
-```
\ No newline at end of file
diff --git a/_posts/2022-11-14-uml.md b/_posts/2022-11-14-uml.md
deleted file mode 100644
index 2a48b99aa7543c5df48e1e24919112bd9ced61a3..0000000000000000000000000000000000000000
--- a/_posts/2022-11-14-uml.md
+++ /dev/null
@@ -1,397 +0,0 @@
----
-layout: post
-title: "[11] UML"
-date:   2022-11-14 14:30:00 +0200
-toc: true
----
-
-<style>
-    p.plantuml-parent {
-        margin-bottom: 0;
-    }
-    p.tab {
-        margin-left: 30px;
-    }
-</style>
-
-# UML
-
-## Introduzione
-
-__UML__ (_Unified Modeling Language_) è un linguaggio di _modeling_ il cui scopo è determinare uno standard comune nella rappresentazione visuale del software.
-
-UML rappresenta in realtà una famiglia di formalismi, che si concretizzano nei concetti di __diagrammi__.
-
-## Class diagram
-
-### Concetto e struttura
-
-Lo scopo del __diagramma delle classi__ è fornire una vista statica del software (una sorta di "fotografia") tramite la rappresentazione delle sue classi, corredate di metodi, attributi e relazioni.
-
-<!-- Hardcoded diagram because the PlantUML jekyll plugin produces a malformed version -->
-<p class="plantuml-parent">
-    <object class="plantuml" style="width: 85%" data="{{ site.baseurl }}/assets/11_UML-base.svg"></object>
-</p>
-
-I componenti identificabili in un diagramma delle classi sono:
-- __oggetti__ (_Classi_ e _Interfacce_), rispettivamente riconoscibili per le lettere "C" e "I" nella parte superiore di ogni blocco. 
-{% plantuml style="width: 40%" %}
-hide fields
-hide methods
-class Card
-interface Comparable
-{% endplantuml %} 
-<p class="tab"> Esiste anche il marcatore "A", che rappresenta una classe astratta.
-Inoltre, per i diagrammi UML relativi a Java si può usare la lettera "E" per rappresentare le classi enum;</p>
-{% plantuml style="width: 36%" %}
-hide fields
-hide methods
-abstract CardStack
-enum Suit
-{% endplantuml %} 
-
-- __metodi__: preceduti da un cerchio e dal tipo di valore ritornato;
-{% plantuml style="width: 25%" %}
-class Deck {
-    + void shuffle()
-    + Card draw()
-    + void sort()
-}
-{% endplantuml %}
-- __attributi__: preceduti da un quadrato, corrispondono agli attributi dell'oggetto;
-{% plantuml style="width: 23%" %}
-class Card {
-    - Rank rank
-    - Suit suit
-}
-{% endplantuml %}
-- __relazioni__: frecce che connettono gli oggetti.
-
-È possibile rappresentare il _cerchio_ dei metodi e il quadrato degli attributi con colori diversi in base alla visibilità. 
-In Java, ad esempio, si può usare il <span style="color:green">verde</span> per la visibilità `public`, l'<span style="color:orange">arancio</span> per `protected` e il <span style="color:red">rosso</span> per `private`.
-
-Valgono anche due regole sintattiche generali:
-- se una scritta è in _corsivo_ vuol dire che all'elemento corrispondente manca qualche definizione ed è dunque da considerarsi __astratto__;
-- se una scritta è <u>sottolineata</u> vuol dire che l'elemento corrispondente (tipicamente metodo o attributo) è __statico__, ovvero ha una visibilità a livello di classe e non a livello di istanza (_i.e_ è possibile riferircisi ad esso anche senza avere precedentemente istanziato la classe).
-
-### Relazioni
-
-Nel diagramma delle classi UML esistono relazioni di diversi tipi.
-Ogni relazione viene rappresentata tramite una specifica forma di freccia:
-- __frecce tratteggiate__ (___associazione___): sono le più generiche e indicano una relazione "gerarchica" tra classi.
-Ciò che c'è scritto nella classe da cui parte la freccia dipende dal codice che c'è nella classe a cui arriva la freccia (_e.g._ `Deck` dipende da `Collections`);
-{% plantuml style="width: 40%" %}
-hide fields
-hide methods
-class Deck
-class Collections
-Deck .> Collections
-{% endplantuml %}
-- __frecce con rombo bianco__ (___aggregazione___): indica che all'interno della classe (_e.g._ `Deck`) è presente una collezione (in questo caso una lista) di $$n$$ oggetti (`Card` nell'esempio). \\
-Questa relazione non è più tra classi, bensì tra _istanze_ delle classi (_e.g._ un'istanza di `Deck` aggrega da 0 a 52 carte);
-{% plantuml style="width: 33%" %}
-hide fields
-hide methods
-class Deck
-class Card
-Deck .o Card
-{% endplantuml %}
-- __frecce con rombo nero__ (___composizione___): è utilizzata quando si hanno degli elementi che sono _fisicamente_ collegati tra loro (non solo virtualmente come nel caso delle carte). \\
-Senza l'uno l'altro non può vivere e viceversa. \\
-Un esempio può essere la rappresentazione del concetto di _aereo_: senza il _motore_ l'aereo non può esistere, poichè il primo è un oggetto indispensabile per funzionamento del secondo. Specularmente, non accadrà mai che il motore passi ad un altro aereo (a differenza delle carte che possono passare a più mani).
-{% plantuml style="width: 36%" %}
-hide fields
-hide methods
-class Aereo
-class Motore
-Aereo .* Motore
-{% endplantuml %}
-- __frecce con la punta a triangolo__ (___implementazione___): una classe può _implementare_ una classe astratta o un'interfaccia.
-{% plantuml style="width: 40%" %}
-hide fields
-hide methods
-class Card
-interface Comparable
-Card .|> Comparable
-{% endplantuml %}
-
-La direzione delle frecce è importante perché indica il _senso_ della relazione. \\
-Per esempio, il mazzo _conosce_ le carte che contiene, ma le carte _non conoscono_ i mazzi di cui fanno parte &#8211; è per questo che la direzione della freccia va da `Deck` a `Card` e non viceversa. <br>
-
-## Sequence diagram
-
-### Concetto e struttura
-
-Lo scopo del __diagramma di sequenza__ è rappresentare il flusso di interazione tra attori all'interno di un software nel tempo.
-Di seguito ne è indicato un esempio.
-
-{% responsive_image path: 'assets/11_sequence-diagram-example.png' %}
-
-Si compone di alcuni elementi chiave:
-- __attori__: rappresentano le entità coinvolte nel processo; spesso sono _oggetti_.
-- __invocazioni__: identificano chiamate di metodo su un attore da parte di un altro e sono rappresentate con una freccia che va da _sinistra verso destra_. <br>
-La parte a sinistra è il _chiamante_ e la parte a destra è il _chiamato_.
-- __valori di ritorno__: visualizzati tramite una freccia tratteggiata che va da _destra verso sinistra_.
-* __cicli__: aree rettangolari etichettate con il termine `loop` che specificano la presenza di un ciclo in una certa zona del diagramma. 
-* __condizioni__: aree rettangolari etichettate con il termine `opt` che specificano la necessità di verificare alcune condizioni prima di entrare nella zona corrispondente.
-
-## State diagram
-
-### Concetto e struttura
-
-L'obiettivo del **diagramma di stato** è fornire un'astrazione di comportamento significativa che sia comune all'intera classe.
-
-La sua struttura deriva dai classici *State Charts*, dei quali costituisce un'ulteriore astrazione.
-
-Al fine di comprendere meglio i diagrammi di stato, può essere utile ricordare che:
-
-> Negli _State Charts_, un automa è una sestupla $$\langle S, \, I, \, U; \; \delta, \, t, \, s_0 \rangle$$.
-> - $$S$$: insieme finito e non vuoto degli stati;
-> - $$I$$: insieme finito dei possibili ingressi;
-> - $$U$$: insieme finito delle possibili uscite;
-> - $$\delta$$: funzione di transizione;
-> - $$t$$: funzione di uscita;
-> - $$s_0$$: stato iniziale.
-
-Negli _State Diagram_, ogni _stato_ è rappresentato da un rettangolo e lo _stato iniziale_ è indicato da un pallino nero.
-
-{% responsive_image path: 'assets/11_state-diagram-example.png' %}
-
-Nel diagramma le frecce possono avere diversi significati:
-- **evento/azione**: semplice e immediata transizione da uno stato ad un altro;
-- **guardie**: disambiguano le transazioni in uscita da uno stato che sono legate ad uno stesso evento;
-- **_time event_**: rappresentano eventi temporizzati. 
-    * *After(duration)*: indicano un tempo massimo di permanenza nello stato destinazione. \\
-    Allo scadere del timer, lo stato cambia.
-* **_change event_**: rappresentano eventi che si innescano al verificarsi di un cambiamento.
-    * *When(condition)*: indicano eventi espressi in termini di valori degli attributi.
-
-Il verificarsi di eventi non esplicitamente marcati da un arco deve portare alla terminazione dell'esecuzione e al sollevamento di un errore.
-
-## Superstate
-
-Ulteriore evoluzione dello State Diagram, il **Superstate** consente di rappresentare più facilmente una "gerarchia" di stati.
-
-La transizione in uno stato può quindi condurre ad un'altra FSM concettualmente "innestata".
-
-{% responsive_image path: 'assets/11_superstate-example.png' %}
-
-Nel caso d'esempio, lo stato `acceso` possiede al suo interno un ulteriore diagramma di stato.
-
-### Ulteriori aggiunte
-
-* è possibile associare al diagramma uno stato **_history_**, il cui scopo è memorizzare lo stato storico prima dell'interruzione dell'FSM;
-* è possibile rendere il diagramma capace di rappresentare il concetto di **concorrenza** tramite la divisione in **regioni** (ognuna regolata da una propria FSM).
-Le regioni possono essere attive contemporaneamente. I confini tra regioni, come mostrato nell'esempio, sono identificati da linee tratteggiate.
-{% responsive_image path: 'assets/11_concurrency-example.png' %} 
-
-## Use cases diagram
-
-### Concetto e struttura
-
-I **diagrammi dei casi d'uso** rappresentano l'astrazione di un insieme di scenari tra loro correlati. \\
-Essi adottano un linguaggio che verte alla risoluzione di esigenze comunicative tramite un lessico potenzialmente meno tecnico.
-Tale natura "informale" li rende ottimi mezzi di comunicazione col *cliente*.
-
-Possono essere utilizzati, ad esempio, per:
-* eplicitare differenti modalità di fare un compito;
-* stabilire quale dovrebbe essere la normale interazione nello scenario e le eccezioni che possono verificarsi.
-
-Infatti, ogni _scenario_ è corredato di:
-* __pre e post condizioni__ da rispettare;
-* __flusso di esecuzione__ da percorrere in condizioni normali;
-* eventuali __eccezioni__ e loro possibili trattamenti.
-
-Infine, parte della versatilità degli *Use Case diagrams* risiede nella loro capacità di collegarsi, eventualmente, ad altri tipi di diagrammi (*Sequence, Activity, etc*) che possono essere impiegati per descriverne in modo più approfondito il flusso.
-
-### Scenari
-
-I componenti di ogni scenario si dividono in **Attori** e **Casi d'Uso**.
-
-{% plantuml style="width: 50%" %}
-:Actor: - (Use)
-"Attore" as Actor
-"Caso d'uso" as (Use)
-{% endplantuml %}
-
-In generale il *collegamento* tra un attore e un caso d'uso rappresenta una __relazione di partecipazione__, *i.e* "Questo attore partecipa a questo caso d'uso".
-
-L'interazione può comunque essere denominata.
-
-Sono contemplati anche collegamenti fra un caso d'uso e un altro (vedi [paragrafo dedicato](#assoc-ucuc)).
-
-### Identificazione degli attori
-
-Gli _attori_ non sono necessariamente persone fisiche.
-Possono corrispondere anche a dei **ruoli** o addirittura ad un **sistema esterno**.
-
-Ogni attore è un'entità esterna al sistema ed interagisce direttamente con esso, fungendo allo stesso tempo da *fonte* e *destinatario* di informazione.
-
-Ci sono due attori particolari:
-
-* **attore beneficiario**: colui che trae beneficio dall'interazione con lo use case, *i.e.* chi è **interessato** a quella funzionalità. \\
-Gli altri attori possono cambiare, ma il beneficiario rimmarrà probabilmente lo stesso;
-* **attore primario**: colui che avvia l'interazione con lo use case.
-
-### Identificazione use case
-Il miglior modo di identificare i casi d'uso è interrogarsi su due fronti:
-
-* **sistema**: _"quali funzionalità si desidera che il sistema possieda?"_;
-* **attori beneficiari**: _"cosa vogliono?"_, _"come agiscono?"_, _"perchè si interfacciano col sistema?"_ e _"cosa si aspettano?"_.
-
-### Associazioni
-
-Ogni diagramma dei casi d'uso deve seguire due convenzioni per quanto riguarda le associazioni.
-
-* > Ogni attore deve avere almeno un'interazione con un caso d'uso.
-
-Un attore che non dovesse possedere alcuna associazione con un caso d'uso sarebbe impossibilitato a interagire col sistema e rappresentarlo nel diagramma non avrebbe alcun senso.
-
-* > Ogni caso d'uso deve essere associato ad almeno un attore.
-
-Un caso d'uso che non coinvolge alcun attore è un caso d'uso che, per definizione, non ha senso di esistere, poichè nessuno è in grado di interagirvi.
-
-#### <a name="assoc-ucuc"></a>Relazioni _use case - use case_
-Esistono due tipologie di relazioni tra use case:
-* **inclusione (*include*)**: relazione che esprime il predicato *"far parte di"*.  \\
-Chi include conosce sempre gli inclusi, ma non viceversa.
-La parte inclusa *deve* essere eseguita;
-* **estensione (*extend*)**: relazione che viene utilizzata per rappresentare casi eccezionali che specificano comportamenti particolari in alcuni use case.
-
-#### Generalizzazione
-
-L'associazione di **generalizzazione** rappresenta un particolare tipo di relazione, applicabile sia ad una coppia *attore - attore* che ad una coppia *use case - use case*.
-
-La sua semantica dipende dal contesto a cui viene applicata:
-* **tra attori**: permette di esplicitare eventuali relazioni tra ruoli. \\
-Ad esempio un ruolo potrebbe includerne un altro.
-* **tra use case**: la semantica è simile all'*extend*, ma senza punti d'estensione. Infatti, alcune parti della descrizione vengono *ereditate* e altre vengono *sostituite*. Non si applica  il secondo principio della Liskov.
-
-### Esempi di utilizzo
-
-Nel seguente diagramma,
-
-{% plantuml style="width: 60%" %}
-"Book Borrower" as BB
-"Extend Loan" as (ext)
-"Borrow copy of a book" as (bor)
-"Check for reservation" as (chk)
-BB -- ext
-BB -- bor
-ext ..> chk : << include >>
-bor ..> chk : << include >>
-{% endplantuml %}
-
-l'attore _Book Borrower_ è associato alle seguenti operazioni:
-- prendere in prestito un libro;
-- chiedere l'estensione del prestito di un libro.
-
-In entrambi i casi, il bibliotecario deve controllare l'esistenza di una richiesta di prenotazione per il libro.
-
-Il prossimo diagramma è differente:
-
-{% plantuml style="width: 100%" %}
-"Book Borrower" as BB
-usecase bor as "Borrow copy of book
---
-**extension points**
-status validation:
-after confirming identity"
-"Refuse loan" as (ref)
-BB - bor
-bor <. ref : << extend >>
-{% endplantuml %}
-
-_rifiuta il prestito_ può essere l'__estensione__ di un comportamento normale come _prendi in prestito il libro_.
-In quest'ultimo ci sono dei punti di estensione in cui vengono fatti dei controlli, come la verifica dello stato di prestito del libro o dell'identità del richiedente.
-
-## Activity diagram
-
-### Concetto e struttura
-
-Gli _activity diagram_ presentano una conformazione simile agli _state diagram_, ma con svariate differenze:
-* al posto degli stati __vi sono le *attività*__;
-* non si usano più le __transizioni__ etichettate tramite eventi &#8211; queste sono quasi tutte __implicitamente temporizzate__;
-* possono esserci ___azioni_ dentro le attività__;
-* le *attività* possono rappresentare __elementi esterni__ al sistema.
-
-La peculiare capacità di collegarsi con attività esterne fa sì che sia possibile utilizzare gli activity diagram come __collante__ con e tra diversi casi d'uso.
-
-Inoltre la __visuale parzialmente informale__, eppure leggermente più tecnica e profonda rispetto ai diagrammi dei casi d'uso, rende gli activity diagram un __ottimo mezzo di comunicazione interna__ (*e.g.* con un manager).
-
-### Livelli di astrazione
-
-Si possono utilizzare i diagrami delle attività per:
-- descrivere la logica interna di un __business process__ (caso più comune);
-- descrivere il __flusso interno di un metodo__, con eventuali indicazioni di (pseudo)concorrenza;
-- dettagliare il __flusso di un caso d'uso__, ovvero chiarire meglio il suo flusso di esecuzione rispetto ad altri diagrammi (*e.g.* Sequence Diagram). 
-Questa rappresentazione è assai utile nei casi in cui, ad esempio, la concorrenza è un fattore rilevante.
-
-
-### Sincronizzazione
-
-{% responsive_image path: 'assets/11_activity-example.png' %}
-
-Attraverso l'uso di barre si possono stabilire dei punti di sincronizzazione (JOIN). \\
-I JOIN, se non diversamente specificato, vengono considerati in ___AND___.
-
-È però possibile porre dei vincoli diversi per stabilire i criteri di soddisficamento della barra di sincronizzazione (come una __*OR*__).
-
-### Decisioni
-
-{% responsive_image path: 'assets/11_activity-decision-example.png' %}
-
-È possibile specificare nel flusso di esecuzione dei momenti di __decisione__.
-I corsi d'azione intraprendibili in questi frangenti sono rappresentati tramite degli archi.
-
-Le decisioni devono rispettare due proprietà:
-* gli archi collegati alla decisione devono essere __mutualmente esclusivi__;
-* l'__unione__ delle condizioni di decisione deve essere sempre vera.
-
-È bene puntualizzare che i punti di decisione sono _veri_ momenti di decisione umana, non banali valutazioni di una condizione logica.
-
-### Swim lane
-
-{% responsive_image path: 'assets/11_activity-swim-lane-example.png' %}
-
-Si può partizionare il diagramma al fine di rappresentare, sulle singole _activity_, delle particolari responsibilità.
-Queste vengono visualizzate tramite delle _"corsie"_ verticali che identificano _chi_ svolge una determinata attività.
-
-## Component diagram
-
-### Concetto e struttura
-
-Lo scopo del __diagramma dei componenti__ è rappresentare e raggruppare i componenti del sistema. \\
-_"Componente"_ è un termine trasversale che include file, librerie, documenti _etc._ (ma che è diverso dal concetto di _classe_!).
-
-Il diagramma include:
-* __componenti__: rettangoli che rappresentano una funzione di sistema ben determinata. \\
-Possono essere _annidati_;
-* __interfacce__: cerchi che indicano le interfacce implementate o utilizzate dai componenti. \\
-I collegamenti con i componenti indicano la presenza di una dipendenza;
-* __stereotipi__: racchiusi tra i caratteri `<<>>`, etichettano e identificano una serie di funzionalità appartenenti ad uno stesso "gruppo".
-
-{% responsive_image path: 'assets/11_component-diagram-example.png' %}
-
-Si noti che un componente può usarne un altro conoscendone solo l'interfaccia.
-
-### Identificare i componenti
-
-Alcune linee guida per identificare e rappresentare correttamente i componenti sono:
-- capire quali parti del sistema sono rimpiazzabili facilmente e/o sono versionate separatamente;
-- identificare quali parti del sistema svolgono una funzione ben determinata;
-- pensare in termini di "gerarchia" dei componenti;
-- chiarire l'esistenza di dipendenze con altri componenti e di dipendenze con le interfacce.
-
-## Deployment diagram
-
-Il _Deployment diagram_ permette di rappresentare la __dislocazione fisica__ delle risorse. \\
-Più precisamente, specifica la dislocazione fisica delle _istanze dei componenti_.
-
-La conformazione del diagramma è quindi molto simile a quella del diagramma dei componenti, ma con qualche differenza:
-* i __nodi__ del sistema indicano macchine fisiche;
-* i __collegamenti__ tra nodi eplicitano le modalità di comunicazione tra gli stessi (_e.g._ RMI, HTTP).
-
-{% responsive_image path: 'assets/11_deployment-diagram-example.png' %}
-
-Il Deployment diagram risulta di particolare utilità per il _deployer_, _i.e._ la figura che si occupa dell'installazione fisica del sistema. 
diff --git a/_posts/2022-11-23-testing-e-processi-di-review.md b/_posts/2022-11-23-testing-e-processi-di-review.md
deleted file mode 100644
index 36bdd9ba2def04e9f72ec56fb99b676a8fcc1813..0000000000000000000000000000000000000000
--- a/_posts/2022-11-23-testing-e-processi-di-review.md
+++ /dev/null
@@ -1,1696 +0,0 @@
----
-layout: post
-title: "[13] Testing e Processi di review"
-date: 2022-11-23 14:40:00 +0200
-toc: true
----
-
-# Testing strutturale
-
-La maggior parte dei problemi che si verificano durante lo sviluppo di un progetto sono causati da _problemi di comunicazione_.
-Ci possono essere incomprensioni quando le informazioni passano da una figura all'altra, come quando ci si interfaccia tra cliente, analista e programmatore.
-Il programmatore dovrà adattare il proprio linguaggio per farsi comprendere dal cliente prestando maggiore attenzione alla formalità e alla chiarezza della comunicazione con il passare del tempo.
-Più i concetti sono spiegati chiaramente, più è difficile incorrere in problemi successivi: è quindi necessario fare attenzione alla __terminologia__ utilizzata.
-
-Partiamo quindi dalle basi: quando un programma si definisce ___corretto___?
-
-Considerando un generico programma $$P$$ come una funzione da un insieme di dati $$D$$ (dominio) a un insieme di dati $$R$$ (codominio) allora:
-
-- $$P(d)$$ indica l'__esecuzione__ di $$P$$ su un certo input $$d \in D$$,
-- il risultato $$P(d)$$ è __corretto__ se soddisfa le specifiche, altrimenti è scorretto,
-- $$\operatorname{ok}(P, \, d)$$ indica la __correttezza__ di $$P$$ per il dato $$d$$
-
-quindi
-
-$$
-\boxed{P \text{ è } \textit{corretto} \Longleftrightarrow \forall d \in D \:, \text{ } \operatorname{ok}(P, \, d)}
-$$
-
-A parole, _un programma __è corretto__ quando __per ogni dato__ del dominio vale $$\operatorname{ok}(P, \, d)$$_.
-
-Per indicare la correttezza di programma $$P$$ si utilizza la notazione $$\operatorname{ok}(P, \, D)$$, che appunto indica che $$P$$ è _corretto_ per qualunque $$d \in D$$.
-
-## Definizione di test
-
-Durante l'attività di testing ciò che viene fatto è sottoporre il programma a una serie di stimolazioni per saggiarne il comportamento in tali circostanze.
-Eseguire un test vuole quindi dire eseguire il programma con una serie di input appartenenti al suo dominio e confrontare i risultati ottenuti con il risultato atteso secondo le specifiche.
-
-Volendone dare una definizione più rigorosa, _un __test__ è un sottoinsieme del dominio dei dati_ e _un singolo __caso di test__ è un elemento di esso_.
-Un test sono quindi __più stimolazioni__, mentre un caso di test è una __singola stimolazione__. \\
-Matematicamente:
-
-- un test $$T$$ per un programma $$P$$ è un sottoinsieme del suo dominio $$D$$;
-- un elemento $$t$$ di un test $$T$$ è detto _caso di test_;
-- l'esecuzione di un test consiste nell'esecuzione del programma $$\forall t \in T \subseteq D$$.
-
-Un programma $$P$$ supera (o _passa_) un test $$T$$ se:
-
-$$
-\operatorname{ok}(P, \, T) \Longleftrightarrow \forall t \in T \:, \text{ } \operatorname{ok}(P, \, t)
-$$
-
-Quindi, _un programma è __corretto per un test__ quando __per ogni caso di test__ esso è __corretto___.
-
-Lo scopo dei test è però ricercare comportamenti anomali nel programma per permetterci di correggerli.
-Diciamo quindi che _un test $$\, T$$ ha __successo__ se rileva uno o più malfunzionamenti presenti nel programma $$P$$_:
-
-$$
-\operatorname{successo}(T, \, P) \Longleftrightarrow \exists t \in T \: | \: \lnot \operatorname{ok}(P, \, t)
-$$
-
-### Test ideale
-
-Se un test non rileva alcun malfunzionamento __non significa che il programma sia corretto__: come visto nella lezione precedente, il test è un'attività ottimistica e normalmente il passaggio di un test non garantisce l'assenza di anomalie.
-Questo smette però di essere vero nel caso di _test ideali_.
-
-_Un test $$T$$ si definisce __ideale__ per $$P$$ se e solo se_
-
-$$\operatorname{ok}(P, \, T) \Rightarrow \operatorname{ok}(P, \, D)$$
-
-_ovvero se il superamento del test __implica la correttezza del programma___.
-
-Purtroppo però in generale è ___impossibile_ trovare un test ideale__, come ci suggerisce la seguente ipotesi universalmente accettata:
-
-> __Tesi di Dijkstra__:
->
-> _Il test di un programma può rilevare la presenza di malfunzionamenti ma non dimostrarne l'assenza._
->
-> _Non esiste quindi un algoritmo che dato un programma arbitrario $$P$$ generi un test ideale __finito__ \\
-> (il caso $$T = D$$ non va considerato)._
-
-Notiamo come la tesi escluda esplicitamente il _test esaustivo_ $$T = D$$, restringendosi a considerare i test finiti (mentre il dominio $$D$$ potrebbe anche essere infinito).
-Per capire il perché di questa distinzione è sufficiente osservare il seguente esempio:
-
-```java
-static int sum(int a, int b) {
-    return a + b;
-}
-```
-
-In Java un int è espresso su 32 bit, quindi il dominio di questa semplice funzione somma ha cardinalità $$2^{32} \cdot 2^{32} = 2^{64} \sim 2 \cdot 10^{19}$$.
-Considerando quindi un tempo di esecuzione ottimistico di 1 nanosecondo per ogni caso di test, un test esaustivo che provi tutte le possibili combinazioni di interi impiegherebbe più di 600 anni per essere eseguito per intero.
-
-_Il __test esaustivo__ è quindi __impraticabile__._
-
-## Criteri di selezione
-
-Assodato che un test ideale è impossibile da realizzare, come possiamo scegliere un _sottoinsieme del dominio_ che approssimi il più possibile un _test ideale_? \\
-Esistono una serie di __criteri di selezione__ che hanno proprio lo scopo di guidare la selezione dei casi di test all'interno del dominio in modo da massimizzare la probabilità che il test abbia successo.
-Prima però di illustrarne alcuni, vediamo quali caratteristiche dovrebbero avere questi criteri.
-
-### Proprietà
-
-#### Affidabilità
-
-_Un criterio di selezione $$C$$ si dice __affidabile__ se presi due test $$T_1$$ e $$T_2$$ in base al criterio allora \\
-o entrambi hanno successo o nessuno dei due ha successo_.
-
-$$
-\boxed{
-    \operatorname{affidabile}(C, \, P) \Longleftrightarrow \left (
-        \forall T_1 \in C, \, \forall T_2 \in C \:, \text{ } \operatorname{successo}(T_1, \, P) \Leftrightarrow \operatorname{successo}(T_2, \, P)
-    \right )
-}
-$$
-
-#### Validità
-
-_Un criterio di selezione si dice __valido___ _se qualora $$P$$ non sia corretto, allora esiste almeno un test $$T$$ selezionato in base al criterio $$C$$ che ha successo e quindi rileva uno o più malfunzionamenti per il programma $$P$$:_
-
-$$
-\boxed{
-    \operatorname{valido}(C, \, P) \Longleftrightarrow \left (
-        \lnot \operatorname{ok}(P, \, D) \Rightarrow \exists T \in C \: | \operatorname{successo}(T,\,P)
-    \right )
-}
-$$
-
-#### Esempio
-
-Si consideri il seguente codice.
-
-```java
-static int raddoppia(int par) {
-    int risultato;
-    risultato = (par * par);
-    return risultato;
-}
-```
-
-Un criterio che seleziona:
-
-- _"i sottoinsiemi di $$\{0, \, 2\}$$”_ è __affidabile__, perché il programma funziona sia con $$0$$ sia con $$2$$, ma __non valido__, perché sappiamo che il programma non è corretto e non esiste un test che trovi malfunzionamenti;
-- _"i sottoinsiemi di $$\{0, \, 1, \, 2, \, 3, \, 4\}$$”_ è __non affidabile__, perché i risultati dei casi di test non sono tutti coerenti (e quindi il test $$T1=\{0,1\}$$ non ha successo mentre $$T2=\{0, 3\}$$ sì), ma __valido__ perché esiste un test che rileva i malfunzionamenti.
-- _"i sottoinsieme finiti di $$D$$ con almeno un valore maggiore di $$18$$”_ è __affidabile__, perché i risultati dei casi di test sono tutti coerenti, e __valido__ perché rileva i malfunzionamenti.
-
-In questo caso la ricerca di un criterio valido e affidabile era semplice perché conoscevamo già l'anomalia.
-Tuttavia, lo stesso non si può dire di un qualunque programma $$P$$ in quanto __non si conoscono i malfunzionamenti a priori__ e dunque è molto più difficile trovare criteri validi e affidabili.
-
-#### Conclusione
-
-L'obiettivo sarebbe quindi quello di trovare un _criterio valido e affidabile_ sempre.
-Tuttavia ciò è purtroppo impossibile in quanto un criterio di questo tipo selezionerebbe test ideali, che sappiamo non esistere.
-
-Immaginiamo infatti di avere un _criterio valido e affidabile_ e che un test selezionato da esso __non abbia successo__.
-Sapendo che:
-
-- non avendo successo allora non sono stati trovati errori,
-- essendo il criterio affidabile allora tutti gli altri test selezionati da quel criterio non troveranno errori,
-- essendo il criterio valido allora se ci fosse stato un errore almeno uno dei test lo avrebbe trovato
-
-allora il programma è __corretto__, ovvero abbiamo trovato un test che quando non ha successo implica la correttezza del programma: in poche parole, un _test ideale_.
-Esiste quindi un altro modo per implicare la correttezza di un programma:
-
-$$
-\boxed{
-    \operatorname{affidabile}(C, \, P) \land \operatorname{valido}(C, \, P) \land T \in C \land \lnot\operatorname{successo}(T, \, P)
-    \Longrightarrow
-    \operatorname{ok}(P, \, D)
-}
-$$
-
-In conclusione, trovare un criterio che sia __contemporaneamente__ affidabile e valido significherebbe trovare un criterio che selezioni __test ideali__ che sappiamo non esistere per la _tesi di Dijkstra_.
-Dovremo dunque accontentarci di criteri che garantiscano solo una delle due caratteristiche.
-
-### Utilità di un test
-
-Abbandonata la vana speranza di un criterio di selezione universalmente valido che permetta di testare alla perfezione qualunque programma vediamo ora cosa significa _utilizzare_ un criterio di selezione per costruire un test.
-Come sappiamo un test altro non è che un insieme di casi di test, specifici input appartenenti al dominio del programma: un criterio di selezione governa dunque quanti e quali casi di test vengono aggiunti al test che si sta costruendo.
-
-Possiamo quindi ora farci una domanda: quali sono le __caratteristiche__ che __rendono utile__ un caso di test, ovvero che rendono "possibile" o "probabile" che il caso di test evidenzi un malfunzionamento causato da un'anomalia?
-Ebbene, un caso di test utile deve:
-
-- __eseguire il comando che contiene l'anomalia__ – non è altrimenti possibile che il malfunzionamento si manifesti;
-- l'esecuzione del comando che contiene l'anomalia deve portare il sistema in uno
-__stato scorretto__, o per meglio dire __inconsistente__;
-- lo stato inconsistente deve propagarsi fino all'uscita del codice in esame in modo da __produrre un output diverso da quello atteso__;
-
-Un buon criterio di selezione dovrà quindi selezionare test contenenti casi di test utili: ma quanti dovrebbe contenerne?
-Per capire ciò si può utilizzare un metro di misura legato alle caratteristiche del codice: a ogni criterio è infatti possibile associare una __metrica__ che misuri la __copertura__ del test che si sta costruendo e che ci permetta di decidere _quando smettere di aggiungere casi di test_, _quali casi di test è opportuno aggiungere_ e di _confrontare la bontà di test diversi_.
-Aggiungeremo infatti solo casi di test che permettano di aumentare la metrica di copertura, e test in grado di garantire una copertura maggiore saranno inerentemente migliori di test con una copertura minore.
-
-### Criteri noti
-
-Esploriamo quindi ora una serie di criteri di selezione, elencandone pro e contro, esplicitandone la metrica di copertura utilizzata e infine confrontandoli tra di loro per comprenderne le relazioni.
-
-#### Criterio di copertura dei comandi
-
-_Un test $$\ T$$ soddisfa il __criterio di copertura dei comandi__ se e solo se ogni comando eseguibile del programma è eseguito in corrispondenza di almeno un caso di test $$t \in T$$._ \\
-La metrica è dunque la frazione di __comandi eseguibili su quelli eseguiti__ dall'intero test.
-
-Consideriamo per esempio il seguente programma in pseudocodice:
-
-<table>
-<thead>
-<tr>
-    <th colspan="2">Esempio 1: copertura dei comandi</th>
-</tr>
-<tr>
-    <td style="width: 50%" align="center">Pseudocodice</td>
-    <td style="width: 50%" align="center">Diagramma di flusso di esecuzione</td>
-</tr>
-</thead>
-<tbody>
-<tr>
-<td markdown="1">
-```c
-01  void main(){
-02      float x, y;
-03      read(x);
-04      read(y);
-05      if (x != 0)
-06          x = x + 10;
-07      y = y / x;
-08      write(x);
-09      write(y);
-10  }
-```
-</td>
-<td markdown="1">
-{% responsive_image path: 'assets/13_criteri-copertura-esempio-1.png' %}
-</td>
-</tr>
-</tbody>
-</table>
-
-È possibile ricostruire un __diagramma di flusso di esecuzione__ del codice trasformando ogni comando in un nodo del diagramma: _coprire tutti i comandi_ significa quindi visitare tutti i nodi raggiungibili.
-
-Applicare il _criterio di copertura dei comandi_ significa quindi trovare un insieme di casi di test in cui _per ogni nodo esiste un caso di test che lo attraversa_.
-
-Nel nostro esempio il singolo caso di test $$ \langle 3, \, 7 \rangle$$ risulterebbe quindi sufficiente, dato che la sua esecuzione permette di _coprire_ tutti i comandi del programma.
-Tuttavia, pur massimizzando la metrica di copertura dei comandi tale test non è in grado di rilevare l'anomalia alla riga 7, in cui viene fatta una divisione senza prima controllare che il divisore sia diverso da zero.
-
-Soddisfare il criterio di copertura dei comando __non garantisce__ dunque la correttezza del programma.
-Come sappiamo infatti un'anomalia non sempre genera un malfunzionamento, per cui eseguire semplicemente tutte le righe di codice raggiungibili non assicura di rilevare eventuali errori.
-
-#### Criterio di copertura delle decisioni
-
-_Un test $$\ T$$ soddisfa il __criterio di copertura delle decisioni__ se e solo se ogni decisione (effettiva) viene resa sia vera che falsa in corrispondenza di almeno un caso di test $$t \in T$$_. \\
-La metrica è quindi la frazione delle __decisioni totali possibili__ presenti nel codice che sono state rese __sia vere che false__ nel test.
-
-Dovendo attraversare ogni possibile flusso di controllo il criterio di copertura delle decisioni __implica il criterio di copertura dei comandi__.
-Estraendo il codice in un diagramma di flusso, infatti, è possibile coprire tutte le decisioni se e solo se ogni arco (_e quindi ogni nodo_) viene attraversato.
-Non è invece vero l'inverso.
-
-<table>
-<thead>
-<tr>
-    <th colspan="2">Esempio 2: copertura delle decisioni</th>
-</tr>
-<tr>
-    <td style="width: 50%" align="center">Pseudocodice</td>
-    <td style="width: 50%" align="center">Diagramma di flusso di esecuzione</td>
-</tr>
-</thead>
-<tbody>
-<tr>
-<td markdown="1">
-```c
-01  void main(){
-02      float x, y;
-03      read(x);
-04      read(y);
-05      if (x != 0 && y > 0)
-06          x = x + 10;
-07      else
-08          y = y / x
-09      write(x);
-10      write(y);
-11  }
-```
-</td>
-<td markdown="1">
-{% responsive_image path: 'assets/13_criteri-copertura-esempio-2.png' %}
-</td>
-</tr>
-</tbody>
-</table>
-
-Dall'esempio sopra, un test che soddisfi il suddetto criterio potrebbe includere $$\{ \langle 3, \, 7 \rangle, \, \langle 3, \, -2 \rangle \}$$.
-Nonostante sia un criterio _"migliore"_ del precedente, la copertura delle decisioni __non garantisce__ la correttezza del programma: nell'esempio il caso $$\langle 0, \, 5 \rangle$$ eseguirebbe comunque una divisione per zero.
-
-#### Criterio di copertura delle condizioni
-
-_Un test $$\ T$$ soddisfa il __criterio di copertura delle condizioni__ se e solo se ogni singola condizione (effettiva) viene resa sia vera che falsa in corrispondenza di almeno un caso di test $$\ t \in T$$_. \\
-Similmente ai criteri precedenti, la metrica è quindi la percentuale delle __condizioni__ che sono state rese __sia vere che false__ su quelle per cui è possibile farlo.
-
-Sebbene simile, si tratta di un criterio diverso da quello di copertura delle decisioni: in caso di condizioni composte, come per esempio `x != 0 && y < 3`, la copertura delle decisioni imporrebbe che l'_intera condizione_ sia resa sia vera che falsa, mentre la copertura delle condizioni richiede di rendere vere e false le singole _condizioni atomiche_ `x != 0` e `y < 3` in almeno un caso di test. \\
-Come vedremo nell'esempio, ciò non impone quindi di seguire tutti i percorsi sul diagramma di flusso e fa sì che questo criterio __non implica__ il soddisfacimento di nessuno dei precedenti.
-
-<table>
-<thead>
-<tr>
-    <th colspan="2">Esempio 3: copertura delle condizioni</th>
-</tr>
-<tr>
-    <td style="width: 50%" align="center">Pseudocodice</td>
-    <td style="width: 50%" align="center">Diagramma di flusso di esecuzione</td>
-</tr>
-</thead>
-<tbody>
-<tr>
-<td markdown="1">
-```c
-01  void main(){
-02      float x, y;
-03      read(x);
-04      read(y);
-05      if (x != 0 || y > 0)
-06          y = y / x;
-07      else
-08          y = (y + 2) / x
-09      y = y / x;
-10      write(x);
-11      write(y);
-12  }
-```
-</td>
-<td markdown="1">
-{% responsive_image path: 'assets/13_criteri-copertura-esempio-3.png' %}
-</td>
-</tr>
-</tbody>
-</table>
-
-Nell'esempio sopra, il test $$ \{ \langle 0, \, 5 \rangle , \, \langle 5, \, -5 \rangle \} $$ __soddisfa il criterio di copertura della condizioni__ \\
-(`x != 0` è falsificato da $$\langle 0, \,5 \rangle$$ e verificato da $$\langle 5, \, -5 \rangle$$, mentre `y > 0` è verificato da $$\langle 0, \, 5 \rangle$$ e falsificato da $$\langle 5, \, -5 \rangle$$), ma __la decisione è sempre vera__.
-
-Sono infatti presenti anomalie alla riga 6 (possibile divisione per zero) e alla riga 8 (overflow e divisione per zero), ma i comandi contenuti nella riga 8 non sono coperti.
-In questo caso più che mai, quindi, la copertura delle condizioni __non garantisce__ la correttezza del programma.
-
-#### Criterio di copertura delle decisioni e condizioni
-
-_Un test $$\ T$$ soddisfa il __criterio di copertura delle decisioni e delle condizioni__ se e solo se __ogni decisione__ vale sia vero che falso e __ogni condizione__ che compare nelle decisioni del programma vale sia vero che falso per diversi casi di test $$\ t \in T$$_.
-
-È – intuitivamente – l'__intersezione__ del criterio di copertura delle decisioni con il criterio di copertura delle condizioni, per cui il soddisfacimento di questo criterio __implica__ sia il criterio di copertura delle condizioni che quello di copertura delle decisioni (e quindi dei comandi).
-
-Nell'esempio 3, il test $$\{ \langle 0, \, -5 \rangle, \, \langle 5, \, 5 \rangle \}$$ soddisfa il criterio di copertura delle decisioni e condizioni e rileva l'anomalia alla riga 8 ma non quella alla riga 6.
-__Non garantisce__ quindi neanche in questo caso la correttezza del programma.
-
-#### Criterio di copertura delle condizioni composte
-
-_Un test $$\ T$$ soddisfa il __criterio di copertura delle condizioni composte__ se e solo se ogni possibile composizione delle condizioni base vale sia vero che falso per diversi casi di test $$\ t \in T$$_.
-
-Viene cioè testata ogni possibile combinazione di valori delle condizioni atomiche quando queste sono aggregate in condizioni composte: riprendendo per esempio la condizione `x != 0 && y < 3`, vengono testati separatamente i casi $$\langle V, V\rangle$$, $$\langle V, F\rangle$$, $$\langle F, V\rangle$$ e $$\langle F, F\rangle$$. \\
-È quindi facile notare che __questo criterio implica il precedente__ (criterio di copertura delle decisioni e condizioni), implicando a sua volta il criterio di copertura delle decisioni, delle condizioni e dei comandi.
-
-Data la __natura combinatoria__ di questo criterio, all'aumento del numero di condizioni di base _il numero di casi di test_ cresce però troppo rapidamente, motivo per cui il soddisfacimento di questo criterio è considerato __non applicabile__ in pratica.
-Inoltre, dato che le condizioni di base potrebbero non risultare indipendenti tra loro, potrebbero esistere __combinazioni non fattibili__ che non avrebbe alcun senso testare.
-
-#### Criterio di copertura delle condizioni e delle decisioni modificate
-
-Non volendo testare tutte le combinazioni di condizioni, ci si rende presto conto che certe combinazioni sono __più rilevanti__ di altre: se modificando una sola condizione atomica si riesce a modificare l'esito della decisione, allora è molto significativa – indipendentemente dalla sua dimensione.
-Se invece l'esito della decisione non varia, allora la modifica può essere considerata neutra o meno significativa. \\
-Il criterio così ottenuto prende il nome di __criterio di copertura delle condizioni e delle decisioni modificate__.
-
-Si dà quindi importanza nella selezione delle combinazioni al fatto che la modifica di una singola condizione base porti a __modificare l'esito della decisione__.
-Per ogni condizione base devono quindi esistere due casi di test che modificano il valore di una sola condizione base e che portino a un diverso esito della decisione: in questo modo, inoltre, il criterio __implica quello di copertura delle condizioni e delle decisioni__.
-
-Si può dimostrare che se si hanno $$N$$ condizioni base __sono sufficienti $$N+1$$ casi di test__ per soddisfare questo criterio, decisamente meno di quelli richiesti dal criterio delle condizioni composte.
-
-#### Implicazioni tra criteri di copertura
-
-{% responsive_image path: 'assets/13_criteri-copertura-implicazione.png' %}
-
-Ecco dunque uno schema delle implicazioni tra i vari criteri di copertura.
-Come si vede, il criterio delle condizioni composte va considerato troppo oneroso e quindi non applicabile, mentre gli altri criteri possono invece essere utilizzati anche nell'ambito di progetti di dimensioni reali.
-
-### Altri criteri
-
-I criteri visti finora __non considerano i cicli__ e possono essere soddisfatti da test che percorrono ogni ciclo al più una volta.
-Molti errori però si verificano durante __iterazioni successive alla prima__, come per esempio quando si superano i limiti di un array.
-
-Occorre quindi sviluppare dei criteri che tengano conto anche delle iterazioni e stimolino i cicli un numero di volte sufficiente.
-
-<table>
-<thead>
-<tr>
-    <th colspan="2">Esempio 4: copertura delle iterazioni</th>
-</tr>
-<tr>
-    <td style="width: 50%" align="center">Pseudocodice</td>
-    <td style="width: 50%" align="center">Diagramma di flusso di esecuzione</td>
-</tr>
-</thead>
-<tbody>
-<tr>
-<td markdown="1">
-```c
-01  void main() {
-02      float a, b, x, y;
-03      read(x);
-04      read(y);
-05      a = x;
-06      b = y;
-07      while (a != b) {
-08          if (a > b)
-09              a = a - b;
-10          else
-11              b = b - a;
-12      }
-13      write(a);
-14  }
-```
-</td>
-<td markdown="1">
-{% responsive_image path: 'assets/13_criteri-copertura-esempio-4.png' %}
-</td>
-</tr>
-</tbody>
-</table>
-
-#### Criterio di copertura dei cammini
-
-_Un test $$\ T$$ soddisfa il __criterio di copertura dei cammini__ se e solo se ogni cammino del grafo di controllo del programma viene percorso per almeno un caso di $$t \in T$$_. \\
-La metrica è quindi il rapporto tra i __cammini percorsi__ e __quelli effettivamente percorribili__.
-
-Questo criterio è molto generale ma è spesso impraticabile, anche per programmi semplici: la presenza di cicli imporrebbe infatti di testare tutti gli infiniti cammini che li attraversano un numero arbitrario di volte. Il criterio è quindi considerato __non applicabile__ in pratica.
-
-#### Criterio di $$n$$-copertura dei cicli
-
-_Un test $$\ T$$ soddisfa il __criterio di $$\bf{\it{n}}$$-copertura__ se e solo se ogni cammino del grafo contenente al massimo un numero d'iterazioni di ogni ciclo non superiore a $$n$$ viene percorso per almeno un caso di test $$\ t \in T$$._
-
-La definizione sopra non significa che il test deve eseguire $$n$$ volte un ciclo, ma che per ogni numero $$k$$ compreso tra 0 e $$n$$ deve esistere un caso di test che esegue tale ciclo $$k$$ volte.
-Si sta quindi __limitando il numero massimo di percorrenze__ dei cicli. \\
-Di conseguenza, al crescere di $$n$$ il numero di test aumenta molto rapidamente.
-Inoltre, fissare $$n$$ a livello di programma può non essere un'azione così semplice: il numero d'iterazioni che necessita un ciclo per essere testato a fondo può essere __molto differente__ a seconda del caso.
-
-Per cercare di minimizzare il numero di test spesso il criterio applicato è quello di __$$\bf{2}$$-copertura dei cicli__.
-Si tratta infatti del numero minimo che permette comunque di testare tutte le casistiche principali:
-
-- zero iterazioni;
-- una iterazione;
-- _più di una_ iterazione.
-
-Il caso $$n = 2$$ è cioè il minimo per considerare casistiche non banali: dando uno sguardo all'esempio sopra, infatti, con $$n = 1$$ il ciclo (`while`) sarebbe stato indistinguibile da una semplice selezione (`if`); testando due iterazioni si incominciano a testare le vere caratteristiche del ciclo.
-Esso permette cioè di testare non solo i comandi che compongono il ciclo, ma anche sue le pre/post-condizioni ed eventuali invarianti.
-
-A differenza del criterio di copertura dei cammini, il criterio di $$n$$-copertura è considerato __applicabile__ a programmi reali.
-
-#### Mappa finale delle implicazioni tra criteri di selezione
-
-Aggiungendo i criteri di copertura che considerano esplicitamente i cicli si ottiene il seguente schema di implicazione tra tutti i criteri di selezione:
-
-{% responsive_image path: assets/13_implicazioni-estese-criteri-copertura.png %}
-
-# Analisi statica
-
-Come abbiamo detto nella lezione precedente, il testing dell'esecuzione del programma non è però l'unica cosa che possiamo fare per aumentare la fiducia nostra e del cliente nella correttezza del programma.
-Un'altra importante iniziativa in tal senso è l'ispezione tramite varie tecniche del _codice_ del programma, attività che prende il nome di __analisi statica__.
-
-L'analisi statica si basa cioè sull'esame di un __insieme finito di elementi__ (_le istruzioni del programma_), contrariamente all'analisi dinamica che invece considera un insieme infinito (_gli stati delle esecuzioni_).
-È un'attività perciò __meno costosa del testing__, poiché non soffre del problema dell'_"esplosione dello spazio degli stati"_.
-
-Considerando solamente il codice "statico" del programma, questa tecnica non ha la capacità di rilevare anomalie dipendenti da particolari valori assunti dalle variabili a runtime.
-Si tratta nondimeno di un'attività estremamente utile, che può aiutare a individuare numerosi errori e inaccortezze.
-
-## Compilatori
-
-Prima di trasformare il codice sorgente in eseguibile, i compilatori fanno un'attività di analisi statica per identificare errori sintattici (o presunti tali) all'interno del codice.
-
-Il lavoro dei compilatori si può dividere solitamente in __quattro tipi di analisi__ (gli esempi sono presi dal compilatore di Rust, caratteristico per la quantità e qualità di analisi svolta durante la compilazione):
-
-- __analisi lessicale__: identifica i token appartenenti o meno al linguaggio, permettendo di individuare possibili errori di battitura;
-
-  ```txt
-  error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `ciao`
-  --> src/main.rs:2:9
-    |
-  2 |     BRO ciao = "mondo";
-    |           ^^^^ expected one of 8 possible tokens
-  ```
-
-- __analisi sintattica__: controlla che i token identificati siano in relazioni _sensate_ tra di loro in base alla grammatica del linguaggio, scovando così possibili errori di incomprensione del linguaggio;
-
-  ```txt
-  error: expected `{`, found keyword `for`
-  --> src/main.rs:2:14
-    |
-  2 |     if !expr for;
-    |              ^^^ expected `{`
-    |
-  ```
-
-- __controllo dei tipi__: nei linguaggi tipizzati, individua violazioni di regole d'uso dei tipi ed eventuali incompatibilità tra tipi di dati;
-
-  ```txt
-  error[E0308]: mismatched types
-  --> src/main.rs:2:24
-    |
-  2 |     let name: String = 42;
-    |               ------   ^^- help: try using a conversion method: `.to_string()`
-    |               |        |
-    |               |        expected struct `String`, found integer
-    |               expected due to this
-
-  For more information about this error, try `rustc --explain E0308`.
-  ```
-
-- __analisi flusso dei dati__: si cercano di rilevare problemi relativi all'_evoluzione dei valori_ associati alle variabili, come per esempio porzioni di codice non raggiungibili.
-
-  ```txt
-  error: equal expressions as operands to `!=`
-  --> src/main.rs:2:8
-    |
-  2 |     if 1 != 1 {
-    |        ^^^^^^
-    |
-  ```
-
-Se i primi tre tipi di analisi sono abbastanza facili da comprendere, l'ultimo merita una maggiore attenzione, motivo per cui gli dedichiamo il prossimo paragrafo.
-
-## Analisi Data Flow
-
-Nata nell'ambito dell'__ottimizzazione dei compilatori__, che per migliorare le proprie performance ricercavano porzioni di codice non raggiungibile da non compilare, l'__analisi del flusso di dati__ è stata più avanti imbracciata dall'ingegneria del software per ricercare e prevenire le cause di errori simili. \\
-Parlando di flusso dei dati si potrebbe pensare a un'analisi prettamente dinamica come il testing, ma l'insieme dei controlli statici che si possono fare sul codice per comprendere come vengono _utilizzati_ i valori presenti nel programma è invece particolarmente significativo.
-
-È possibile infatti analizzare staticamente il tipo delle operazioni eseguite su una variabile e l'__insieme dei legami di compatibilità__ tra di esse per determinare se il valore in questione viene usato in maniera _semanticamente sensata_ all'interno del codice. \\
-Nello specifico, le operazioni che possono essere eseguite su un __dato__ sono solamente di tre tipi:
-
-<!-- KaTeX op macro definitions -->
-<div style="display: none; margin: 0;">
-$$
-\require{color}
-% Regular operations
-\def\op#1{
-  \fcolorbox{black}{white}{$\vphantom{d} \sf{#1}$}
-}
-\def\d{\op{d} \,}
-\def\a{\op{a} \,}
-\def\u{\op{u} \,}
-% Erroneous operations
-\def\opR#1{
-  \fcolorbox{black}{orangered}{$\vphantom{d} \color{white}{\sf{#1}}$}
-}
-\def\dR{\opR{d} \,}
-\def\aR{\opR{a} \,}
-\def\uR{\opR{u} \,}
-% Subscript operations
-\def\Op#1#2{
-  \fcolorbox{black}{white}{$\vphantom{d_6} \sf{#1}_{#2}$}
-}
-\def\D#1{\Op{d}{#1} \,}
-\def\A#1{\Op{a}{#1} \,}
-\def\U#1{\Op{u}{#1} \,}
-% Warning subscript operations
-\def\OpW#1#2{
-  \fcolorbox{black}{orange}{$\vphantom{d_6} \sf{#1}_{#2}$}
-}
-% Green subscript operations
-\def\OpG#1#2{
-  \fcolorbox{black}{lightgreen}{$\vphantom{d_6} \sf{#1}_{#2}$}
-}
-\def\DG#1{\OpG{d}{#1} \,}
-\def\AG#1{\OpG{a}{#1} \,}
-\def\UG#1{\OpG{u}{#1} \,}
-% Error
-\def\Err{
-  \color{red}{\sf{ERROR}}
-}
-\def\err{
-  \, \Err
-}
-$$
-</div>
-
-- $$\op{d}$$ (__definizione__): il comando __assegna un valore__ alla variabile; anche il passaggio del dato come parametro ad una funzione che lo modifica è considerata un'operazione di (ri)definizione;
-
-- $$\op{u}$$ (__uso__): il comando __legge il contenuto__ di una variabile, come per esempio l'espressione sul lato destro di un assegnamento;
-
-- $$\op{a}$$ (__annullamento__): al termine dell'esecuzione del comando il valore della variabile __non è significativo/affidabile__.
-  Per esempio, dopo la _dichiarazione senza inizializzazione_ di una variabile e al termine del suo _scope_ il valore è da considerarsi inaffidabile.
-
-Dal punto di vista di ciascuna variabile è possibile ridurre una qualsiasi sequenza d'istruzioni (_ovvero un cammino sul diagramma di flusso_) a una sequenza di definizioni, usi e annullamenti.
-
-### Regole
-
-Fatta questa semplificazione è allora possibile individuare la presenza di anomalie nell'uso delle variabili definendo alcune __regole di flusso__: alcune di queste devono essere necessariamente rispettate in un programma corretto (1 e 3), mentre altre hanno più a che fare con la semantica dell'uso di un valore (2).
-
-<ol>
-
-<li markdown="1">
-  L'**uso di una variabile** deve essere **sempre preceduto** in ogni sequenza **da una definizione senza annullamenti intermedi**.
-
-  $$
-  \a\u\err
-  $$
-
-</li>
-<li markdown="1">
-  La **definizione di una variabile** deve essere **sempre seguita** da **un uso**, **prima** di un suo **annullamento** o nuova **definizione**.
-
-  $$
-  \d\a\err \\
-  \d\d\err
-  $$
-
-</li>
-  <li markdown="1">
-  L'**annullamento di una variabile** deve essere **sempre seguito** da **una definizione**, **prima** di un **uso** o **altro annullamento**.
-  
-  $$
-  \a\a\err
-  $$
-
-</li>
-</ol>
-
-Riassumendo, $$\a\op{u}$$, $$\d\op{a}$$, $$\d\op{d}$$ e $$\a\op{a}$$ sono sequenze che identificano __situazioni anomale__, anche se non necessariamente dannose: se per esempio usare una variabile subito dopo averla annullata rende l'esecuzione del programma non controllabile, un annullamento subito dopo una definizione non crea nessun problema a runtime, ma è altresì indice di un possibile errore concettuale.
-
-<table align="center" style="width: 50%">
-<tr>
-  <th></th>
-  <th markdown="1">$$\a$$</th>
-  <th markdown="1">$$\d$$</th>
-  <th markdown="1">$$\u$$</th>
-</tr>
-<tr>
-  <th markdown="1">$$\a$$</th>
-  <th markdown="1">$$\Err$$</th>
-  <th markdown="1"></th>
-  <th markdown="1">$$\Err$$</th>
-</tr>
-<tr>
-  <th markdown="1">$$\d$$</th>
-  <th markdown="1">$$\Err$$</th>
-  <th markdown="1">$$\Err$$</th>
-  <th markdown="1"></th>
-</tr>
-<tr>
-  <th markdown="1">$$\u$$</th>
-  <th markdown="1"></th>
-  <th markdown="1"></th>
-  <th markdown="1"></th>
-</tr>
-</table>
-
-#### Esempio
-
-Consideriamo la seguente funzione C con il compito di scambiare il valore di due variabili:
-
-```c
-void swap(int &x1, int &x2) {
-    int x1;
-    x3 = x1;
-    x3 = x2;
-    x2 = x1;
-}
-```
-
-Analizzando il codice, le sequenze per ogni variabile sono le seguenti:
-
-| Variabile | Sequenza | Anomalie |
-|-|-|-|
-| `x1` | $$\aR\uR\u\a$$ | `x1` viene usata 2 volte senza essere stata prima definita |
-| `x2` | $$\dots \d\u\op{d} \dots$$ | Nessuna |
-| `x3` | $$\dots \d\dR\opR{d} \dots$$ | `x3` viene definita più volte senza nel frattempo essere stata usata |
-
-Come si vede, in un codice sintatticamente corretto l'analisi Data Flow ci permette quindi di scovare un errore semantico osservando le sequenze di operazioni sulle sue variabili.
-
-### Sequenze Data Flow
-
-Abbiamo accennato più volte al concetto di "sequenza" di operazioni su una variabile.
-Più formalmente, definiamo __sequenza__ di operazioni per la variabile $$\mathtt{a}$$ secondo il cammino $$p$$ la concatenazione della tipologia delle istruzioni che coinvolgono tale variabile, e la indichiamo con $$\operatorname{P}(p, \, \mathtt{a})$$.
-
-Considerando per esempio il seguente programma C:
-
-```c
-01  void main() {
-02      float a, b, x, y;
-03      read(x);
-04      read(y);
-05      a = x;
-06      b = y;
-07      while (a != b)
-08          if (a > b)
-09              a = a - b;
-10          else
-11              b = b - a;
-12      write(a);
-13  }
-```
-
-possiamo dire che:
-
-$$
-\begin{align*}
-&\operatorname{P}([1, 2, 3, 4, 5, 6, 7, 8, 9, 7, 12, 13], \, \mathtt{a}) \\
-&= \A{2} \D{5} \U{7} \U{8} \U{9} \D{9} \U{7} \U{12} \A{13}
-\end{align*}
-$$
-
-Eseguendo questo tipo di operazione su tutte le variabili e per tutti i cammini del programma si potrebbe verificare la presenza eventuali anomalie, ma come sappiamo __i cammini sono potenzialmente infiniti__ quando il programma contiene cicli e decisioni: per scoprire quali percorsi segue effettivamente l'esecuzione del programma dovremmo eseguirlo e quindi uscire dal campo dell'analisi statica.
-
-#### Espressioni regolari
-
-Tuttavia non tutto è perduto: un caso di cammini contenenti __cicli__ e __decisioni__ è possibile rappresentare un insieme di sequenze ottenute dal programma $$P$$ utilizzando delle __espressioni regolari__.
-Con $$\operatorname{P}([1 \rightarrow], \, \mathtt{a})$$ si indica infatti l'espressione regolare che rappresenta __tutti i cammini__ che partono dall'istruzione $$1$$ per la variabile $$\mathtt{a}$$.
-
-Questo perché nelle espressioni regolari è possibile inserire, oltre che una serie di parentesi che isolano sotto-sequenze, anche due simboli molto particolari:
-
-- la __pipe__ (\|), che indica che i simboli (o le sotto-sequenze) alla propria destra e alla propria sinistra si _escludono_ a vicenda: _una e una sola_ delle due è presente;
-- l'__asterisco__ (\*), che indica che il simbolo (o la sotto-sequenza) precedente può essere _ripetuto da 0 a $$n$$ volte_.
-
-Grazie a questi simboli è possibile rappresentare rispettivamente decisioni e cicli.
-Prendendo per esempio il codice precedente, è possibile costruire $$\operatorname{P}([1 \rightarrow], \, \mathtt{a})$$ come:
-
-$$
-\begin{align*}
-&\A{2} \D{5} & & &&&  && && & & \\
-&\A{2} \D{5} &\U{7} &\Big( &\phantom{\U8} &&\textit{while body} &&\phantom{\U{7}} &&\Big)* &\quad \quad \U{12} &\A{13} \\
-&\A{2} \D{5} &\U{7} &\Big( &\U{8} &&\textit{if body} &&\phantom{\U{7}} &&\Big)* &\quad \quad \U{12} &\A{13} \\
-&\A{2} \D{5} &\U{7} &\Big( &\U{8} &&\Big(\, \U{9} \D{9} \Big | \: \U{11} \Big) && &&\Big)* &\quad \quad \U{12} &\A{13} \\
-&\A{2} \D{5} &\OpW{u}{7} \, &\Big( \, &\U{8} &&\Big(\, \U{9} \D{9} \Big | \: \U{11} \Big)
-  &&\OpW{u}{7} \,
-&&\Big)* &\quad \quad \U{12} &\A{13}
-\end{align*}
-$$
-
-Osserviamo come $$\OpW{u}{7}$$ si ripeta due volte: questo può rendere _fastidioso_ ricercare errori, per via della difficoltà di considerare cammini multipli.
-Comunque sia, una volta ottenuta un'espressione regolare è facile verificare l'eventuale presenza di errori applicando le solite regole (nell'esempio non ce n'erano).
-
-Bisogna però fare attenzione a un'aspetto: le espressioni regolari così costruite rappresentano __tutti i cammini__ possibili del programma, ma __non tutti e i soli__!
-Trattandosi di oggetti puramente matematici, infatti, le espressioni regolari sono necessariamente _più generali_ di qualunque programma: esse non tengono infatti conto degli _effetti_ che le istruzioni hanno sui dati e delle relative proprietà che si possono inferire. \\
-Riprendendo a esempio l'espressione regolare di cui sopra, essa contiene la sequenza nella quale il ciclo viene eseguito _infinite volte_, ma osservando il programma è facile indovinare che tale comportamento non sia in realtà possibile: diminuendo progressivamente $$\mathtt{a}$$ e $$\mathtt{b}$$ a seconda di chi sia il maggiore si può dimostrare che prima o poi i due convergeranno allo stesso valore permettendo così di uscire dal ciclo.
-
-In effetti, uno stesso programma può essere rappresentato tramite __un numero infinito di espressioni regolari__ valide.
-Si potrebbe addirittura argomentare che l'espressione regolare
-
-$$
-\Big ( \, \u \Big | \: \d \Big | \: \a \Big)*
-$$
-
-possa rappresentare qualsiasi programma. \\
-Allontanandosi però dai casi estremi, si dimostra essere impossibile scrivere un algoritmo che dato un qualsiasi programma riesca a generare un'espressione regolare che rappresenti __tutti e soli__ i suoi cammini possibili senza osservare i valori delle variabili.
-Bisogna dunque accontentarsi di trovare espressioni regolari che rappresentino __al meglio__ l'esecuzione del programma, ovvero con il minor numero di cammini impossibili rappresentati.
-
-Nell'analisi Data Flow tramite espressioni regolari è quindi necessario tenere conto che il modello generato è un'__astrazione pessimistica__: se viene notificata la presenza di un errore non si può essere certi che esso ci sia veramente, in quanto esso potrebbe derivare da un cammino non percorribile.
-
-## Analisi statica e Testing
-
-Oltre ad essere un processo utile di per sé per il rilevamento di potenziali errori, l'__analisi statica__ può anche contribuire a guidare l'attività di __testing__. \\
-Per capire come, osserviamo che a partire dall'analisi statica è possibile fare le seguenti osservazioni:
-
-- perché si presenti un malfunzionamento dovuto a una anomalia in una _definizione_, deve esserci un _uso_ che si serva del valore assegnato;
-- un ciclo dovrebbe essere ripetuto (di nuovo) se verrà _usato_ un valore _definito_ alla iterazione precedente.
-
-L'analisi statica può quindi aiutare a __selezionare i casi di test__ basandosi sulle _sequenze definizione-uso_ delle variabili, costruendo cioè dei nuovi criteri di copertura.
-
-### Terminologia
-
-Per rendere più scorrevole la spiegazione dei prossimi argomenti introduciamo innanzitutto un po' di terminologia.
-
-Dato un nodo $$i$$ del diagramma di flusso (_un comando/riga del programma_), chiamiamo $$\operatorname{def}(i)$$ l'__insieme delle variabili definite in__ $$\bf{i}$$.
-
-Data invece una variabile $$x$$ e un nodo $$i$$, chiamiamo $$\operatorname{du}(x, \, i)$$ l'insieme dei nodi $$j$$ tali che:
-
-- $$x \in \operatorname{def}(i)$$, ovvero la variabile $$x$$ è __definita__ in $$i$$;
-- $$x$$ è __usata__ nel nodo $$j$$;
-- __esiste un cammino__ da $$i$$ a $$j$$ __libero da definizioni__ di $$x$$, ovvero che se seguito non sovrascrive il valore di $$x$$.
-
-Si tratta cioè dell'__insieme di nodi $$\bf{j}$$ che _potrebbero_ usare il valore di $$\bf{x}$$ definito in $$\bf{i}$$__.
-
-### Criteri di copertura derivati dall'analisi statica
-
-#### Criterio di copertura delle definizioni
-
-_Un test $$\ T$$ soddisfa il __criterio di copertura delle definizioni__ se e solo se per ogni nodo $$i$$ e ogni variabile $$x \in \operatorname{def}(i)$$, $$T$$ include un caso di test che esegue un cammino libero da definizioni da $$i$$ ad __almeno uno__ degli elementi di $$\operatorname{du}(i, x).$$_
-
-Ci si vuole cioè assicurare di testare tutte le definizioni, assicurandosi che funzionino osservando almeno un uso del valore da loro assegnato.
-Matematicamente si può dire che:
-
-$$
-\begin{align*}
-T \in C_{def} \Longleftrightarrow& \forall i \in P, \  \forall x \in \operatorname{def}(i), \ \exists j \in \operatorname{du}(i, \, x) \:, \\
-& \: \exists t \in T \ \text{che esegue un cammino da $i$ a $j$ senza ulteriori definizioni di $x$}.
-\end{align*}
-$$
-
-Riconsideriamo l'esempio già visto in precedenza, considerando la variabile $$\mathtt{a}$$:
-
-```c
-01  void main() {
-02      float a, b, x, y;
-03      read(x);
-04      read(y);
-05      a = x;
-06      b = y;
-07      while (a != b)
-08          if (a > b)
-09              a = a - b;
-10          else
-11              b = b - a;
-12      write(a);
-13  }
-```
-
-Partiamo definendo gli insiemi dei nodi degli usi $$\operatorname{du}(i, \, \mathtt a)$$:
-
-1. $$\operatorname{du}(5, \, \mathtt a)$$ = $$\{7, \, 8, \, 9, \, 11, \, 12\}$$;
-2. $$\operatorname{du}(9, \, \mathtt a)$$ = $$\{7, \, 8, \, 9, \, 11, \, 12\}$$.
-
-È solo __un caso__ il fatto che in questo esempio tali insiemi siano uguali. \\
-Comunque sia, l'obiettivo è _per ognuna delle due definizioni_ ottenere un __uso__ di tale definizione:
-
-1. Per la prima definizione la soluzione è banale, a riga 7 la variabile $$\mathtt a$$ viene letta sempre:
-$$\D{5}\U{7}$$.
-2. Per la seconda, invece, è necessario scegliere un valore tale per cui il flusso di esecuzione entri almeno una volta nel ciclo ed esegua almeno una volta la riga 9:
-$$\D{9}\U{7}$$.
-
-Un test che soddisfa totalmente il criterio può essere il seguente:
-
-$$
-T = \{ \langle 8, \, 4 \rangle \}.
-$$
-
-Come si vede, il criterio di copertura delle definizioni non copre tutti i comandi e di conseguenza __non implica il criterio di copertura dei comandi__.
-
-#### Criterio di copertura degli usi
-
-_Un test $$\ T$$ soddisfa il __criterio di copertura degli usi__ se e solo se per ogni nodo $$i$$ e ogni variabile $$x$$ appartenente a $$\operatorname{def}(i)$$, $$T$$ include un caso di test che esegue un cammino libero da definizioni da $$i$$ ad __ogni elemento__ di $$\operatorname{du}(i, \, x).$$_
-
-Sembra simile al precedente, con la differenza che ora bisogna coprire __tutti__ i potenziali usi di una variabile definita.
-Questo appare ancora più chiaro osservando la formula matematica:
-
-$$
-\begin{align*}
-T \in C_{path} \Longleftrightarrow& \forall i \in P, \  \forall x \in \operatorname{def}(i), \ \forall j \in \operatorname{du}(i, \, x) \:, \\
-& \: \exists t \in T \ \text{che esegue un cammino da $i$ a $j$ senza ulteriori definizioni di $x$}.
-\end{align*}
-$$
-
-Si noti però che il criterio di copertura degli usi __non implica il criterio di copertura delle definizioni__, perché nel caso in cui non esistano $$j \in \operatorname{du}(i, \, x)$$ l'uso del $$\forall$$ è più _"permissivo"_ del $$\exists$$ del criterio precedente: quest'ultimo richiedeva infatti che per ogni definizione esistesse almeno un uso, mentre il criterio di copertura degli usi non pone tale clausola (_se non ci sono usi il $$\forall$$ è sempre vero_).
-Viene quindi da sé che questo criterio non copre neanche il criterio di copertura dei comandi.
-
-Riconsideriamo nuovamente il programma in C visto in precedenza come esempio:
-
-```c
-01  void main() {
-02      float a, b, x, y;
-03      read(x);
-04      read(y);
-05      a = x;
-06      b = y;
-07      while (a != b)
-08          if (a > b)
-09              a = a - b;
-10          else
-11              b = b - a;
-12      write(a);
-13  }
-```
-
-Come prima, consideriamo la variabile $$\mathtt a$$ e i relativi insieme dei nodi degli usi per ogni sua definizione:
-
-1. $$\operatorname{du}(5, \, \mathtt a)$$ = $$\{7, \, 8, \, 9, \, 11, \, 12\}$$;
-2. $$\operatorname{du}(9, \, \mathtt a)$$ = $$\{7, \, 8, \, 9, \, 11, \, 12\}$$.
-
-Per ogni definizione occorre coprire __tutti gli usi__:
-
-<style>
-  #criterio-usi-tabella {
-    text-align: center;
-  }
-  #criterio-usi-tabella p {
-    margin-bottom: 0;
-  }
-</style>
-
-<table id="criterio-usi-tabella" style="text-align: center;">
-<tr>
-  <th style="width: 50%" markdown="1">
-    $$\operatorname{du}(5, \, \mathtt a)$$
-  </th>
-  <th markdown="1">$$\operatorname{du}(9, \, \mathtt a)$$</th>
-</tr>
-<tr>
-  <td markdown="1">$$\D{5}\UG{7}\UG{8}\UG{11}\U{7}\UG{12}$$
-  </td>
-  <td markdown="1">$$\dots \, \D{9} \UG7 \UG8 \UG9 \dots$$
-  </td>
-</tr>
-<tr>
-  <td markdown="1">$$\dots \, \D5 \U7 \U8 \UG9 \dots$$
-  </td>
-  <td markdown="1">$$\dots \, \D9 \U7 \U8 \UG{12} \dots$$
-  </td>
-</tr>
-<tr>
-  <td></td>
-  <td markdown="1">$$\dots \, \D9 \U7 \U8 \UG{11} \dots$$
-  </td>
-</tr>
-</table>
-
-Un test che soddisfa totalmente il criterio può essere il seguente:
-
-$$
-T = \{ \langle 4, \,  8 \rangle, \, \langle 12, \, 8 \rangle, \, \langle 12, \, 4 \rangle \}.
-$$
-
-Questo esempio permette di notare qualcosa sulla natura dei cicli: dovendo testare ogni percorso al loro interno è necessario fare almeno due iterazioni.
-Può quindi sorgere un dubbio: è meglio che le due iterazioni siano fatte nello stesso caso di test o in casi test separati? Ovvero, è meglio __minimizzare__ i __casi di test__ o le __iterazioni per caso__? \\
-Opinione diffusa è quella secondo cui è preferibile __minimizzare le iterazioni__: partizionando le casistiche in diversi casi di test è possibile rilevare con più precisione gli errori, riducendo il tempo di debug.
-In alcune situazioni però aumentare il numero di iterazioni può diminuire il tempo di esecuzione totale dei test, in quanto dovendo riavviare il programma per ciascun caso di test la somma dei tempi di startup può diventare significativa per software molto massicci.
-
-#### Criterio di copertura dei cammini DU
-
-Nel criterio precedente si richiedeva di testare _un_ cammino da ogni definizione ad ogni suo uso, ma come sappiamo i cammini tra due istruzioni di un programma possono essere molteplici.
-Potrebbe dunque sorgere l'idea di testarli _tutti_: da questa intuizione nasce il __criterio di copertura dei cammini DU__.
-
-$$
-\begin{align*}
-T \in C_{pathDU} \Longleftrightarrow& \forall i \in P, \  \forall x \in \operatorname{def}(i), \ \forall j \in \operatorname{du}(i, \, x), \\
-&\forall \text{ cammino da $i$ a $j$ senza ulteriori definizioni di $x$} \\
-& \exists t \in T \ \text{che lo esegue}.
-\end{align*}
-$$
-
-Questo criterio può essere __utile da ipotizzare__, ma a causa dell'esplosione combinatoria del numero dei cammini è considerato __impraticabile__ (_"sopra la barra rossa"_).
-
-### Oltre le variabili
-
-L'analisi del flusso dati si può estendere anche su altri _"oggetti"_, non solo variabili. \\
-Per esempio, è possibile prevedere le seguenti operazioni su un __file__:
-
-- $$\op{a}$$ (__apertura__): specializzata in _per lettura_ o _per scrittura_;
-- $$\op{c}$$ (__chiusura__);
-- $$\op{l}$$ (__lettura__);
-- $$\op{s}$$ (__scrittura__).
-
-Date queste operazioni si possono individuare una serie di regole, come per esempio:
-
-1. $$\op{l}$$, $$\op{s}$$ e $$\op{c}$$ devono essere precedute da $$\op{a}$$ senza $$\op{c}$$ intermedie;
-2. $$\op{a}$$ deve essere seguita da $$\op{c}$$ prima di un'altra $$\op{a}$$;
-3. legami tra tipo di apertura (per lettura o per scrittura) e relative operazioni.
-
-È interessante notare il __legame__ tra l'attività di analisi del flusso di dati e i diagrammi UML a stati finiti: un _oggetto_ risponde a una certa _tipologia di eventi_, può essere in diversi _stati_ e in certi _stati_ non sono ammesse alcune _operazioni_.
-Si noti come nessuna delle due discipline entra comunque nel merito del valore delle variabili, relegato ad un'analisi a runtime.
-
-#### Criterio di _copertura del budget_
-
-Molto spesso nei contesti reali l'unico criterio applicato è quello di __copertura del budget__: si continuano a creare casi di test finché non sono finite le risorse (tempo e soldi).
-Questa tecnica ovviamente non fornisce alcuna garanzia sull'efficacia dei test, ed è sicuramente sconsigliata.
-
-# Tecniche di review
-
-Finora abbiamo esplorato tecniche più o meno _automatiche_ per la ricerca di errori, che stimolavano il programma con specifici input o ne analizzavano il codice per individuare potenziali anomalie. \\
-Tuttavia, alcuni tipi di errori non possono essere rilevati con questi metodi: si tratta soprattutto errori legati a _incomprensione delle specifiche_.
-Del resto, attività come il testing richiedono che il programmatore fornisca l'output "corretto" che si aspetta dal programma che viene confrontato con quello effettivo per scovare eventuali differenze: se chi scrive il codice non comprende in primo luogo _cosa_ dovrebbe fare il suo software non c'è modo di individuare l'errore.
-
-Per questo motivo molto spesso il codice viene sottoposto ad un'attività di __review__, in cui un operatore umano ne analizza la struttura e il funzionamento: egli sarà chiaramente in grado di cogliere una serie di __errori semantici__ che sfuggono alla comprensione dei tool automatici di test.
-Spesso questa mansione viene svolta da un __team di testing__ separato dal team di sviluppo: non solo questo promuove l'effettiva ricerca di errori (_mentre gli sviluppatori avrebbero tutto l'interesse di non trovarne nessuno_), ma sottopone il software a uno sguardo esterno più critico e imparziale.
-
-Anche per la review esistono una serie di tecniche: vediamone quindi le principali.
-
-## Bebugging
-
-Talvolta può capitare che il team di testing __non trovi errori__ nel programma sotto osservazione.
-Oltre ad essere scoraggiante per chi esegue la review questo è spesso indice del fatto che tale attività non viene svolta in maniera corretta, poiché raramente un programma è effettivamente corretto al 100% dopo la prima scrittura.
-
-Un metodo efficace per risolvere questo problema è il cosiddetto __bebugging__, una tecnica secondo la quale gli sviluppatori __inseriscono deliberatamente $$\bf{n}$$ errori__ nel codice prima di mandarlo in analisi al team di testing, a cui viene comunicato il numero $$n$$ di errori da trovare.
-L'ovvio vantaggio di questa tecnica è l'__incentivo__ per il team di testing a continuare a cercare errori, facendo sì che durante la ricerca ne vengano scovati molti altri non ancora noti.
-
-La metrica utilizzata per valutare l'efficacia del testing tramite questa tecnica è dunque la __percentuale di errori trovati__ tra quelli inseriti artificialmente, che può fornire un'indicazione della frazione di errori che il team di testing è in grado di trovare.
-Se per esempio il team di sviluppo ha aggiunto 10 bug _"artificiali"_ e durante il testing ne vengono trovati 8 più 2 non noti, si può supporre che il team di review riesce a trovare l'_80% degli errori_ e che quindi ce ne è ancora un altra porzione di errori _reali_ da scovare. \\
-Bisogna però essere molto cauti nel fare considerazioni di questo tipo: è possibile che gli errori immessi artificialmente siano __troppo facili__ o __troppo difficili__ da trovare, per cui conviene sempre prendere tutto con le pinze.
-
-## Analisi mutazionale
-
-Una evoluzione del bebugging è l'__analisi mutazionale__.
-Dato un programma $$P$$ e un test $$T$$ (_insieme di casi di test_), viene generato un insieme di programmi $$\Pi$$ _simili_ al programma $$P$$ in esame: tali programmi prendono il nome di __mutanti__. \\
-Si esegue poi il test $$T$$ su ciascun mutante: se $$P$$ era corretto i programmi in $$\Pi$$ __devono essere sbagliati__, ovvero devono produrre un __risultato diverso__ per almeno un caso di test $$t \in T$$.
-Se così non fosse, infatti, vorrebbe dire che il programma $$P$$ non viene opportunamente testato nell'aspetto in cui si discosta dal mutante che non ha sollevato errori, per cui non si può essere sicuri della sua correttezza.
-Non viene cioè testata la correttezza del programma, ma piuttosto __quanto il test è approfondito__.
-
-Si può quindi valutare la capacità di un test di rilevare le differenze introdotte nei mutanti tramite un nuovo criterio di copertura, che prende il nome di __criterio di copertura dei mutanti__.
-
-### Criterio di copertura dei mutanti
-
-_Un test $$\ T$$ soddisfa il __criterio di copertura dei mutanti__ se e solo se per ogni mutante $$\pi \in \Pi$$ esiste almeno un caso di test $$t \in T$$ la cui esecuzione produca per $$\pi$$ un risultato diverso da quello prodotto da $$P$$_.
-
-La metrica di valutazione di questo criterio è la __frazione di mutanti $$\pi$$ riconosciuta come diversa__ da $$P$$ sul totale di mutanti generati.
-Se non tutti i mutanti vengono scovati sarà necessario aggiungere dei casi di test che li riconoscano.
-
-I tre passi da seguire per costruire un test tramite l'analisi mutazionale sono quindi:
-
-1. __analisi__ delle classi e generazione dei mutanti;
-2. __selezionare__ dei casi di test da aggiungere a $$T$$, in base alla metrica di cui sopra;
-3. __esecuzione__ dei casi di test sui mutanti, pensando anche alle performance;
-
-Analizziamo ciascuno di tali step in maggior dettaglio.
-
-### Generazione dei mutanti
-
-Idealmente i mutanti generati dovrebbero essere il __meno differenti possibile__ dal programma di partenza, ovvero dovrebbe esserci __un mutante per ogni singola anomalia__ che sarebbe possibile inserire nel programma.
-
-Questo richiederebbe però di generare __infiniti__ mutanti, mentre per mantenere la suite di test _eseguibile in tempi ragionevoli_ il numero di mutanti non dovrebbe essere troppo elevato: un centinaio è una buona stima, ma un migliaio sarebbe auspicabile. \\
-Visto il numero limitato è necessario dunque concentrarsi sulla "__qualità__" dei mutanti generati, dove i mutanti sono tanto più buoni quanto più permettono di scovare degli errori.
-Per questo motivo vengono creati degli specifici _operatori_ che dato un programma restituiscono dei mutanti _utili_.
-
-#### Operatori mutanti
-
-Come già accennato, gli __operatori mutanti__ sono delle funzioni (_o piccoli programmi_) che dato un programma $$P$$ generano un insieme di mutanti $$\Pi$$.
-Essi operano eseguendo piccole __modifiche sintattiche__ che modifichino la __semantica del programma__ senza però causare errori di compilazione.
-
-Tali operatori si distinguono in __classi__ in base agli oggetti su cui operano:
-
-- __costanti__ e __variabili__, per esempio scambiando l'occorrenza di una con l'altra;
-- __operatori__ ed __espressioni__, per esempio trasformando `<` in `<=`, oppure `true` in `false`;
-- __comandi__, per esempio trasformando un `while` in `if`, facendo così eseguire il ciclo una sola volta.
-
-Alcuni operatori possono essere anche specifici su alcuni tipi di applicazioni, come nel caso di:
-
-- operatori per __sistemi concorrenti__: operano principalmente sulle primitive di sincronizzazione – come eseguire una `notify()` invece che una `notifyAll()`;
-- operatori per __sistemi object-oriented__: operano principalmente sulle interfacce dei moduli.
-
-Poiché la generazione dei mutanti è un'attività tediosa, il compito di applicare questi operatori viene spesso affidato a tool automatici.
-Esistono però numerosi __problemi di prestazioni__, in quanto per ogni mutante occorre modificare il codice, ricompilarlo, controllare che non si sovrapponga allo spazio di compilazione delle classi di altri mutanti e fare una serie di altre operazioni che comportano un pesante overhead.
-Per questo motivo i tool moderni lavorano spesso sull'__eseguibile__ in sé (_sul bytecode nel caso di Java_): sebbene questo diminuisca il lavoro da fare per ogni mutante è possibile che il codice eseguibile così ottenuto sia un programma che non sarebbe possibile generare tramite compilazione.
-Si espande quindi l'universo delle possibili anomalie anche a quelle _non ottenibili_, un aspetto che bisognerà tenere in considerazione nella valutazione della metrica di copertura.
-
-#### High Order Mutation
-
-Normalmente i mutanti vengono generati introducendo una _singola modifica_ al programma originale.
-Nella variante __HOM__ (__High Order Mutation__) si applicano invece modifiche a __codice già modificato__.
-
-La giustificazione per tale tecnica è che esistono alcuni casi in cui trovare errori dopo aver applicato più modifiche è _più difficile_ rispetto ad applicarne solo una.
-Può essere che un errore mascheri parzialmente lo stato inconsistente dell'altro rendendo più difficile il rilevamento di malfunzionamenti, cosa che porta a generare test ancora più approfonditi.
-
-### Automatizzare l'analisi mutazionale
-
-Generalmente nel testing gli unici due _outcomes_ sono _risultato corretto_ o _non corretto_ e la metrica è una misura della correttezza del programma.
-Il discriminante delle tecniche di analisi mutazionale è invece il numero di casi di test che forniscono un risultato ___diverso___ da quello di $$P$$, indipendentemente dalla correttezza (di entrambi).
-
-Come già detto, trovare errori con queste tecniche (specialmente l'HOM) misura quindi il __livello di approfondimento__ dei casi di test e __non__ la __correttezza__ del programma di partenza. \\
-Prescindere dalla _correttezza_ dei risultati ha però un aspetto positivo: per eseguire l'analisi mutazionale non è necessario conoscere il comportamento corretto del programma, eliminando la necessità di un _oracolo_ che ce lo fornisca.
-Si può quindi misurare la bontà di un insieme casi di test __automatizzando la loro creazione__: come già detto precedentemente, occorre però vigilare sulla __proliferazione del numero di esecuzioni__ da effettuare per completare il test – un caso di test dà infatti origine a $$n+1$$ esecuzioni, dove $$n$$ è il numero di mutanti.
-
-Il seguente diagramma di flusso visualizza quindi l'attività __facilmente automatizzabile__ di analisi mutazionale:
-
-{% responsive_image path: 'assets/13_analisi-mutazionale-schema.png' %}
-
-Benché semplice, questo algoritmo __non garantisce la terminazione__ per una serie di motivi:
-
-- quando si estrae un caso di test casuale, c'è sempre il rischio di __estrarre sempre lo stesso__;
-- si potrebbe essere _particolarmente sfortunati_ e __non trovare un caso di test utile__ in tempo breve;
-- __esistono infinite varianti__ di programmi __funzionalmente identici__ ma __sintatticamente diversi__, ovvero che svolgono la stessa funzione anche se sono diversi: una modifica sintattica potrebbe non avere alcun effetto sul funzionamento del programma, come per esempio scambiare `<` con `<=` in un algoritmo di ordinamento.
-  In tal caso, nessun nuovo caso di test permetterebbe di coprire il mutante, in quanto esso restituirebbe sempre lo stesso output del programma originale.
-
-Spesso viene quindi posto un timeout sull'algoritmo dipendente sia dal tempo totale di esecuzione, sia dal numero di casi di test estratti.
-
-Per verificare la validità del test, è necessario controllare il __numero di mutanti generati__: se questo numero è elevato, il test non era affidabile.
-In alternativa, è possibile _"nascondere"_ i mutanti, a patto che non sia richiesta una copertura totale.
-In questo modo, è possibile __analizzare programmi__ che sono __funzionalmente uguali ma sintatticamente diversi__, al fine di dimostrarne l'equivalenza o scoprire casi in cui essa non è valida.
-
-# Object-oriented testing
-
-Finora abbiamo trattato i programmi come funzioni matematiche da un dominio di input a un dominio di output.
-Questo è tutto sommato vero per quanto riguarda i __linguaggi procedurali__: un programma in tale paradigma è composto da un insieme di funzioni e procedure che preso un dato in ingresso ne restituiscono uno in uscita.
-A meno di eventuali variabili globali condivise (il cui uso è comunque sconsigliato), tali funzioni sono indipendenti l'una dall'altra, e possono quindi essere _testate indipendentemente_ come fossero dei piccoli sotto-programmi.
-
-La situazione cambia per quanto riguarda invece i __linguaggi object oriented__ (__OO__), che introducono i concetti di classe e istanza: in tali linguaggi gli oggetti sono l'__unione di metodi e stato__.
-Le tecniche di testing viste finora smettono quindi di funzionare: la maggior parte delle volte testare i metodi isolatamente come funzioni da input ad output perde di senso, in quanto non si considera il contesto (lo _stato_ dell'oggetto associato) in cui essi operano.
-
-Bisogna dunque sviluppare una serie di tecniche di test specifiche per i linguaggi orientati agli oggetti, in cui l'__unità testabile__ si sposti dalle procedure alle __classi__.
-
-## Ereditarietà e collegamento dinamico
-
-Prima di capire _come_ è possibile testare un'intera classe, affrontiamo due punti critici che derivano dal funzionamento intrinseco dei linguaggi a oggetti: l'__ereditarietà__ e il __collegamento dinamico__.
-
-Partiamo dal primo e immaginiamo di avere una classe già completamente testata.
-Creando ora una sottoclasse di tale classe originale può sorgere un dubbio: _visto che i metodi ereditati sono già stati testati nella classe genitore ha senso testarli nella classe figlia?_
-Un quesito simile sorge nel caso di metodi di default appartenenti a un'interfaccia: _ha senso testare i metodi di default direttamente nell'interfaccia o è meglio testarli nelle classi concrete che implementano tale interfaccia?_ \\
-Il consenso degli esperti è di __testare nuovamente tutti i metodi ereditati__: nelle sottoclassi e nelle classi che implementano delle interfacce con metodi di default tali metodi opereranno infatti in __nuovi contesti__, per cui non vi è alcuna certezza che funzionino ancora a dovere.
-Inoltre, a causa del collegamento dinamico non è nemmeno sicuro che eseguire lo stesso metodo nella classe base significa eseguire le stesse istruzioni nella classe ereditata. \\
-In generale dunque non si eredita l'attività di testing, ma si possono invece ereditare i casi di test e i relativi valori attesi (_l'oracolo_): è perciò opportuno __rieseguire__ i casi di test anche nelle sottoclassi.
-
-Un altro motivo per cui il testing object-oriented differisce fortemente da quello per linguaggi funzionali è la preponderanza del __collegamento dinamico__, attraverso il quale le chiamate ai metodi vengono collegate a runtime in base al tipo effettivo degli oggetti.
-Dal punto di vista teorico, infatti, tale meccanismo rende difficile stabilire staticamente tutti i possibili cammini di esecuzione, complicando la determinazione dei criteri di copertura.
-
-## Testare una classe
-
-Entriamo ora nel vivo della questione.
-Per __testare una classe__:
-
-- la __si isola__ utilizzando più _classi stub_ possibili per renderla eseguibile indipendentemente dal contesto;
-- si implementano eventuali __metodi astratti__ o non ancora implementati (stub);
-- si aggiunge una funzione per permettere di estrarre ed esaminare lo stato dell'oggetto e quindi __bypassare l'incapsulamento__;
-- si costruisce una classe driver che permetta di istanziare oggetti e chiamare i metodi secondo il __criterio di copertura__ scelto.
-
-Ebbene sì, sono stati progettati dei criteri di copertura specifici per il testing delle classi.
-Vediamo dunque di cosa si tratta.
-
-### Copertura della classe
-
-I __criteri classici__ visti precedentemente (comandi, decisioni, ...) continuano a valere ma __non sono sufficienti__.
-Per testare completamente una classe occorre considerare lo __stato dell'oggetto__: in particolare, è comodo utilizzare una __macchina a stati__ che rappresenti gli _stati possibili_ della classe e le relative _transazioni_, ovvero le chiamate di metodi che cambiano lo stato.
-
-Tale rappresentazione potrebbe esistere nella documentazione o essere creato specificatamente per l'attività di testing.
-Il seguente diagramma rappresenta per esempio una macchina a stati di una classe avente due metodi, $$\mathtt{m1}$$ e $$\mathtt{m2}$$.
-
-{% responsive_image path: 'assets/13_criteri-copertura-grafo.png' %}
-
-Ottenuta una rappresentazione di questo tipo, alcuni criteri di copertura che si possono ipotizzare sono:
-
-- __coprire tutti i nodi__: per ogni __stato__ dell'oggetto deve esistere almeno un caso di test che lo raggiunge;
-- __coprire tutti gli archi__: per ogni stato e per ogni metodo deve esistere almeno un caso di test che esegue tale metodo trovandosi in tale stato;
-- __coprire tutte le coppie di archi input/output__: per ogni stato e per ogni coppia di archi entranti e uscenti deve esistere almeno un caso di test che arriva nello stato tramite l'arco entrante e lo lascia tramite l'arco uscente (consideriamo anche _come_ siamo arrivati nello stato);
-- __coprire tutti i cammini identificabili nel grafo__: spesso i cammini in questione sono infiniti, cosa che rende l'applicazione di questo criterio infattibile (_"sopra la linea rossa"_).
-
-#### Tipo di testing: white o black box?
-
-Abbiamo assunto che il diagramma degli stati facesse parte delle specifiche del progetto.
-Se così fosse, allora il testing appena descritto assume una connotazione __black box__:  il diagramma rappresenta sì la classe ma è ancora una sua __astrazione__, che non considera il codice effettivo che rappresenta lo stato o che implementa uno specifico metodo ma solo le relazioni tra i vari stati.
-
-In caso il diagramma degli stati non sia però fornito, il testing delle classi è comunque possibile!
-Attraverso tecniche di __reverse engineering__ guidate da certe euristiche (che operano ad un livello di astrazione variabile) è possibile ad __estrarre informazioni sugli stati__ di una _classe già scritta_; spesso tali informazioni non sono comprensibili per un essere umano, motivo per cui esse vengono piuttosto utilizzate da vari tool di testing automatico.
-In questo caso, però, il testing assume caratteristiche __white box__, in quanto il codice che implementava la classe era già noto prima di iniziare a testarlo.
-
-# Testing funzionale
-
-Introduciamo ora una nuova attività di testing che parte da presupposti completamente diversi rispetto a quelli del test strutturale.
-
-Il __test funzionale__ è infatti un tipo di test che si concentra sulla verifica del comportamento del programma dal punto di vista dell'__utente finale__, senza considerare il suo funzionamento interno.
-In altre parole, il test funzionale è un approccio __black box__ in cui non si ha (o non comunque non si sfrutta) la conoscenza del codice sorgente.
-
-Talvolta questo può essere l'__unico approccio possibile__ al testing, come nel caso di validazione del lavoro di un committente esterno; altre volte invece si decide volontariamente di farlo, concentrandosi sul __dominio delle informazioni__ invece che sulla struttura di controllo. \\
-Il test funzionale, che prende in considerazione le __specifiche__ (e non i requisiti) del progetto per discriminare un comportamento corretto da uno scorretto, permette infatti di identificare errori che __non possono essere individuati__ con criteri strutturali, come per esempio funzionalità non implementate, flussi di esecuzione dimenticati o errori di interfaccia e di prestazioni.
-
-## Tecniche
-
-Le tecniche di test funzionale si possono raggruppare in:
-
-- __metodi basati su grafi__: oltre alle tecniche già viste in precedenza, si può per esempio lavorare anche sui diagrammi di sequenza;
-- __suddivisioni del dominio in classi di equivalenza__: si possono raggruppare i valori del dominio che causano lo stesso comportamento in classi d'equivalenza, così da testare tutti i _comportamenti distinti_ piuttosto che tutti i possibili valori del dominio.
-Occorre fare attenzione a non fare l'inverso, ovvero a concentrarsi sui soli valori appartenenti ad una classe di equivalenza ignorando il resto;
-- __analisi dei valori limite (test di frontiera)__: si testano, tra tutti i possibili valori del dominio, quelli _"a cavallo"_ tra una categoria e l'altra, in quanto essi possono più facilmente causare malfunzionamenti;
-- __collaudo per confronto__: si confronta la nuova versione del programma con la vecchia, assicurandosi che non siano presenti regressioni.
-Non solo si possono confrontare gli eseguibili, ma anche _specifiche formali eseguibili_ che rappresentino le caratteristiche importanti del software;
-
-Non tutte le metodologie di testing funzionale ricadono però in una di queste categorie, e la più notevole è sicuramente il __testing delle interfacce__, di cui diamo un assaggio prima di passare a parlare di classi di equivalenza.
-
-### Testing delle interfacce
-
-Questa tecnica mira a testare come i vari sotto-sistemi del programma dialoghino e __collaborino__ tra loro: per "interfacce" non si intendono infatti le `interface` Java o le _signature_, ma l'insieme di funzionalità che permettono l'interoperabilità dei componenti. \\
-Esistono in particolare diversi tipi di interfacce:
-
-- a __invocazione di parametri__;
-- a __condivisione di memoria__;
-- a __metodi sincroni__;
-- a __passaggio di messaggi__.
-
-Le interfacce aderenti a ciascuna categoria possono essere analizzate in modi diversi alla ricerca di anomalie.
-Gli sbagli più comuni sono per esempio __errori nell'uso dell'interfaccia__, come il passaggio di parametri in ordine o tipo errato oppure assunzioni sbagliate circa ciò che le funzionalità richiedono (_precondizioni_), ed __errori di tempistica o di sincronizzazione__ tra componenti.
-
-### Classi di equivalenza
-
-La tecnica delle classi di equivalenza si pone l'obiettivo di dividere il dominio del programma in __classi di dati__, ovvero gruppi di valori di input che _dovrebbero_ __stimolare il programma nella stessa maniera__.
-Non si tratta quindi di classi di equivalenza degli output, ovvero valori che dati in pasto al programma forniscono lo stesso risultato, quanto piuttosto valori che dati in pasto al programma forniscono un risultato diverso ma _prodotto nello stesso modo_.
-
-Una volta individuate le classi di dati l'obiettivo sarebbe quindi di estrarre da esse casi di test in modo da testare il funzionamento del programma in tutti suoi funzionamenti standard. \\
-In realtà, dunque, si cercano di individuare casi di test che rivelino eventuali __classi di equivalenza di errori__, ovvero insiemi di valori che generano malfunzionamenti per lo stesso motivo.
-Classi di equivalenza di questo tipo sono solitamente più _"stabili"_ rispetto alle normali classi di equivalenza in quanto il risultato ottenuto, ovvero l'errore, è spesso lo stesso o molto simile.
-
-Volendo dare una definizione più formale, _una __classe di equivalenza__ rappresenta un insieme di __stati validi o non validi__ per i dati in input e un insieme di stati validi per i dati di output_, dove per dato si intendono valori, intervalli o insiemi di valori correlati. \\
-È importante comprendere anche i possibili _stati non validi_ in quanto bisogna testare che il programma reagisca bene all'input mal formattato.
-Ogni dominio avrà quindi almeno due classi di equivalenza:
-
-- la classe degli __input validi__
-- la classe degli __input non validi__
-
-Per fare un esempio si può considerare un programma che chiede in input un __codice PIN di 4 cifre__.
-Il suo dominio può quindi essere suddiviso in due semplici classi di equivalenza:
-
-1. PIN corretto;
-2. tutti i numeri di 4 cifre diversi dal PIN.
-
-Volendo fare un altro esempio, se ci si aspetta che i valori in input ricadano in un intervallo, per esempio $$[100, \, 700]$$), si possono definire la classe di equivalenza valida $$x \in [100, 700]$$ e la classe di equivalenza non valida $$x \notin [100, 700]$$.
-Per voler aumentare la granularità si può però spezzare la classe degli input non validi in due, ottenendo una classe valida e due non valide:
-
-1. $$x \in [100, 700]$$;
-2. $$x < 100$$;
-3. $$x > 700$$.
-
-Come si vede, la scelta delle classi di equivalenza da considerare non è univoca, e richiede un minimo di conoscenza di dominio.
-Alternativamente esistono delle tecniche standard di individuazione delle classi di equivalenza a partire dalle specifiche che prendono il nome di __category partition__.
-
-### Test di frontiera
-
-La tecnica dei __test di frontiera__ è _complementare_ a quella delle classi di equivalenza.
-Partendo dal presupposto che gli errori tendono ad accumularsi sui __casi limite__, ovvero quelli la cui gestione è più particolare, questa tecnica suggerisce di selezionare come casi di test non valori a caso all'interno delle classi di equivalenza, ma i valori presenti __al confine__ tra di loro.
-
-### Category partition
-
-La tecnica di __category partition__ è un metodologia che permette di caratterizzare e identificare le classi di equivalenza del dominio di un problema a partire dalle sue specifiche.
-Può essere utilizzata a __vari livelli__ a seconda che si debbano realizzare test di unità, test di integrazione e o test funzionali.
-
-Il metodo è composto da una serie di passi in sequenza:
-
-1. __analisi delle specifiche__: in questa fase vengono identificate le _unità funzionali individuali_ che possono essere verificate singolarmente; non necessariamente sono un'unica classe, è sufficiente che siano componenti facilmente separabili dal resto, sia a livello di testing che concettuale.
-Per ogni unità vengono quindi identificate delle caratteristiche (__categorie__) dei parametri e dell'ambiente in cui opera;
-2. __scegliere dei valori__: per ogni categoria, occorre scegliere quali sono i _valori sensati_ su cui fare riferimento;
-3. __determinare eventuali vincoli tra le scelte__, che non sono sempre indipendenti;
-4. __scrivere test e documentazione__.
-
-Per capire meglio ciascuna di tali fasi vediamo un'esempio di utilizzo della tecnica di _category partition_ prendendo come soggetto il comando `find` della shell Linux.
-
-#### PASSO 1 – analizzare le specifiche
-
-Per prima cosa analizziamo le specifiche del comando:
-
-> __Syntax__: `find <pattern> <file>`
->
-> The find command is used to locate one or more instances of a given pattern in a file.
-> All lines in the file that contain the pattern are written to standard output.
-> A line containing the pattern is written only once, regardless of the number of times the pattern occur in it.
->
-> The pattern is any sequence of characters whose length does not exceed the maximum length of a line in the file.
-> To include a blank in the pattern, the entire pattern must be enclosed in quotes (`"`).
-> To include a quotation mark in the pattern, two quotes (`""`) in a row must be used.
-
-Vista la relativa semplicità, `find` è un'unità funzionale individuale che può essere verificata separatamente.
-Bisogna dunque individuarne i parametri: come è chiaro dalla sintassi essi sono due, il `pattern` da cercare e il `file` in cui farlo.
-
-Ora, ciascuno di tali parametri può possedere determinate caratteristiche, ed è nostro compito in questa fase comprenderle ed estrarle. \\
-Tali caratteristiche possono essere di due tipi: __esplicite__, ovvero quelle ricavabili direttamente dalla lettura specifiche, e __implicite__, ovvero quelle che provengono dalla nostra conoscenza del dominio di applicazione e che quindi non vengono specificate.
-
-Tornando al nostro caso di studio possiamo per esempio ottenere la seguente tabella:
-
-<style>
-  #category-partition-find-table ul {
-    margin-bottom: 0;
-  }
-</style>
-
-<table id="category-partition-find-table">
-<tr>
-  <th>Oggetto</th>
-  <th>Caratteristiche esplicite</th>
-  <th>Caratteristiche implicite</th>
-</tr>
-<tr>
-  <th markdown="1">`pattern`
-  </th>
-  <td markdown="1">
-- lunghezza del pattern;
-- pattern tra doppi apici;
-- pattern contenente spazi;
-- pattern contenente apici.
-</td>
-  <td markdown="1">
-- pattern tra apici con/senza spazi;
-- più apici successivi inclusi nel pattern.
-</td>
-</tr>
-  <th markdown="1">`file` \\
-(nome)
-  </th>
-  <td style="text-align: center;"><i>(nessuna)</i></td>
-  <td markdown="1">
-- caratteri nel nome ammissibili o meno;
-- file esistente (con permessi di lettura) o meno.
-</td>
-<tr>
-  <th markdown="1">`file` \\
-(contenuto)
-  </th>
-  <td markdown="1">
-- numero occorrenze del pattern nel file;
-- massimo numero di occorrenze del pattern in una linea;
-- massima lunghezza linea.
-</td>
-  <td markdown="1">
-- pattern sovrapposti;
-- tipo del file.
-</td>
-</tr>
-</table>
-
-È importante _esplicitare le caratteristiche implicite_ dei parametri dell'unità funzionale perché __le specifiche non sono mai complete__ e solo così possiamo disporre di tutti gli elementi su cui ragionare nelle fasi successive.
-
-Si presti poi attenzione alla distinzione fatta tra il _nome_ del file e il suo _contenuto_: il primo infatti è un __parametro__ che viene passato al comando per iniziarne l'esecuzione, mentre il secondo fa parte dell'__ambiente__ in cui il comando opera ed è dunque soggetto ad una sottile distinzione concettuale.
-
-##### <big>ALPHA E BETA TESTING</big>
-
-Spesso, però, analizzare le specifiche non basta per comprendere tutte le variabili che entrano in gioco durante l'esecuzione di un programma.
-Bisogna infatti ricordare che ci sono moltissime altre caratteristiche d'ambiente che ancora __non sono state considerate__: la versione del sistema operativo, del browser, il tipo di architettura della macchina su cui gira il programma eccetera.
-
-Spesso, quindi, la fase di testing funzionale si divide in due fasi:
-
-- __alpha testing__: l'unità funzionale viene testata in-house, ovvero su una macchina all'interno dello studio di sviluppo.
-In questa fase si considerano soprattutto le caratteristiche legate alle specifiche di cui sopra;
-- __beta testing__: per testare varie _configurazioni d'ambiente_ una versione preliminare del programma viene distribuito in un _ambiente variegato_ per osservare come esso si comporta sulle macchine di diversi utenti.
-
-Per il momento, però, consideriamo solo la fase di alpha testing e le categorie ad essa relative.
-
-#### PASSO 2 – scegliere dei valori
-
-Individuate le caratteristiche dei parametri e delle variabili d'ambiente da cui l'unità funzionale dipende, che prendono il nome di __categorie__, si passa quindi alla seconda fase.
-
-In questa fase si devono identificati __tutti e i soli__ _casi significativi_ per ogni categoria, ovvero quei valori della stessa che si ritiene abbia senso testare; poiché si tratta di un compito molto soggettivo è importante in questa fase avere __esperienza__ (_know-how_) nel dominio d'applicazione.
-
-Per capire meglio di cosa stiamo parlando ritorniamo al nostro esempio e consideriamo il parametro `pattern`.
-Per ciascuna delle sue categorie possono essere individuati vari casi significativi:
-
-- __lunghezza del pattern__: vuoto, un solo carattere, più caratteri, più lungo di almeno una linea del file;
-- __presenza di apici__: pattern tra apici, pattern non tra apici, pattern tra apici errati;
-- __presenza di spazi__: nessuno spazio nel pattern, uno spazio nel pattern, molti spazi nel pattern;
-- __presenza di apici interni__: nessun apice nel pattern, un apice nel pattern, molti apici nel pattern.
-
-È interessante notare il _mantra_ già visto del "__nessuno__, __uno__, __molti__", spesso molto utile in questa fase.
-
-#### PASSO 3 – determinare i vincoli tra le scelte
-
-Trovati tutti i valori significativi delle categorie dell'unità funzionale come possiamo costruire i casi di test da utilizzare per verificarne la correttezza?
-
-Si potrebbe pensare di testare __tutte le combinazioni__ di valori significativi, facendo cioè il prodotto cartesiano tra le categorie.
-Nella pratica, però, ciò risulterebbe in un numero esagerato di casi di test: già solo nel nostro semplice esempio questi sarebbero ben 1944, decisamente __troppi__.
-
-Nel tentativo di evitare quest'esplosione combinatoria ci si accorge però che spesso le anomalie sorgono dall'interazione di __coppie__ di caratteristiche indipendentemente dal valore assunto da tutte le altre: per esempio, un problema potrebbe presentarsi se si usa il browser _Edge_ sul sistema operativo _Linux_, indipendentemente da caratteristiche quali la dimensione dello schermo, l'architettura del processore eccetera. \\
-Per ridurre il numero di casi di test si sviluppa quindi la tecnica del ___pairwise testing___, che riduce l'insieme delle configurazioni da testare a tutte le combinazioni di coppie di valori.
-È quindi presente almeno un caso di test _per ogni coppia ipotizzabile_ di valori: in rete e in Java sono presenti diversi [__strumenti__](https://www.pairwise.org/tools.html) che permettono di creare casi di test combinati con il metodo _pairwise_.
-
-Un'ulteriore tentativo di ridurre il numero di casi di test prevede di definire una serie di ___vincoli___ per la generazione delle coppie, escludendo particolari combinazioni di caratteristiche: così, per esempio si potrebbe escludere la coppia "OS == MacOs" e "browser == Edge" perché sfruttando la conoscenza di dominio sappiamo che tale browser non è disponibile sul suddetto sistema operativo. \\
-Volendo essere più precisi, la creazione di vincoli prevede un passaggio intermedio: vengono definite una serie di __proprietà__ (es. _NotEmpty_ o _Quoted_ per l'esempio su `find`) e si creano dei vincoli logici a partire da esse.
-I vincoli seguono poi una struttura tra le seguenti:
-
-- __se__: si può limitare l'uso di un valore solo ai casi in cui è definita una proprietà. Per esempio, è inutile testare il caso _"il file non esiste"_ se la proprietà _NotEmpty_ si manifesta;
-- __single__: alcune caratteristiche prese singolarmente anche se combinate con altre generano lo stesso risultato. Per esempio, se il file non contiene occorrenze del pattern cercato il risultato del programma è indipendente dal tipo di pattern cercato;
-- __error__: alcune caratteristiche generano semplicemente errore, come per esempio se si omette un parametro.
-
-#### PASSO 4 – scrivere i test
-
-Fissati i vincoli e fatti i calcoli combinatori si procede ad enumerare iterativamente tutti i casi di test generati continuando ad aggiungere vincoli fino ad arrivare ad un __numero ragionevole__.
-
-Ovviamente, i casi di test avranno poi bisogno di __valori specifici__ per le caratteristiche: non basta dire "pattern con apici all'interno", bisogna creare un pattern aderente a questa descrizione!
-Fortunatamente questa operazione è solitamente molto facile, anche con tool automatici.
-
-#### Conclusioni
-
-Per quanto intuitiva e utile, la tecnica di category partition presenta due criticità:
-
-- individuare i __casi significativi__ delle varie caratteristiche può essere difficile e si può sbagliare, anche utilizzando mantra come "_zero_, _uno_, _molti_";
-- una volta generati i casi di test serve comunque un "__oracolo__" che fornisca la risposta giusta, ovvero quella che ci si attende dall'esecuzione sul caso di test.
-L'attività non è dunque _completamente_ automatizzabile.
-
-Va però detto che esistono delle tecniche di __property-based testing__ che cercano di eliminare la necessità di un oracolo considerando particolari proprietà che dovrebbero sempre valere durante l'esecuzione (invarianti) piuttosto che analizzare il risultato dell'esecuzione dei casi di test per determinare la correttezza del programma.
-
-## Object orientation e testing funzionale
-
-Trattandosi di un approccio __black box__ che ragiona sulle __funzionalità__ e non sui dettagli implementativi, l'introduzione del paradigma a oggetti __non dovrebbe cambiare nulla__ per quanto riguarda il testing funzionale.
-Se questa affermazione è vera per quanto riguarda la verifica di singole unità funzionali, lo stesso non si può dire nel caso di __test di integrazione__.
-
-Nei linguaggi procedurali i test di integrazione sono infatti scritti secondo logiche alternativamente __bottom-up__ o __top-down__: esiste cioè un punto di partenza dal quale partire ad aggregare le componenti, seguendo cioè una qualche forma di __albero di decomposizione__ del programma. \\
-Per quanto riguarda la programmazione a oggetti, invece, la situazione è __molto più caotica__: le relazioni tra le classi sono spesso cicliche e non gerarchiche (tranne per l'ereditarietà &mdash; la relazione meno interessante), in una serie di _inter-dipendenze_ che rendono difficoltoso individuare un punto da cui partire a integrare.
-
-Relazioni interessanti in questa fase sono infatti _associazioni_,_aggregazioni_ o _dipendenze_, ma rendono complicato identificare il __sottoinsieme di classi da testare__.
-Per fare ciò si possono comunque utilizzare alcuni strumenti già visti:
-
-- si può partire dai diagrammi degli __use cases e scenari__ per testare i componenti citati;
-- si possono osservare i __sequence diagram__ per testare le classi protagoniste delle interazioni a scambio di messaggi descritte;
-- si possono infine usare gli __state diagram__ nella modalità che abbiamo già descritto.
-
-# Software inspection
-
-Un'altra classe di tecniche di verifica e convalida è la __software inspection__, ovvero tecniche manuali per individuare e correggere gli errori basati su una attività di gruppo in cui si analizza il codice insieme passo passo: si pensi per esempio alla tecnica di _pair programming_ già ampiamente citata parlando di XP.
-
-Le tecniche di software inspection sono molto interessanti in quanto hanno __pochi requisiti__ e l'unico __tool__ richiesto è un essere __umano__ che si prenda la briga ispezionare il codice, spesso in riunioni di gruppo da 5-6 persone.
-
-Trattandosi di una tecnica umana essa è molto __flessibile__: l'__oggetto sotto ispezione__ può essere una porzione di codice non funzionante, una serie di specifiche formali o informali o direttamente l'eseguibile compilato.
-La software inspection può quindi essere eseguita durante tutte le fasi del ciclo di vita di un programma.
-
-## Fagan code inspection
-
-La __Fagan code inspection__ è una metodologia sviluppata da Michael Fagan alla IBM negli anni '70.
-La metodologia prevede che un __gruppo di esperti__ esegua una serie di passi per verificare la correttezza del codice sorgente al fine di individuare eventuali errori, incongruenze o altri problemi. \\
-È __la più diffusa__ tra le tecniche di ispezione, nonché la più rigorosa e definita.
-
-### Ruoli
-
-Essendo un'attività di gruppo, nella Fagan code inspection vengono identificati diversi ruoli:
-
-- __Moderatore__: è colui che coordina i meeting, sceglie i partecipanti e ha la responsabilità di far rispettare le regole di cui parleremo tra poco.
-  È di solito una persona che lavora ad un progetto diverso da quello in esame in modo da evitare conflitti di interessi.
-- __Readers e Testers__: non sono persone diverse, semplicemente a seconda dei momenti i partecipanti possono coprire uno di questi due ruoli: i primi leggono il codice al gruppo, mentre i secondi cercano difetti al suo interno.
-  La lettura del codice è una vera e propria _parafrasi_ di esso, ovvero un'interpretazione del codice nella quale si spiega quello che fa ma seguendo comunque la sua struttura.
-- __Autore__: è colui che ha scritto il codice sotto ispezione; è un partecipante passivo che risponde solo a eventuali domande.
-  È simile al ruolo del _cliente_ nell'eXtreme Programming: pronto a rispondere a qualsiasi domanda per accelerare il lavoro degli altri.
-
-### Processo
-
-Definiti i ruoli, secondo la tecnica __Fagan__ di ispezione del codice il processo si articola come segue:
-
-1. __Planning__: in questa prima fase il moderatore sceglie i partecipanti, si definiscono i loro ruoli e il tempo da dedicare alla ispezione, pianificando anche i vari incontri.
-2. __Overview__: viene fornito a tutti i partecipanti materiale sul progetto per permettere loro di farsi un'idea del contesto in cui l'oggetto dell'ispezione si inserisce in ottica della riunione vera e propria.
-3. __Preparation__: i partecipanti _"offline"_ comprendono il codice e la sua struttura autonomamente sulla base anche del materiale distribuito nella fase precedente;
-4. __Inspection__: la vera e propria fase di ispezione.
-  In questa fase si verifica che il codice soddisfi le regole definite in precedenza e si segnalano eventuali problemi o anomalie.
-  Durante l'ispezione, il gruppo di esperti esamina il codice riga per riga, confrontandolo con le specifiche e cercando di individuare errori, incongruenze o altri problemi.
-5. __Rework__: una volta individuati i problemi, l'autore del codice si occupa di correggere i difetti individuati.
-6. __Follow-up__: possibile re-ispezione del nuovo codice ottenuto dopo la fase precedente.
-
-Se la maggior parte delle fasi è abbastanza autoesplicativa, è bene dare uno sguardo più approfondito all'attività di ispezione vera e propria.
-
-#### Ispezione
-
-Durante la fase di ispezione, l'obiettivo è __trovare e registrare__ i difetti __senza correggerli__: la tentazione di correggere i difetti è sicuramente fortissima ma non è compito dei partecipanti alla riunione farlo.
-Ciascuno di loro potrebbe infatti avere le proprie idee e preferenze e metterli d'accordo potrebbe non essere facile: si preferisce quindi che sia l'autore del codice a correggere successivamente i problemi trovati.
-
-Per evitare ulteriormente di perdere tempo sono previste _al massimo_ 2 sessioni di ispezione di 2 ore al giorno, durante le quali lavorare approssimativamente a __150 linee di codice all'ora__.
-Quest'ultimo vincolo è __molto variable__ in quanto cambia in base al linguaggio, al progetto, all'attenzione ai dettagli richiesta e alla complessità.
-
-Una possibilità prevista in questa fase è anche quella di fare _"test a mano"_: si analizza
-il flusso di controllo del programma su una serie di casi di test così da verificarne il funzionamento.
-
-Ancora più prominente è però l'uso di una serie di __checklist__, di cui parliamo nel prossimo paragrafo.
-
-#### Checklist
-
-Rispetto all'attività di testing, che a partire dai malfunzionamenti permetteva di risalire ai difetti e dunque agli sbagli commessi, il _thought-process_ per le __checklist__ è inverso: __si parte dagli _sbagli___ che più frequentemente hanno portato ad inserire determinate anomalie nel codice e si controlla che nessuno di questi sia stato commesso nuovamente.
-
-In letteratura è reperibile la __conoscenza__ di tutto ciò che è meglio evitare poiché in passato ha portato più volte ad avere anomalie nel codice.
-Tale conoscenza è raccolta in __checklist comuni__ per i vari linguaggi.
-
-Inoltre, l'ispezione del codice funziona così bene anche perché tali checklist possono essere __redatte internamente__ all'azienda, in base all'esperienza passata e alla storia di un determinato progetto. \\
-Man mano che il progetto va avanti, l'__individuazione di un nuovo sbaglio__ si traduce in un'__evoluzione della checklist__: dalla prossima ispezione si controllerà di non aver commesso lo stesso errore.
-
-##### <big>Esempio NASA</big>
-
-La NASA nel suo <a href="../assets/13_nasa-software-inspection.pdf"><i>Software Formal Inspections Guidebook</i></a> (1993) ha formalizzato circa __2.5 pagine di checklist__ per C e 4 per FORTRAN.
-
-Sono divise in _functionality_, _data usage_, _control_, _linkage_, _computation_, _maintenance_ e _clarity_.
-
-Di seguito sono elencati alcuni esempi dei punti di tali checklist:
-
-> - [ ] Does each module have a single function?
-> - [ ] Does the code match the Detailed Design?
-> - [ ] Are all constant names upper case?
-> - [ ] Are pointers not `typecast` (except assignment of `NULL`)?
-> - [ ] Are nested `#include` files avoided?
-> - [ ] Are non-standard usage isolated in subroutines and well documented?
-> - [ ] Are there sufficient comments to understand the code?
-
-### Struttura di incentivi
-
-Perché l'ispezione del codice come è stata descritta funzioni bene, occorre prevedere una serie di __dinamiche positive__ di incentivi al suo interno.
-
-In particolare, è importante sottolineare che i difetti trovati __non devono essere utilizzati__ per la valutazione del personale: questo evita che i programmatori nascondano i difetti nel proprio codice, minando la qualità del prodotto.
-
-Dall'altro canto si possono invece considerare per la __valutazione di readers e tester__ i difetti trovati durante l'ispezione, in modo che questi siano incentivati a fare l'ispezione più accurata possibile.
-
-### Variante: _active_ design reviews
-
-Purché il processo di ispezione funzioni al meglio __le persone__ coinvolte __devono partecipare__, ma per come abbiamo descritto l'attività di Fagan Code Inspection nulla vieterebbe ai revisori non preparati di essere presenti ma non partecipare, rimanendo in silenzio e pensando ad altro.
-
-Innanzitutto, per sopperire a questo problema i partecipanti andrebbero __scelti__ tra persone di adeguata esperienza e sopratutto assicurando che nel team vi siano revisori per diversi aspetti nel progetto.
-
-Qualora questo non bastasse, una variante del processo che prende il nome di __active design review__ suggerisce che sia l'__autore__ a leggere le checklist e sollevare questioni all'attenzione dei revisori, chiedendo diverse domande.
-Essendo presi direttamente in causa, i revisori saranno quindi costretti a partecipare.
-
-## Automazione
-
-Sebbene l'ispezione del codice sia una tecnica manuale esistono diversi __strumenti di supporto automatici__ in grado di velocizzare notevolmente il lavoro.
-Alcuni di questi tool possono aiutare con:
-
-- __controlli banali__, come la formattazione; in fase di ispezione manuale si controllerà poi il risultato del controllo automatico;
-- __riferimenti__: checklist e standard in formati elettronici facilmente consultabili e compilabili;
-- __aiuti alla comprensione del codice__: tool che permettono di navigare e leggere il codice con maggiore facilità e spesso utilizzati durante attività di _re-engineering_;
-- __annotazione e comunicazioni__ del team, come l'email;
-- __guida al processo e rinforzo__: non permettere di chiudere il processo se non sono stati soddisfatti alcuni requisiti (_come la necessità di approvazione prima del merge di una pull request_).
-
-## Pro e contro
-
-Finito di descrivere il processo di ispezione del software possiamo chiederci: funziona?
-Prove empiriche parrebbero suggerire che la risposta sia __sì__ e evidenziano anche che tale tecnica è particolarmente _cost-effective_.
-
-I vantaggi dell'uso di questa tecnica di verifica e convalida sono infatti numerosi:
-
-- Esiste un __processo rigoroso e dettagliato__;
-- Si basa sull'__accumulo dell'esperienza__, auto-migliorandosi con il tempo (vd. _checklist_);
-- Il processo integra una serie di __incentivi sociali__ che spingono l'autore del codice ad analizzarlo in modo critico;
-- A differenza del testing è possibile per la mente umana __astrarre il dominio completo__ dei dati, considerando quindi in un certo senso tutti i casi di test;
-- È applicabile anche a __programmi incompleti__.
-
-La software inspection funziona così bene che è spesso utilizzata come _baseline_ per valutare altre tecniche di verifica e convalida.
-
-Questo non significa però che essa sia esente da __limiti__. \\
-Innanzitutto il test può essere fatto __solo a livello di unità__ in quanto la mente umana ha difficoltà a lavorare in situazioni in cui sono presenti molte informazioni contemporaneamente in assenza di astrazioni e indirettezze.
-Inoltre la software inspection __non è incrementale__: spesso infatti la fase di follow-up non è così efficace, in quanto il codice è cambiato talmente tanto che è necessario ricominciare l'ispezione da capo.
-
-Ciò non toglie però che, come afferma la __Legge di Fagan (L17)__:
-> Le ispezioni aumentano in maniera significativa la produttività, qualità e la stabilità del progetto.
-
-## Confronto tra tecniche di verifica e convalida
-
-Numerosi studi hanno provato a confrontare l'efficacia di varie tecniche di testing, con particolare riferimento a testing strutturale, testing funzionale e software inspection.
-Un [articolo](https://web.archive.org/web/20060920113729/http:/www2.umassd.edu/SWPI/ISERN/ISERN-98-10.pdf) del 2004 riporta in una __tabella di confronto__ i risultati di alcuni di questi studi, considerando come metrica di valutazione delle tecniche di verifica e convalida la _percentuale media di difetti individuati_.
-
-{% responsive_image path: assets/13_tabella-confronto-tecniche-vc.png %}
-
-Come si può notare, a seconda dello studio appare più efficace l'una o l'altra tecnica; inoltre, la somma delle percentuali per ogni riga non è 100%, il che significa che alcuni difetti non possono essere rilevati da nessuna delle tre tecniche. \\
-Nonostante ciò, si possono fare una serie di osservazioni: innanzitutto, l'efficacia di una  o dell'altra tecnica dipende dalla __tipologia del progetto__ su cui viene esercitata.
-Inoltre, __non è detto__ che tecniche diverse trovino __gli stessi errori__: l'ispezione potrebbe aver trovato una certa tipologia di errore mentre il testing funzionale un'altra; le diverse tecniche controllano infatti diversamente aspetti differenti del programma, osservandolo da __diversi punti di vista__.
-
-Confrontare le varie tecniche non è dunque necessariamente una perdita di tempo, mentre lo è sicuramente __confrontare solo i numeri__, come la varietà di risultati diversi ottenuti dai parte di studi diversi.
-Tra l'altro, dal riassunto della tabella si __perdono__ informazioni sulle __modalità di rilevazione__ dei dati, attribuendole ad espressioni generiche (come _comunemente_, _in media_, _progetti junior_, ...).
-
-In conclusione, non c'è una risposta semplice al confronto e __non esiste una tecnica _sempre_ migliore__ rispetto alle altre.
-
-### Combinare le tecniche
-
-Una domanda che sorge spontanea è chiedersi quindi cosa può succedere se si __combinano insieme__ diverse tecniche di verifica e convalida.
-
-Diversi [studi](https://web.archive.org/web/20070221162909/http://www2.umassd.edu/SWPI/TechnicalReview/r4094.pdf) mostrano che applicando tutte e quattro le tecniche qui descritte &mdash; anche se solo in modo superficiale &mdash; il risultato è sicuramente __più performante__ delle tecniche applicate singolarmente.
-
-{% responsive_image path: assets/13_tabella-tecniche-vc-insieme.png %}
-
-Anche se una certa percentuale di errori può essere rilevata senza alcuna tecnica formale di verifica e convalida, semplicemente usando il software, si può infatti notare ciascuna tecnica presa singolarmente migliora tale percentuale e che la __combinazione__ di tecniche diverse la incrementa ulteriormente.
-Questo perché tendenzialmente __ogni tecnica controlla aspetti differenti__ e le rispettive aree di controllo si sovrappongono poco: è dunque conveniente applicare superficialmente ciascuna tecnica piuttosto che una sola tecnica in modo molto approfondito.
-
-In conclusione, come afferma la __Legge di Hetzel-Meyer (L20)__:
-> Una combinazione di diversi metodi di V/C supera qualsiasi metodo singolo.
-
-## Gruppi di test autonomi
-
-È convinzione comune che colui che ha sviluppato un pezzo di codice sia la persona meno adatta a testarlo, come afferma la __Legge di Weinberg (L23)__:
-
-> Uno sviluppatore non è adatto a testare il suo codice.
-
-Di conseguenza, si preferisce spesso che il testing sia affidato ad un __gruppo di tester autonomi__.
-Questo implica infatti una serie di vantaggi, sia __tecnici__ che e __psicologici__:
-
-- __Aspetti tecnici__:
-  - __maggiore specializzazione__: si evita così di richiedere che i propri sviluppatori siano anche esperti di testing;
-  - __maggiore conoscenze delle tecniche di verifica e convalida__ e dei relativi tool: chi fa il _tester_ di lavoro acquisisce competenze specifiche sui tool e sugli strumenti di testing (spesso complessi), oltre che sui concetti di copertura e mutazioni.
-- __Aspetti psicologici__:
-  - __distacco dal codice__: a causa dell'assenza di modelli mentali precedenti su come il software dovrebbe operare, un tester esterno pone maggiore attenzione agli aspetti spesso trascurati o dimenticati;
-  - __indipendenza nella valutazione__: una persona che testa il proprio codice è incentivata a _non_ trovare molti errori in quanto potrebbe suggerire un lavoro di dubbia qualità in fase di sviluppo.
-  Un gruppo specializzato nel testing è invece incentivato a trovarne il più possibile per giustificare il loro impiego.
-
-Ci sono tuttavia anche una serie di __svantaggi__ legati all'avere un gruppo di tester autonomo.
-Innanzitutto, i problemi più ovvi sono legati all'__aspetto tecnico__: il fatto che i tester diventino specializzati nel testing significa che __perderanno__ con il tempo la __capacità di progettare__ e __codificare__, oltre a possedere una __minore conoscenza dei requisiti__ del progetto.
-
-Nell'analisi di Elisabeth Hendrickson denominata "[__Better testing &mdash; worse quality?__](https://web.archive.org/web/20220526084408/http:/testobsessed.com/wp-content/uploads/2011/04/btwq.pdf)" viene analizzata poi la tecnica sotto un __punto di vista psicologico__: come è possibile che un maggior investimento nel team di testing porti a un calo delle prestazioni in termini di numero di errori nel codice?
-
-La risposta pare dipendere dal concetto di ___responsabilità___: seppur vero che l'attività di testing è compito del tester, è anche vero che è lo sviluppatore stesso che ha il compito di fare __test di unità__ del proprio codice &mdash; il team di testing dovrebbe occuparsi solo di quello funzionale o di integrazione.
-Spesso però, a fronte di un aumento del personale nel team di testing e specialmente quando una deadline è vicina, il team di sviluppo tende a __spostare la responsabilità__ di trovare gli errori ai tester, __abbassando la qualità del codice__. \\
-Il team di testing troverà sì gli errori, riconsegnando il codice agli sviluppatori per correggerli, ma questo passaggio ulteriore implica una notevole __perdita di tempo__ e risorse.
-
-Inoltre, la presenza di un team di testing dedicato può generare __pressioni negative__ sul team di sviluppo: ogni sviluppatore potrebbe sentirsi sotto costante valutazione da parte del team di testing.
-
-### Possibili alternative
-
-Una possibile soluzione alle criticità appena evidenziate consisterebbe nella __rotazione del personale__: una stessa persona potrebbe ricoprire il ruolo di sviluppatore per un progetto e di tester per un altro.
-Questo approccio mostra diversi vantaggi, tra cui:
-
-- __evitare pressioni negative__: ricoprendo diversi ruoli in diversi progetti, il personale non si dovrebbe sentire _giudicato_ o _giudicante_;
-- __evitare il progressivo depauperamento tecnico__ dovuto ad all'eccessiva specializzazione;
-- __evitare lo svuotamento dei ruoli__.
-
-C'è però da considerare un certo __aumento dei costi di formazione__ per via del raddoppio delle responsabilità individuali e un parallelo __aumento della difficoltà di pianificazione__: potrebbe succedere che la stessa persona debba lavorare a più progetti contemporaneamente, dovendo quindi dividere il proprio tempo e le proprie competenze.
-
-Un'altra possibile alternativa consiste nella __condivisione del personale__, che prevede che siano gli stessi sviluppatori a occuparsi del testing: ciò permette di __sopperire__ al problema di __scarsa conoscenza del software__ in esame e del relativo dominio applicativo ma, oltre a far riemergere le __criticità__ individuate precedentemente, aumenta le __difficoltà nella gestione dei ruoli__.
-
-# Modelli statistici di distribuzione degli errori
-
-Negli ultimi tempi si stanno sviluppando una serie di __modelli statistici__ sulla distribuzione degli errori nel codice che dovrebbero teoricamente aiutare l'attività di testing guidandola verso le porzioni di sorgente che _più probabilmente_ potrebbero presentare difetti.
-
-Tali modelli propongono infatti una __correlazione statistica__ tra una serie di __metriche__ quali la lunghezza del codice, il tipo di linguaggio, il grado massimo di indentamento etc. e:
-
-- __la presenza di errori__ per categoria di errore;
-- __il numero di errori__ per categoria di errore.
-
-L'idea sarebbe quindi di __predire la distribuzione e il numero di errori__ all'interno di uno specifico modulo del software in esame.
-
-Occorre però __fare attenzione__ alle conclusioni di queste statistiche.
-Utilizzare i risultati di tali modelli statistici come indicazioni sul fatto che su determinati moduli vada fatta più attività di testing rispetto ad altri potrebbe inizialmente sembrare la __soluzione più logica__.
-Tuttavia, tali risultati non considerano l'attività di testing già effettuata e le correzioni successive e quindi __non cambiano__: codice inizialmente _"scritto male"_ secondo il modello rimarrà per sempre scritto male, anche se testato estensivamente.
-
-Con ciò in mente, si cita spesso la __Legge di Pareto/Zipf (L24)__:
-> Circa l'80% dei difetti proviene dal 20% dei moduli.
-
-Sebbene tale affermazione è indubbiamente probabilmente vera, è difficile sfruttare questa nozione in quanto non sono conosciuti in principio i __moduli particolarmente problematici__, e il testing è comunque necessario anche in tutti gli altri.
-
-# Debugging
-
-Il debugging è l'insieme di tecniche che mirano a __localizzare__ e __rimuovere__ le anomalie che sono la causa di malfunzionamenti riscontrati nel programma.
-Come già detto, esso non è invece utilizzato per _rilevare_ tali malfunzionamenti.
-
-Il debugging richiede una __comprensione approfondita del codice__ e del funzionamento del programma e può essere un processo complesso e articolato.
-Tuttavia, può contribuire in modo significativo a migliorare la qualità e la stabilità del codice, oltre che a _risolvere_ malfunzionamenti.
-
-Trattandosi di ricerca delle anomalie che generano malfunzionamenti noti, l'attività è definita per un __programma__ e __un insieme di dati che causano malfunzionamenti__.
-Essa si basa infatti sulla __riproducibilità__ del malfunzionamento, verificando prima che non sia dovuto in realtà a specifiche in errate.
-
-Si tratta di un'attività molto complicata, come fa notare Brian W. Kernighan nella sua famosa citazione:
-
->"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it".
-
-È dunque importante scrivere codice __più semplice possibile__ in modo tale da poterne fare un altrettanto semplice debugging laddove necessario.
-
-## Perché è così difficile?
-
-L'attività di debugging è particolarmente complessa soprattutto perché non è sempre possibile individuare con precisione la __relazione anomalia-malfunzionamento__.
-Non è un legame banale, in quanto potrebbero esserci anomalie che prima di manifestarsi sotto forma di malfunzionamenti abbiano avuto molte evoluzioni.
-
-Inoltre, __non esiste una relazione biunivoca__ tra anomalie e malfunzionamenti: non è detto che un'anomalia causi un unico  malfunzionamento, ma nemmeno che un malfunzionamento sia causato da un'unica anomalia.
-
-Un altro problema è dovuto al fatto che la __correzione di anomalie__ non garantisce affatto un software migliore o con meno errori: per correggere un'anomalia è necessario per forza di cose anche modificare il codice sorgente, ma ogni volta che viene fatto si apre la possibilità di introdurre __nuove anomalie__ nel codice stesso.
-
-## Tecnica naïve
-
-La tecnica di debugging maggiormente utilizzata dai programmatori consiste nell'introdurre nel modulo in esame una serie di __comandi di output__ (es. _print_) che stampino su console il valore intermedio assunto dalle sue variabili.
-Questo permetterebbe di osservare l'evoluzione dei dati e, si spera, di comprendere la causa del malfunzionamento a partire da tale storia.
-
-Nonostante sia __facile da applicare__, si tratta in realtà di una tecnica __molto debole__: non solo essa __richiede la modifica del codice__ (e quindi una _rimozione_ di tali modifiche al termine), ma è __poco flessibile__ in quanto richiede una nuova compilazione per ogni stato esaminato. \\
-Bisogna inoltre considerare che questa tecnica testa un __programma diverso__ da quello originale che presenta delle _print_ aggiuntive solo _apparentemente_ innocue e senza effetti collaterali.
-
-L'unico scenario (irrealistico) in cui la tecnica potrebbe essere considerata sufficiente
-sarebbe nel caso in cui il codice sia progettato talmente bene e il modulo così ben isolato che basterebbe scrivere un'unica _print_ per risalire all'anomalia.
-
-### Tecnica naïve avanzata
-
-Un miglioramento parziale alla tecnica appena descritta si può ottenere sfruttando le __funzionalità del linguaggio__ oppure alcuni tool specifici per il debug, come per esempio:
-
-- `#ifdef` e `gcc -D` per il linguaggio C;
-- __librerie di logging__ (con diverso livello), che permettono peraltro di rimuovere i messaggi di log in fase di produzione del codice;
-- __asserzioni__, ovvero check interni al codice di specifiche proprietà: possono essere visti anche come _"oracoli" interni_ al codice che permettono di segnalare facilmente stati illegali.
-
-Ciò non toglie che la tecnica sia comunque __naïve__, in quanto si sta ancora modificando il codice in modo che fornisca informazioni aggiuntive.
-
-## Dump di memoria
-
-Una tecnica lievemente più interessante è quella dei __dump di memoria__, che consiste nel produrre un'__immagine esatta__ della __memoria__ del programma dopo un passo di esecuzione: si scrive cioè su un file l'intero contenuto della memoria a livello di linguaggio macchina (_nei sistemi a 32 bit, la dimensione dei dump può arrivare fino a 4GB_).
-
-```txt
-Segmentation fault (core dumped)
-```
-
-Sebbene questa tecnica non richieda la modifica del codice, essa è spesso __difficile da applicare__ a causa della differenza tra la rappresentazione astratta dello stato (legata alle strutture dati del linguaggio utilizzato) e la rappresentazione a livello di memoria di tale stato.
-Viene inoltre prodotta una __enorme mole di dati__ per la maggior parte inutili.
-
-## Debugging simbolico
-
-Il prossimo passo è invece il cosiddetto __debugging simbolico__, un'attività che utilizza __tool__ specifici di debugging per semplificare la ricerca delle anomalie che causano il malfunzionamento in esame.
-Tali strumenti permettono di __osservare in tempo reale l'esecuzione del programma__, sollevando una cortina e rendendo possibile analizzare l'evoluzione del valore delle variabili passo per passo: questi tool non alterano il codice ma _come_ esso è eseguito.
-
-A tal proposito, i debugger simbolici forniscono informazioni sullo stato delle variabili utilizzando lo __stesso livello di astrazione__ del linguaggio utilizzato per scrivere il codice: gli stati sono cioè rappresentati con __stessi simboli__ per cui le locazioni di memoria sono state definite (_stesse strutture dati_), rendendo quindi utile e semplice l'attività di __ispezione dello stato__.
-
-In aggiunta, i debugger simbolici forniscono __ulteriori strumenti__ che permettono di visualizzare il comportamento del programma in maniera selettiva, come per esempio _watch_ e _spy monitor_.
-
-Per regolare il flusso del programma è poi possibile inserire __breakpoint__ e __watchpoint__ su certe linee di codice che ne arrestino l'esecuzione in uno specifico punto, eventualmente rendendoli dipendenti dal valore di variabili.
-Volendo poi riprendere l'esecuzione si può invece scegliere la granularità del successivo passo:
-
-- __singolo__: si procede alla linea successiva;
-- __dentro una funzione__: si salta al codice eseguito dalle funzioni richiamate sulla riga corrente;
-- __drop/reset del frame__: vengono scartate le variabili nel frame d'esecuzione ritornando ad una situazione precedente.
-
-### Debugging per prova
-
-Molti debugging simbolici permettono non solo di visualizzare gli stati ottenuti, ma anche di  __esaminarli automaticamente__ in modo da verificarne la correttezza.
-
-In particolare, utilizzando __watch condizionali__ è possibile aggiungere __asserzioni a livello di monitor__, verificando così che certe proprietà continuino a valere durante l'intera esecuzione. \\
-Così, per esempio, è possibile chiedere al _monitor_ (l'_esecutore_ del programma) di controllare che gli indici di un array siano sempre interni all'intervallo di definizione.
-
-### Altre funzionalità dei debugger
-
-Ma non finisce qui! I debugger moderni sono strumenti veramente molto interessanti, che permettono per esempio anche di:
-
-- __modificare il contenuto di una variabile__ (o zona di memoria) a runtime;
-- __modificare il codice__: nonostante non sia sempre possibile, può essere comodo per esempio dopo tante iterazioni di un ciclo;
-- ottenere __rappresentazioni grafiche__ dei dati: strutture dinamiche come puntatori, alberi e grafi possono essere rappresentate graficamente per migliorare la comprensione dello stato.
-
-## Automazione
-
-Visti tutti questi fantastici tool può sorgere una domanda: __l'attività di debugging può essere automatizzata?__
-
-Andreas Zeller tratta questo argomento in maniera approfondita nel suo [Debugging Book](http://debuggingbook.org/), proponendo alcune direzioni di sviluppo di ipotetici strumenti di debugging automatico. \\
-I due concetti principali della trattazione sono i seguenti:
-
-- __shrinking input__: dato un __input molto grande__ e complesso che causa un malfunzionamento, strumenti automatici possono aiutare a ridurlo il più possibile in modo da semplificare il debugging;
-- __differential debugging__: dato lo stesso input, in maniera automatica vengono esplorati gli stati del programma mutando ad ogni iterazione piccole porzioni di codice per individuare dove è più probabile che si trovi l'anomalia.
-
-Purtroppo per il momento la prospettiva di debugger automatici è ancora lontana. \\
-Tuttavia, esiste già qualcosa di simile, vale a dire il comando __`git bisect`__ di Git: data una versione vecchia in cui il bug non è presente, una versione nuova in cui esso si è manifestato e un oracolo che stabilisce se il bug è presente o meno, Git esegue una __ricerca dicotomica__ per trovare la versione che ha introdotto il problema.
-Sebbene non sia proprio la stessa cosa, si tratta sicuramente di uno strumento utile.
diff --git a/_posts/2022-11-23-verifica-convalida.md b/_posts/2022-11-23-verifica-convalida.md
deleted file mode 100644
index 63dea00d74ba532ff2b1f549f2360592a94e0d9f..0000000000000000000000000000000000000000
--- a/_posts/2022-11-23-verifica-convalida.md
+++ /dev/null
@@ -1,199 +0,0 @@
----
-layout: post
-title: "[12] Verifica e convalida"
-date:   2022-11-23 14:30:00 +0200
-toc: true
----
-
-# Terminologia
-
-## Verifica e convalida
-
-Verifica e convalida sono due termini con un significato apparentemente molto simile ma che celano in realtà una differenza non banale tra loro:
-- per _verifica (della correttezza)_ si intende l'attività di confronto del software con le __specifiche__ (formali) prodotte dall'analista;
-- per _convalida (dell'affidabilità)_ si intende l'attività di confronto del software con i __requisiti__ (informali) posti dal committente.
-
-Ci sono quindi due punti critici che vanno a sottolineare maggiormente questa differenza:
-- requisiti e specifiche sono spesso __formulati diversamente__.
-Solitamente i _requisiti_, essendo scritti dal committente, sono formulati in un linguaggio più vicino al dominio di quest'ultimo.
-Diversamente, le _specifiche_ sono scritte in un linguaggio più vicino al dominio dello sviluppatore, spesso in maniera formale e poco ambigua;
-- è facile che i requisiti __cambino__ in corso d'opera mentre le specifiche rimangano congelate; questo aspetto dipende molto dai contratti tra committente e il team di sviluppo.
-
-La definizione dei _requisiti_ forniti dal cliente è immediata ma informale: scrivere dei test che li _convalidano_ può risultare molto complicato.
-Invece, è più semplice validare le _specifiche_ attraverso test in quanto sono scritte dal team di sviluppo e sono quindi più formali e complete.
-
-Ad ogni modo, nelle attività di verifica e convalida si cercano degli __errori__, ma la parola "errore" stessa può assumere molti significati a seconda del contesto.
-È quindi importante capire di quale _errore_ si sta parlando, introducendo termini diversi, come _malfunzionamento_ e _difetto_.
-
-N.B: Esistono dei glossari e vocabolari di terminologia comune redatti dalla IEEE, ad esempio [Systems and software engineering —
-Vocabulary]({{ site.baseurl }}/assets/12_ieee-vocabulary.pdf) che possono essere addottati dagli sviluppatori come standard in modo da snellire la comunicazione tra di loro.
-
-## Malfunzionamento (guasto/failure)
-
-Un malfunzionamento è uno __scostamento__ dal corretto funzionamento del programma.
-
-__Non dipende dal codice__ e in generale non ci si accorge di esso osservando il codice ma solo da un punto di vista più esterno, utilizzando il programma.
-Il malfunzionamento potrebbe riguardare sia le specifiche (quindi relativo alla fase di _verifica_) che i requisiti (fase di _convalida_, ovvero "non rispetta le aspettative").
-Secondo il vocabolario citato in precedenza:
-
-> __failure:__ 
-> 1. termination of the ability of a product to perform a required function or its inability to perform within previously specified limits.
-ISO/IEC 25000:2005, Software Engineering — Software product Quality Requirements and Evaluation (SQuaRE) — Guide to SQuaRE.4.20.
-> 2. an event in which a system or system
-component does not perform a required function within specified limits.
-> 
->_NOTE: A failure may be produced when a fault is encountered_
-
-### Esempio
-
-Di seguito è illustrato un esempio di malfunzionamento.
-```java
-static int raddoppia(int par) {
-    int risultato;
-    risultato = (par * par);
-    return risultato;
-}
-
-static void main(String[] args) {
-    int risultato = raddoppia(3);
-    System.out.println(risultato);  // 9
-}
-```
-La funzione dovrebbe ritornare il doppio del numero in ingresso, ma se passiamo 3 in argomento verrà ritornato 9.
-
-## Difetto (anomalia/fault)
-Un difetto è il __punto del codice__ che causa il verificarsi del malfunzionamento.
-
-È __condizione necessaria__ (ma non sufficiente) per la verifica di un malfunzionamento.
-
-> __fault:__
-> 1. a manifestation of an error in software.
-> 2. an incorrect step, process, or data definition in a computer
-program. 
-> 3. a defect in a hardware device or component. Syn: bug
-> 
-> _NOTE: A fault, if encountered, may cause a failure._
-
-Nell'esempio di codice precedente, il difetto è in `risulato = (par * par)`.
-
-Il _difetto_ è condizione _non sufficiente_ per il verificarsi di un _malfunzionamento_: ad esempio, __non si verificano malfunzionamenti__ in caso l'argomento passato sia 0 oppure 2.
-Il raddoppio in quei casi avverrebbe in maniera corretta. 
-
-Un altro esempio di tale è proprietà è il caso in cui esistono _"più anomalie che si compensano"_: se si sta utilizzando una libreria per operazioni su temperature in gradi Fahrenheit, ponendo il caso che stia partendo da gradi Celsius, dovrà essere effettuata una conversione.
-Se in questa conversione è presente un'anomalia che però si riflette allo stesso modo in fase di riconversione per restituire il risultato, le due anomalie combinate non si manifestano in un malfunzionamento.
-
-Spesso le anomalie si annidano nella gestione di casi particolari o eccezionali del programma in quanto il flusso di controllo ordinario è solitamente il più testato.
-
-## Sbaglio (mistake)
-
-Uno sbaglio è la causa di un’anomalia. 
-Si tratta in genere di un errore umano.
-
->__mistake:__
->1. a human action that produces an incorrect result
-> 
->_NOTE: The fault tolerance discipline distinguishes between a human action (a mistake), its manifestation (a hardware
-or software fault), the result of the fault (a failure), and the amount by which the result is incorrect (the error)._
-
-
-Relativamente all'esempio precedente, possibili sbagli possono essere:
-- __errori di battitura__ (scrivere `*` invece di `+`);
-- __concettuali__ (non sapere cosa vuol dire _raddoppiare_);
-- relativi alla __padronanza del linguaggio__ (credere che `*` sia il simbolo dell’addizione).
-
-È importante capire quale sia la _causa_ di uno sbaglio in modo da poter intraprendere azioni correttive per il futuro (_es. studiare meglio la sintassi del linguaggio_).
-
-### Esempio notevole: _il caso Ariane 5_
-
-<iframe width="560" height="315" src="https://www.youtube.com/embed/PK_yguLapgA?start=67" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
-
-[Wikipedia: Ariane 5 notable launches](https://en.wikipedia.org/wiki/Ariane_5#Notable_launches 'Ariane 5 - Notable Launches')
-
-Il 4 giugno 1996 il primo volo di prova del razzo Ariane 5 è fallito a causa di un problema al software di controllo che ha portato all'autodistruzione del missile.
-
-Il _malfunziamento_ è palese: il razzo è esploso e chiaramente non era il comportamento richiesto.
-
-Qual era l'_anomalia_? Il malfunziamento si è verificato per una eccezione di overflow, sollevatosi durante una conversione da un 64 bit float a un 16 bit signed int che indicava il valore della velocità orizzontale. 
-Questo ha bloccato sia l'unità principale che il backup dell'unità stessa.
-
-Lo _sbaglio_? Tale eccezione non veniva gestita perché questa parte del software era stata ereditata da Ariane 4, modello di razzo antecedente a Ariane 5, la cui traiettora faceva sì che non si raggiungessero mai velocità orizzontali non rappresentabili con int 16 bit. 
-La variabile incriminata non veniva protetta per gli _"ampi margini di sicurezza"_ (a posteriori, non così ampi).
-
-Il comportamento della variabile non era mai stato analizzato con i dati relativi alla traiettoria da seguire.
-
-# Tecniche di verifica e convalida
-## Classificazione delle tecniche
-
-Nell'ambito della _verifica e convalida_ è possibile classificare le tecniche di analisi in due categorie:
-- __tecniche statiche__, basate sull'analisi degli elementi sintattici del codice. \\
-Ad esempio: metodi formali, analisi del dataflow e modelli statistici;
-- __tecniche dinamiche__, basate sull'esecuzione del programma. \\
-Ad esempio: testing e debugging.
-
-In generale, __è più facile determinare tecniche dinamiche__ rispetto alle tecniche statiche.
-Per contro, una volta ideate e a patto di avere dimensioni del codice ragionevoli e costrutti sintattici non troppo complessi le __tecniche statiche sono più veloci__ nell'analizzare il codice e, soprattutto, più complete dato che le tecniche dinamiche lavorano sui possibili stati del programma - che possono essere infiniti.
-
-Ovviamente diverse metodologie di verifica e convalida avranno i rispettivi pro e contro.
-Come si possono dunque confrontare queste tecniche?
-
-{% responsive_image path: 'assets/12_classificazione-tecniche-di-verifica-e-convalida.jpg' %}
-
-Nell'immagine sopra è possibile osservare una _piramide immaginaria a 3 dimensioni_ che riassume dove si posizionano le tecniche di verifica e convalida relativamente le une con le altre.
-La cima della piramide rappresenta il __punto ideale__ a cui tendere, nel quale è possibile affermare di esser riusciti a verificare perfettamente una proprietà arbitraria attraverso una prova logica (dal lato statico) o una ricerca esaustiva su tutti gli stati del problema (dal lato dinamico).
-
-Tale punto ideale è __praticamente impossibile__ da raggiungere per la stragrande maggioranza dei problemi che siamo interessati a risolvere. 
-Bisogna scegliere da quale versante iniziare la scalata della piramide: __lato verde__ (approccio statico) o __lato blu__ (approccio dinamico)?
-
-Più ci si posiziona verso il basso, più si degenera in:
-- __estrema semplificazione delle proprietà__ (in basso a sinistra): si stanno in qualche modo _rilassando_ eccessivamente gli obiettivi che si vogliono raggiungere.
-
-    Ad esempio, se si vuole dimostrare che si sta usando un puntatore in maniera corretta e nel farlo si sta semplicemente controllando che non valga `null`, è _cambiata_ la proprietà che si vuole come obiettivo (controllare che un puntatore non valga `null` __non significa che__ lo si stia usando nel modo corretto);
-- __<span id="innaccuratezza_pessimistica">estrema inaccuratezza pessimistica</span>__ (in basso al centro): è dovuta all'approccio pessimistico che ha come mantra:
-> _"Se non riesco a dimostrare l'assenza di un problema assumo che il problema sia presente"_
-
-    Ad esempio, si manifesta nei compilatori quando non riescono a dimostrare che una determinata funzione che deve ritornare un valore ritorni effettivamente un valore per tutti i possibili cammini `if` / `else if` / eccetera.
-    La mancanza di capacità nel dimostrare l'assenza di un problema non ne implica la presenza di uno.
-- __estrema <span id="innaccurettezza_ottimistica">inaccuratezza ottimistica</span>__ (in basso a destra): è dovuta all'approccio ottimistico che ha come mantra:
-> _"Se non riesco a dimostrare la presenza di un problema assumo che questo non sia presente"_
-
-    È una possibile deriva degli approcci legati al testing: con esso si cercano malfunzionamenti, se a seguito dei test non ne vengono trovati allora si assume che il programma funzioni correttamente.
-
-A metà strada tra questi estremi inferiori e l'estremo superiore ideale si posizionano quindi le varie tecniche di verifica e convalida, ciascuna più o meno legata ai tra approcci sopra descritti.
-Tra queste evidenziamo le dimostrazioni con metodi formali, il testing e il debugging.
-
-## Metodi formali
-
-L'approccio dei metodi formali tenta di dimostrare l'_assenza_ di anomalie nel prodotto finale. \\
-Si possono utilizzare diverse tecniche (spiegate nelle lezioni successive), come:
-- analisi di dataflow;
-- dimostrazione di correttezza delle specifiche logiche.
-
-Questo approccio segue la linea dell'_<a href="#innaccuratezza_pessimistica">inaccuratezza pessimistica</a>_.
-
-## Testing
-
-Il testing è l'insieme delle tecniche che si prefiggono di rilevare __malfunzionamenti__. \\
-Attraverso il testing non si può dimostrare la correttezza ma solo aumentare la _fiducia_ dei clienti rispetto all'affidabilità del prodotto.
-
-Le tecniche di testing possono essere molto varie e si raggruppano in:
-- __white box__: si ha accesso al codice da testare e si possono cercare anomalie guardandolo da un punto di vista interno;
-- __black box__: non si ha accesso al codice ma è possibile testare e cercare malfunzionamenti tramite le interfacce esterne;
-- __gray box__: non si ha accesso al codice ma si ha solo un'idea dell'implementazione ad alto livello. \\
-Per esempio, se sappiamo che il sistema segue il pattern <big>M</big>ODEL <big>V</big>IEW <big>C</big>ONTROLLER ci si può aspettare che certe stimolazioni portino a chiamate al database mentre altre no.
-
-Come è chiaro, questo approccio segue una logica di _<a href="#innaccuretezza_ottimistica">inaccuratezza ottimistica</a>_.
-
-È inoltre interessante notare che il Test Driven Development (TDD) adotta una filosofia di testing completamente black box: imponendo che venga scritto prima il test del codice questo non può assumere niente sul funzionamento interno dell'oggetto di testing.
-
-## Debugging
-
-Dato un _programma_ e un _malfunzionamento noto e riproducibile_, il debugging permette di localizzare le __anomalie__ che causano i malfunzionamenti.
-A differenza del testing, infatti, è richiesta la conoscenza a priori di un malfunzionamento prima di procedere con il debugging.
-
-Molto spesso viene usato il debugging al posto del __testing__, almeno a livello di terminologia: questo è un problema perché il debugging non è fatto per la "grande esecuzione" ma al contrario per esaminare in maniera granulare (a volte anche passo passo per istruzioni macchina) una determinata sezione di codice in esecuzione con lo scopo di trovare l'anomalia che provoca un malfunzionamento.
-Se si usassero le tecniche di debugging per effettuare il testing il tempo speso sarebbe enorme: il debugging osserva infatti _stati interni_ dell'esecuzione e per rilevare un malfunzionamento in questo modo sarebbe necessario osservare tutti i possibili - e potenzialmente infiniti - stati del programma.
-
-Due possibili approcci al debugging sono:
-- partendo da una malfunzionamento _noto_ e _riproducibile_ si avvia una procedura di analisi basata sulla __produzione degli stati intermedi__ dell'esecuzione del programma: _passo passo_ (a livello a piacere, da istruzione macchina a chiamata di funzione) si controllano tutti gli stati di memoria alla ricerca di uno inconsistente;
-- ___divide-et-impera___: il codice viene smontato sezione per sezione e componente per componente in modo da poter trovare il punto in cui c'è l'anomalia. 
-Si possono mettere breakpoint o _"print tattiche"_.
diff --git a/_posts/2022-12-14-reti-petri.md b/_posts/2022-12-14-reti-petri.md
deleted file mode 100644
index c0629a14e1f55365139a38f87aee15188ed5fce7..0000000000000000000000000000000000000000
--- a/_posts/2022-12-14-reti-petri.md
+++ /dev/null
@@ -1,536 +0,0 @@
----
-layout: post
-title: "[14] Reti di Petri"
-date: 2022-12-12 14:40:00 +0200
-toc: true
----
-
-# [Reti di Petri](https://www2.informatik.uni-hamburg.de/TGI/PetriNets/index.php)
-
-In questa lezione verranno mostrate le reti di Petri come esempio di **linguaggio formale**: fin dall'inizio del corso è stato possibile apprendere come l'ingegneria del software si occupi di linguaggi e comunicazione.
-
-Partendo infatti dai processi che sfruttano un linguaggio poco formale e con poca terminologia tecnica (ad esempio le _user story_) e passando per la progettazione in cui è stato utilizzato un linguaggio più rigoroso, si arriva infine a un vero linguaggio formale utile a **raccogliere delle specifiche**.
-
-Esiste un modello standard di rete di Petri e delle possibili estensioni di quest'ultimo: ad esempio nelle prossime lezioni saranno illustrati alcuni possibili dialetti come le **reti temporizzate**, utili a descrivere sistemi real time che necessitano di requisiti formali per ridurne le criticità.
-
-In generale utilizzare linguaggi complessi e formali per descrivere le specifiche può essere costoso: vengono infatti utilizzati perlopiù in **contesti critici** dove i fallimenti provocano conseguenze molto gravi e in cui la **sicurezza deve essere garantita** prima di mettere in funzione il software.
-
-Le reti di Petri sono in parte simili agli __automi a stati finiti__ (FSM), ma nascono specificatamente per descrivere sistemi concorrenti.
-Tra gli altri aspetti, i concetti di _stato_ e _transizione_ per le reti di Petri differiscono rispetto a quelli già conosciuti per le FSM:
-- lo __stato__ nelle reti di Petri non è più un'informazione atomica osservata a livello di sistema ma è frammentata in __parti diverse__ la cui composizione avviene tramite la loro visione generale;
-- di conseguenza le __transizioni__ non operano sullo stato globale ma si limitano a variarne una parte.
-
-Nelle FSM esiste un unico stato attivo e gli stati disponibili sono dati dal prodotto cartesiano di tutti i possibili valori delle diverse entità.
-Per contro nelle reti di Petri ci sono __diversi stati attivi__ in un dato momento, cosa che permette di semplificarne notevolmente la rappresentazione e l'analisi.
-
-## Definizione informale
-
-Un vantaggio delle reti di Petri è che possono essere viste in maniera informale dal cliente.
-È infatti facile rappresentare una rete di Petri come un grafo in cui ogni nodo rappresenta o un __posto__ o una __transizione__ e gli archi i collegamenti presenti tra le transizioni e i posti.
-Il grafo è __bipartito__, ovvero un grafo in cui nodi di un tipo sono messi in relazione __solo__ con nodi dell'altro tipo: in questo caso _i posti possono essere collegati soltano a transizioni e viceversa_.
-
-Ad ogni posto è assegnato un certo numero di ___gettoni___ (o __token__) – sarà successivamente approfondito il senso dell'assegnamento di un numero infinito di gettoni a un posto.
-
-La **disposizione dei gettoni nei posti** in un dato momento all'interno della rete di Petri ne determina il suo __stato complessivo__.
-
-{% responsive_image path: 'assets/14_rete-petri-informale.png' %}
-
-Per far evolvere lo stato della rete, l'__assegnamento dei gettoni deve poter variare__. \\
-La trasformazione dello stato è effettuata dallo scatto di una transizione:
-- una transizione si dice ___abilitata___ (_enabled_) quando la somma dei gettoni dei posti collegati ingresso è maggiore di un certo numero;
-- una transizione ___scatta___ (_fire_) quando, dopo essere stata abilitata, consuma i gettoni dei posti collegati in ingresso e ne genera altri all'interno dei posti collegati in uscita.
-È importante notare come i gettoni __non si spostano__ da un posto a un altro conseguentemente a uno scatto, ma vengono __distrutti__ nei posti in ingresso alla transizione e __generati__ nei posti in uscita.
-Quest'ultima considerazione è importante per capire che i gettoni _non sono necessariamente sempre dello stesso numero in ingresso e in uscita_.
-
-Tramite questo __modello operativo__ è facile mostrare al cliente quando qualcosa cambia all'interno del sistema, perché risulta più intuitivo rispetto a un linguaggio logico e descrittivo.
-
-Lo **svantaggio** è che fornisce informazioni parziali su _come_ il sistema compie le azioni che dovrebbe eseguire, rischiando di essere una via di mezzo tra _specifica_ e _documento di design_.
-Si può comunque chiamare specifica perché _viene definito totalmente e inequivocabilmente il comportamento del sistema_.
-
-La rete descritta è quindi una **macchina di riferimento** da utilizzare come confronto per stabilire la validità del sistema sotto esame, come se fosse un _oracolo_.
-
-## Definizione matematica
-
-Come già detto, esistono numerosi dialetti di reti di Petri. 
-In questo caso vediamo le __PT net__ (reti con posti e transizioni) che sono le più classiche, successivamente verranno descritte delle estensioni e riduzioni di queste reti.
-
-Una rete di Petri classicamente è una 5-tupla $$[P, \, T; \; F, \, W, \, M_0]$$ in cui:
-- $$P$$ è l'insieme degli **identificatori dei posti**;
-- $$T$$ è l'insieme degli **identificatori delle transizioni**;
-- $$F$$ è l'insieme delle **relazioni di flusso**;
-- $$W$$ è una funzione che associa un **peso ad ogni flusso**; 
-- $$M_0$$ è la **marcatura iniziale**, ovvero l'assegnamento iniziale dei _gettoni_.
-
-In generale definiamo come __marcatura__ una _particolare configurazione dell'assegnamento dei gettoni all'interno della rete di Petri_, sia essa _iniziale_ o una sua  _evoluzione_.
-
-Da notare che $$P$$ e $$T$$ a livello matematico sono degli insiemi di __identificatori__ che non si sovrappongono (dato che si tratta di entità differenti) a cui poi verrà assegnato un significato, quindi precedentemente sono stati associati a posti e transizioni, ma di fatto sono tutti __identificatori__.
-
-Data la 5-tupla appena descritta esistono le seguenti proprietà:
-- $$P \cap T = \varnothing$$; 
-- $$P \cup T \neq \varnothing$$ (una rete in cui non c'è nulla non è una rete: almeno un posto o una transizione ci devono essere);
-- $$F \subseteq (P \times T) \cup (T \times P)$$;
-- $$W: \: F \rightarrow \mathbb N \setminus \{ 0 \}$$;
-- $$M_0: P \rightarrow \mathbb N$$.
-
-Utilizziamo inoltre alcune _scorciatoie_:
-
-- $$\operatorname{Pre}(a) = \{ d \in (P \cup T) \quad \langle d, \, a \rangle \in F \}$$. \\
-Il **preset** di un nodo $$a$$ è l'insieme degli elementi $$d$$ appartenenti all'unione degli insiemi degli identificatori di posti e transizioni tali che esiste una relazione di flusso tra $$d$$ e $$a$$ appartenente a $$F$$. \\
-In sostanza questo insieme rappresenta l'insieme degli **identificatori antecedenti** ad $$a$$;
-- $$\operatorname{Post}(a) = \{ d \in (P \cup T) \quad \langle a,\, d \rangle \in F \}$$. \\
-Il **postset** di un nodo $$a$$ è l'insieme degli elementi $$d$$ appartenenti all'unione degli insiemi degli identificatori di posti e transizioni tali che esiste una relazione di flusso tra $$a$$ e $$d$$ appartenente a $$F$$. \\
-In sostanza questo insieme rappresenta l'insieme degli **identificatori successivi** ad $$a$$.
-
-Tutto questo rappresenta la parte statica delle reti di Petri, ovvero quando vengono osservate in un preciso istante di tempo, senza considerare i cambiamenti che potrebbero avvenire al suo interno.
-
-### Comportamento dinamico
-
-Una transizione $$t \in T$$ è __abilitata__ in una particolare marcatura $$M$$ se e solo se
-
-$$
-\boxed{
-    \forall p \in \operatorname{Pre}(t) \quad M(p) \geq W( \langle p, \, t \rangle )
-}.
-$$
-
-In notazione, $$\boxed{M \ [ \ t >}$$ significa che _$$t$$ è abilitata in $$M$$._ 
-
-Significa che per ogni elemento collegato in ingresso a $$t$$ esiste un numero di gettoni maggiore del peso dell'arco che collega $$p$$ a $$t$$.
-Un aspetto interessante di questa definizione è che non si sta ragionando su tutti i posti della rete, ma solo su quelli collegati in ingresso a $$t$$. 
-Di conseguenza, non è necessario conoscere l'intera rete per poter affermare che una transizione sia abilitata o meno, ma è sufficiente controllare la zona che comprende i posti appartenenti a $$ \operatorname{Pre}(a) $$. 
-Questa proprietà è chiamata __località dell'analisi__.
-
-Lo __scatto__ di una transizione $$t \in T$$ in una particolare marcatura $$M$$ produce nel momento successivo una nuova marcatura $$M'$$ tale per cui
-
-$$
-\begin{aligned}
-\forall p \in \operatorname{Pre}(t) \setminus \operatorname{Post}(t) &\quad  M'(p) = M(p) - W(\langle p, \, t \rangle); \\
-\forall p \in \operatorname{Post}(t) \setminus \operatorname{Pre}(t) &\quad M'(p) = M(p) + W(\langle t, \, p \rangle); \\
-\forall p \in \operatorname{Post}(t) \cap \operatorname{Pre}(t) &\quad M'(p) = M(p) - W(\langle p, \, t \rangle) + W(\langle t, \, p \rangle); \\
-\forall p \in P - \left ( \operatorname{Post}(t) \cup \operatorname{Pre}(t) \right ) &\quad M'(p) = M(p).
-\end{aligned}
-$$
-
-Specificando in modo descrittivo le notazioni precedenti:
-- per ogni identificatore $$p$$ appartenente al preset ma non al postset della transizione $$t$$, il numero di gettoni della nuova marcatura $$M'$$ sarà uguale al numero di gettoni della marcatura precedente $$M$$ \\
-meno il peso dell'arco che collega $$p$$ a $$t$$;
-- per ogni identificatore $$p$$ appartenente al postset ma non al preset della transizione $$t$$, il numero di gettoni della nuova marcatura $$M'$$ sarà uguale al numero di gettoni della marcatura precedente $$M$$ \\
-più il peso dell'arco che collega $$t$$ a $$p$$;
-- per ogni identificatore $$p$$ appartenente sia al preset sia al postset della transizione $$t$$, il numero di gettoni della nuova marcatura $$M'$$ sarà uguale al numero di gettoni della marcatura precedente $$M$$ \\
-meno il peso dell'arco che collega $$p$$ a $$t$$ più il peso dell'arco che collega $$t$$ a $$p$$;
-- per ogni identificatore $$p$$ appartenente all'insieme dei posti meno l'unione tra preset e postset di $$p$$ la marcatura non cambia.
-
-In notazione, $$\boxed{\boxed{M \ [ \ t >} \, M'}$$ significa che lo scatto di $$t$$ in $$M$$ produce $$M'$$.
-
-È importante notare come una transizione può scattare nel caso in cui non abbia alcun elemento nel suo preset; questo significa che la transizione in questione **non possiede prerequisiti** per scattare.
-
-## Da FSM a rete di Petri
-
-È _meccanicamente_ possibile trasformare una macchina a stati finiti in una rete di Petri.
-
-{% responsive_image path: 'assets/14_produttore.png' %}
-
-Riferendosi all'esempio del produttore, l'unico problema è l'**esistenza di collegamenti diretti tra posti**: come è stato detto in precedenza questo non è possibile in una rete di Petri. 
-Sarà quindi necessario interporre tra i posti delle transizioni per avere una rete di Petri valida. \\
-Immaginando di mettere un solo gettone in uno dei due posti della rete appena creata, questo indicherà lo **stato attivo** presente nella macchina a stati finiti. \\
-Seguendo questi passaggi diventa banale mappare una macchina a stati finiti su una rete di Petri: di seguito è possibile osservare l'operazione analoga eseguita sulle FSM di un consumatore e di un buffer.
-
-{% responsive_image path: 'assets/14_consumatore-buffer.png' %}
-
-Componendo le reti di Petri di _produttore_, _consumatore_ e _buffer_ appena create, si crea la seguente.
-
-{% responsive_image path: 'assets/14_produttore-consumatore-buffer.png' %}
-
-In termini di automi a stati finiti, per trovare gli **stati raggiungibili** da questa composizione sarebbe stato necessario eseguire il prodotto cartesiano tra gli stati delle tre macchine a stati finiti combinate tra loro. \\
-Trattandosi invece di una rete di Petri, è sufficiente unire tutti gli identificatori uguali in un unico identificatore (ad esempio la transizione deposita della rete _produttore_ e della rete _buffer_) e aggiungere a quest'ultimo tutti collegamenti posseduti dagli identificatori uniti.
-
-<span style="color: red">__ATTENZIONE__</span>: nell'esempio della rete composta le coppie di transizioni _"preleva"_ e _"deposita"_ dovrebbero avere due nomi differenti, ma siccome sono indicate con due rettangoli diversi è stato omesso questo particolare. 
-In termini matematici **devono avere nomi differenti**.
-
-Precedentemente è stato detto che, _nel caso di una rappresentazione di una FSM in termini di una rete di Petri_, si rappresenta lo stato attivo nella FSM con un gettone: di conseguenza, portando all'interno della rete composta tutti i gettoni delle varie reti si arriva ad ottenere il risultato descritto dall'immagine precedente, in cui tutte le "entità" (_consumatore_, _produttore_ e _buffer_) mantengono la propria individualità (è infatti presente un gettone per ogni entità).
-
-In questo caso si può quindi notare che il produttore è pronto a produrre, il buffer è vuoto e il consumatore è pronto a consumare una volta che il buffer avrà al suo interno qualcosa.
-
-### Come evolve questa rete?
-Per rispondere a questa domanda la prima cosa da considerare è quali sono le **transizioni abilitate**: in questo caso si tratta solo della transizione _produci_ sotto a $$p_0$$, in quanto è l'unica ad avere tutti gli elementi del suo preset con un numero di gettoni sufficienti a farla scattare; $$p_0$$ possiede infatti un gettone e l'arco ha peso 1 (_quando non è specificato il peso è 1_). \\
-Una rete di Petri _non forza lo scatto di alcuna transizione_, quindi volendo si potrebbe rimanere nello stato corrente all'infinito senza far mai scattare _produci_. 
-Se però _produci_ scatta, il gettone in $$p_0$$ viene distrutto e in $$p_1$$ viene generato un nuovo gettone.
-
-{% responsive_image path: 'assets/14_primo-scatto.png' %}
-
-Dopo questo scatto la rete di Petri si trova in una situazione in cui il produttore ha prodotto qualcosa ed è pronto a depositarlo nel buffer: a questo punto non resta che porsi nuovamente la domanda _"quali transizioni sono abilitate?"_ per capire come può procedere l'evoluzione della rete. \\
-È facile notare come la transizione _deposita_ sotto $$b_0$$ sia l'unica abilitata e di conseguenza, _se dovesse scattare_, il risultato sarebbe il seguente:
-
-{% responsive_image path: 'assets/14_secondo-scatto.png' %}
-
-Ora è possibile identificare una situazione particolare, ovvero quella in cui le transizioni pronte a scattare sono due. Sorge spontanea la domanda: _"quale delle due transizioni scatta prima?"_.
-Nelle reti di Petri descritte fino ad ora non è stato presentato lo **scatto simultaneo** delle transizioni, ma nulla vieta che possa avvenire in un contesto reale.
-In tal caso si tratterebbe di un'istanza di __non determinismo__, ovvero _non si può dire quale transizione deve scattare_. 
-Sono quindi 3 le situazioni che si possono verificare:
-- scatta la prima transizione;
-- scatta la seconda transizione;
-- non scatta nessuna transizione (la _non evoluzione_ è comunque un'evoluzione).
-
-Nel caso in cui fosse stato necessario definire che una delle due transizioni scatti prima dell'altra, ci si troverebbe di fronte ad una rete **non corretta**: è infatti possibile modificare la rete in modo tale che imponga un ordine di scatto alle transizioni.
-
-### Sfruttare le reti di Petri
-A questo punto è possibile chiedersi se si stiano sfruttando realmente tutte le potenzialità delle reti di Petri, siccome la rete dell'esempio precedente è stata ricavata da un automa a stati finiti.
-Per capire ciò è possibile osservare un secondo esempio in cui è presentata una rete alternativa alla precedente, ma con lo stesso scopo.
-
-{% responsive_image path: 'assets/14_rete-alternativa.png' %}
-
-La differenza che salta subito all'occhio è il numero di gettoni presenti all'interno di $$b_0$$ che indicano il numero di posizioni libere nel buffer.
-Questo è un vantaggio perchè se il buffer dovesse cambiare la sua capienza, sfruttando questa rete è sufficiente modificare la marcatura di $$b_0$$ e il problema sarebbe risolto; la rete precedente avrebbe invece bisogno di una pesante modifica per essere adattata. \\
-Di conseguenza si può applicare lo stesso concetto per il consumatore e per il produttore: aumentandone il numero dei gettoni (rispettivamente in $$p_0$$ e $$c_0$$) aumenterebbe il numero di entità in grado di produrre e consumare.
-
-{% responsive_image path: 'assets/14_rete-alternativa-diverse-entita.png' %}
-
-È possibile affermare quindi che cambiando il **numero di gettoni** è possibile moltiplicare gli elementi del sistema di cui si vuole tracciare l'evoluzione. Si sottolinea ancora che questo risulterebbe molto oneroso in termini di dimensioni se fosse stato riadattato in una macchina a stati finiti.
-
-Per definizione le macchine a stati finiti __non__ possono rappresentare **situazioni infinite**, se si volesse quindi modificare ulteriormente l'esempio appena visto imponendo una capienza illimitata al buffer, non sarebbe possibile utilizzando una macchina a stati finiti.
-Sfruttando le reti di Petri invece è sufficiente eliminare l'identificatore del posto $$b_0$$: in questo modo abbiamo una situazione in cui i produttori possono depositare senza limiti all'interno del buffer, mentre i consumatori non possono prelevare più elementi di quelli presenti nel buffer.
-Questo vincolo è imposto dalla marcatura di $$b_1$$, infatti la transizione "preleva" può scattare al massimo $$n$$ volte consecutivamente, dove $$n$$ è la marcatura di $$b_1$$ &mdash; assumendo che nel mentre non avvengano depositi da parte dei produttori.
-
-Un altra modifica applicabile all'esempio sfrutta i pesi degli archi: ponendo un peso di 3 all'arco che collega _deposita_ a $$b_1$$ si può dire che il produttore crea e deposita tre prodotti, occupando tre posizioni nel buffer.
-Ponendo invece un peso di 2 all'arco che collega $$b_1$$ a _preleva_ si specifica che è possibile prelevare dal buffer due elementi alla volta.
-Questo esempio, in parte forzato, è utile per chiarire il fatto che nelle reti di Petri _gli archi non sono semplici collegamenti, ma è possibile attribuirgli un significato_.
-
-Vengono infatti informalmente chiamati _archi_, rifacendosi alla terminologia dei grafi, ma in realtà indicano una relazione più profonda che coinvolge due identificatori: in questo esempio esiste infatti una relazione per cui ogni elemento prodotto occupa tre posizioni all'interno del buffer e un'altra relazione in cui ogni consumatore può prelevare obbligatoriamente due elementi alla volta.
-Tramite il peso degli archi è possibile creare delle situazioni ambigue: ad esempio se la relazione che coinvolge _deposita_ e $$p_0$$ avesse un peso di 2, ogni volta che viene prodotto qualcosa i produttori si moltiplicherebbero e ovviamente questa situazione indicherebbe che la rete è sbagliata, quindi è necessario fare attenzione ad evitare questo tipo di situazioni.
-
-{% responsive_image path: 'assets/14_archi-con-pesi.png' %}
-
-È da sottolineare che è possibile ridurre una rete P/T avente pesi sugli archi in una rete P/T senza pesi sugli archi: successivamente verrà illustrato come ciò è possibile.
-
-## Relazioni
-
-Di seguito verranno elencati le tipologie di relazioni che possono coinvolgere i diversi identificatori e cosa comporta la loro presenza.
-
-### Sequenza
-
-Una transizione $$t_1$$ è __in sequenza__ con una transizione $$t_2$$ in una marcatura $$M$$ se e solo se
-
-$$\boxed{M \ [ \ t_1 >} \: \land \: \lnot \, \boxed{ M \ [ \ t_2 > } \: \land \: \boxed{ M \ [ \ t_1 t_2 > } \, .$$
-
-Questa formula indica che:
-- $$t_1$$ è abilitata in $$M$$;
-- $$t_2$$ NON è abilitata in $$M$$;
-- $$t_2$$ viene abilitata dallo scatto di $$t_1$$ in $$M$$.
-
-Si può notare una **relazione d'ordine non simmetrica** in cui _lo scatto di $$t_1$$_ è una condizione sufficiente per cui $$t_2$$ possa scattare: questo tipo di relazione permette quindi di creare un **ordine di scatto** delle transizioni.
-È condizione sufficiente e non necessaria perchè osservando l'esempio sottostante è facile capire che lo sacatto di $$t_0$$ non è necessario per far si che $$t_2$$ scatti: infatti anche se dovesse avvenire lo scatto di $$t_2$$, la transizione $$t_1$$ diventerebbe comunque abilitata.
-
-{% responsive_image path: 'assets/14_sequenza.png' %}
-
-### Conflitto
-
-Due transizioni $$(t_1, \, t_2)$$ sono in:
-- __conflitto strutturale__ $$\Longleftrightarrow \operatorname{Pre}(t_1) \cap \operatorname{Pre}(t_2) \neq \varnothing $$;
-- __conflitto effettivo__ in una marcatura $$M$$ $$\Longleftrightarrow$$:
-    - $$\boxed{M \ [ \ t_1 >} \land \boxed{M \ [ \ t_2 >}$$;
-    - $$\exists p \in \operatorname{Pre}(t_1) \cap \operatorname{Pre}(t_2) \mid M(p) < W(\langle p, \, t_1 \rangle) + W(\langle  p, \, t_2\rangle)$$.
-
-Analizzando i due tipi di conflitto è possibile notare che:
-- due transizioni sono in __conflitto strutturale__ se l'intersezione dei due preset non è vuota e quindi hanno posti in ingresso in comune: possono quindi interferire tra loro.
-Il conflitto strutturale dipende solo dalla topologia dela rete, infatti non vengono citate le marcature;
-- due transizioni sono in __conflitto effettivo__ se sono entrambe abilitate in una marcatura $$M$$ ed esiste un posto in ingresso in comune ai due preset tale per cui il numero di gettoni in quel posto è minore della somma dei pesi dei due flussi che vanno dal posto alla transizione (quindi il posto in ingresso non ha abbastanza gettoni per far scattare entrambe le transizioni).
-Entrano quindi in conflitto sulla disponibilità di gettoni nel preset.
-
-Esiste una versione __rilassata__ della definizione di conflitto esplicitata dalla seguente formula:
-
-$$
-\boxed{M \ [ \ t_1 >} \: \land \: \boxed{M \ [ \ t_2 >} \: \land \: \lnot \, \boxed{M \ [ \ t_1 t_2 >}.
-$$
-
-Questa proposizione indica che il conflitto è presente se $$t_1$$ e $$t_2$$ sono abilitate in una marcatura $$M$$ e non è possibile la sequenza $$t_1$$ $$t_2$$ a partire da $$M$$.
-Ma cosa vuol dire che è una _versione rilassata_? 
-Per capirlo si osservi questo l'esempio sottostante:
-
-{% responsive_image path: 'assets/14_esempio-conflitto1.png' %}
-
-Secondo le definizioni di conflitto che sono state date, in questa rete di Petri è presente un conflitto sia per la prima definizione che per la seconda.
-È possibile però fare in modo che rimanga in conflitto per la prima definizione data ma non più per la definizione rilassata introducendo una piccola modifica:
-
-{% responsive_image path: 'assets/14_esempio-conflitto1-differenza.png' %}
-
-Aggiungendo una relazione tra $$t_1$$ a $$p_1$$ si può notare che dopo lo scatto di $$t_1$$ quest'ultima è ancora abilitata e quindi non rientra più sotto la definizione rilassata di conflitto.
-
-Lasciando da parte la definizione rilassata, è facile osservare a questo punto che la definizione per il conflitto strutturale si basa solo sui preset, ignorando quindi qualsiasi arco in uscita, mentre la quella per il conflitto effettivo ragiona anche sugli effetti dello scatto delle transizioni. Si noti che la presenza di un conflitto strutturale __non implica__ obbligatoriamente la presenza di un conflitto effettivo in quanto quest'ultimo per esistere necessita che venga soddisfatta una condizione in più.
-Al contrario invece un conflitto effettivo __implica__ la presenza di un conflitto strutturale in qunato le condizioni di quest'ultimo sono comprese in quelle del conflitto effettivo. \\
-Di seguito viene mostrato un esempio di conflitto _effettivo_ e _strutturale_.
-
-{% responsive_image path: 'assets/14_conflitto-effettivo-e-strutturale.png' %}
-
-### Concorrenza
-
-È in qualche modo intuitivo considerare la relazione di concorrenza come la relazione opposta alla relazione di conflitto: due transizioni $$(t_1, \, t_2)$$ sono in:
-- __concorrenza strutturale__ $$\Longleftrightarrow \operatorname{Pre}(t_1) \cap \operatorname{Pre}(t_2) = \varnothing$$;
-- __concorrenza effettiva__ in una marcatura $$M \Longleftrightarrow$$
-    - $$\boxed{M \ [ \ t_1 >} \cap \boxed{M \ [ \ t_2 >} $$;
-    - $$\forall p \in \operatorname{Pre}(t_1) \cap \operatorname{Pre}(t_2) \quad M(p) \geq W(\langle p, \, t_1 \rangle) + W(\langle  p, \, t_2\rangle)$$.
-
-Quest'ultima formula indica che due identificatori delle transizioni sono in concorrenza effettiva se e solo se per tutti i posti che hanno in comune c'è un numero di gettoni sufficienti per farle scattare entrambe.
-
-In questo caso non esiste alcun legame tra concorrenza strutturale ed effettiva, diversamente da quanto abbiamo visto in precedenza per le relazioni di conflitto.
-Se si verificano le condizioni per avere una concorrenza strutturale è __possibile__ che le due transizioni non siano abilitate, oppure se si verificano le condizioni per avere concorrenza effettiva è __possibile__ che $$t_1$$ e $$t_2$$ abbiano posti in comune che posseggano abbastanza gettoni per entrambe.
-
-Questo però non esclude il fatto che sia possibile avere concorrenza strutturale ed effettiva contemporaneamente, infatti di seguito sono riportati degli esempi che confermano ciò:
-
-{% responsive_image path: 'assets/14_esempio-concorrenza.png' %}
-
-Ovviamente è anche possibile che non ci sia alcun tipo di concorrenza: è sufficiente che due transizioni abbiano in comune un posto e una delle due non sia abilitata.
-
-## Insieme di raggiungibilità 
-
-<span style="display: none;">$$\def\pt{\mathcal{P/T}}$$</span>
-L'insieme di raggiungibilità $$R$$ di una rete $$\pt$$ a partire da una marcatura $$M$$ è il più piccolo insieme di marcature tale che:
-- $$M \in R(\pt, \, M)$$;
-- $$M' \in R(\pt, \, M) \land \exists t \in T \quad \boxed{\boxed{M' \ [\ t >} \, M''} \Longrightarrow M'' \in R(\pt, \, M)$$.
-
-Questa definizione induttiva viene interpretata nel seguente modo:
-- __passo base__: la marcatura $$M$$ appartiene all'insieme di raggiungibilità $$R(\pt, \, M)$$ \\
-($$M$$ indica la marcatura iniziale mentre $$\pt$$ indica la rete posti-transizioni);
-- __passo induttivo__: se $$M'$$ appartiene all'insieme di raggiungibilità (quindi si dice che _è raggiungibile_) ed esiste una transizione della rete tale per cui è abilitata in $$M'$$ e porta in $$M''$$ &mdash; per cui con uno scatto è possibile passare dalla marcatura $$M'$$ alla marcatura $$M''$$ &mdash; _allora_ anche quest'ultima è __raggiungibile__.
-
-Procedendo ricorsivamente con questa definizione è possibile ottenere tutte le marcature raggiungibili.
-
-## Limitatezza
-<span id="limitatezza"></span>
-Una proprietà importante delle reti di Petri è la __limitatezza__, che indica se le possibili evoluzioni della rete possono essere limitate o illimitate, quindi se gli stati raggiungibili sono in numero finito oppure infiniti.
-Volendo dare una definizione più formale è possibile dire che una rete posti-transizioni ($$\pt$$) con marcatura $$M$$ si dice __limitata__ se e solo se:
-
-$$
-\exists k \in \mathbb N, \: \forall M' \! \in R(\pt, \, M), \: \forall p \in P \quad M'(p) \leq k
-$$
-
-cioè se esiste un numero naturale $$k$$ tale per cui per ogni marcatura $$M'$$ raggiungibile da $$M$$, per ogni posto $$p$$ all'interno della rete il numero di gettoni in quella marcatura _raggiungibile_ è minore o uguale di $$k$$ &mdash; ovvero se è possibile porre un numero finito tale per cui dopo qualsiasi evoluzione non esista alcun posto che possiede un numero di gettoni maggiore di $$k$$ &mdash; allora è possibile affermare che **la rete è limitata**. \\
-Se ciò non si verifica esiste almeno un posto in cui è possibile aumentare tendenzialmente all'infinito il numero di gettoni, tramite una certa evoluzione della rete.
-È importante sottolineare che la limitatezza di una rete può dipendere dalla sua **marcatura iniziale**.
-
-{% responsive_image path: 'assets/14_esempio-rete-illimitata.png' %}
-
-## Da reti di Petri a Automi
-
-Precedentemente è stato mostrato come a partire da un automa a stati finiti sia possibile ricavare una rete di Petri, ma è possibile fare **il contrario**?
-Se la rete è limitata allora l'insieme di raggiungibilità è finito, di conseguenza è possibile definire un corrispondente automa a stati finiti che prende ogni marcatura raggiungibile come un proprio _stato_ e ne traccia le transizioni di stato dell'automa conseguenti alla transizione scattata nella rete di Petri.
-Due considerazioni:
-- gli **stati** sono le possibili marcature dell'insieme di raggiungibilità;
-- le **transizioni** sono gli eventi che permettono il passaggio da una configurazione alla successiva.
-
-Riuscire a passare dalle reti di Petri agli automi ci permette di modellare un problema in modo più sintetico, ma allo stesso tempo rimane possibile utilizzare i **tool di analisi** che sfruttano proprietà già consolidate per gli automi.
-L'unico problema è che questo approccio vale solo per **reti limitate**.
-
-## Vitalità di una transizione
-
-Una transizione $$t$$ in una marcatura $$M$$ si può dire _viva_ con un certo __grado__:
-- __grado 0__ (o __morta__): non è abilitata in nessuna marcatura appartanente all'insieme di raggiungibilità, quindi qualunque evoluzione avvenga nella rete, la transizione non portà mai scattare (non è sempre un aspetto negativo);
-- __grado 1__: esiste almeno una marcatura raggiungibile a partire da $$M$$ in cui la transizione è abilitata;
-- __grado 2__: per ogni numero $$n$$ naturale escluso lo zero esiste almeno una sequenza di scatti ammissibile a partire da $$M$$ in cui la transizione scatta $$n$$ volte, ovvero è possibile far scattare la transizione un numero $$n$$ grande a piacere di volte;
-- __grado 3__: esiste una sequenza di scatti ammissibile a partire da $$M$$ per cui la transizione scatta _infinite_ volte;
-- __grado 4__ in _qualunque marcatura raggiungibile_ esiste una sequenza ammissibile in cui è possibile far scattare la transizione almeno una volta, di conseguenza può scattare infinite volte in qualunque situazione ci si trovi (ovvero in qualunque marcatura).  \\
-In questo caso si dice che la transizione è __viva__ _in maniera assoluta_.
-
-Si noti come il concetto di _$$n$$ grande a piacere_ presente nel grado 2 sia differente dal concetto di _infinite volte_ nel grado.
-
-Gli esempi seguenti rappresentano delle situazioni verosimili riguardanti la vitalità delle transizioni:
-- __grado 0__: qualunque cosa accada _la centrale nucleare non può esplodere_;
-- __grado 1__: in un certo momento se si assume il controllo di tutto ciò che avverrà _è possibile portare la centrale nucleare allo spegnimento_;
-- __grado 2__: Duccio, ingegnere della centrale nucleare che si trova in coffee break, è in grado di interagire con la macchinetta del caffé appena accesa in modo da avere un numero di caffé _grande a piacere_, almeno finchè qualcuno non inserisce una moneta nella macchinetta;
-- __grado 3__: Biascica, guardia giurata della centrale, è in grado di fare alzare la sbarra per il parcheggio _un numero infinito di volte_;
-- __grado 4__: se succede qualcosa fuori dal controllo all'interno della centrale si può comunque riuscire ad eseguire lo spegnimento (René urla _"chiudi tutto, Duccio!"_).
-
-Una rete viene chiamata __viva__ quando tutte le sue transizioni sono vive.
-
-### Esempio
-
-{% responsive_image path: 'assets/14_esempio-vitalita-transizioni.png' %}
-
-- Da questo esempio pratico è possibile notare come la transizione $$t_0$$ è di **grado 0** in quanto non potrà mai scattare, perchè è impossibile che abbia i gettoni necessari nel preset per scattare (al massimo o in $$p_0$$ o in $$p_1$$).
-- La transizione $$t_1$$ è di **grado 1** perchè esiste almeno una marcatura raggiungibile per cui essa scatti, infatti la marcatura corrente è quella che ne _permette_ lo scatto (ricordando ancora che se una transizione è abilitata allo scatto non significa che debba scattare).
-- Osservando la transizione $$t_3$$ è possibile notare che essa scatti infinite volte (e non $$n$$ grande a piacere, quindi non si tratta di una transizione di grado 2), ma nel caso avvenga lo scatto di $$t_1$$ la transizione $$t_3$$ non potrà mai più essere abilitata (quindi esiste una marcatura in cui non sarà possibile il suo scatto) garantendo che non si tratta di una transizione di grado 4, ma bensì di **grado 3**.
-- Il caso più particolare è quello della transizione $$t_2$$: è noto che $$t_3$$ può scattare infinite volte e quindi in $$p_2$$ possono esserci infiniti gettoni; inoltre, conseguentemente allo scatto di $$t_1$$ il posto $$p_1$$ conterrà un gettone, ma comunque la transizione $$t_2$$ non può scattare infinite volte.
-Questo perchè è vero che all'infinito posso generare gettoni in $$p_2$$, ma dal momento che scatta $$t_1$$ si perde questa possibilità, permettendo a $$t_2$$ di scattare tante volte quanti sono i gettoni in $$p_2$$. 
-La transazione è quindi di **grado 2**.
-- Infine $$t_4$$ è una transizione viva (di **grado 4**), perchè qualunque sia la marcatura raggiungibile dalla marcatura corrente è possibile prendere il controllo e sicuramente esiste una sequenza di scatti tale per cui $$t_4$$ diventi abilitata.
-
-## Capacità dei posti 
-
-Inizialmente è stato detto che esistono diversi dialetti riguardanti le reti di Petri.
-Una possibile estensione consiste infatti nel fissare una **capacità massima** rispetto al numero di gettoni ammissibili in un posto.
-Un esempio potrebbe essere quello in cui in un sistema possono essere presenti $$k$$ lettori contemporaneamente e non più di $$k$$.
-Avendo la possibilità di definire una capacità dei posti, è facile intuire che diventa possibile _forzare la limitatezza della rete_.
-
-Tale estensione aumenta la potenza espressiva oppure è semplicemente una scorciatoia?
-Tramite l'esempio sottostate si può notare che questa estensione non è altro che una tecnica per facilitare la scrittura della rete.
-
-{% responsive_image path: 'assets/14_simulazione-capacita-posti.png' %}
-
-Nella rete con capacità dei posti limitata per far sì che ad esempio la transizione $$t_0$$ scatti, è necessario sia che i posti nel suo preset abbiano gettoni sufficienti sia che dopo il suo scatto il posto $$p_0$$ non superi il limite assegnatogli.
-Volendo scrivere la stessa rete utilizzando il metodo classico visto fino ad ora basta aggiungere un __posto complementare__ che quindi rende le reti __equipollenti__, ossia aventi lo stesso valore espressivo.
-
-Fino a che nel posto complementare esistono dei gettoni, la transizione $$t_0$$ può infatti scattare; dal momento però che tutti i gettoni di $$p_0(\text{compl})$$ vengono bruciati, $$t_0$$ non sarà più abilitata e nel posto $$p_0$$ ci sarà il numero massimo di gettoni possibili.
-Notare come la somma dei gettoni del posto considerato sia esattamente la capacità massima scelta in precedenza.
-
-Questa proprietà vale solo per le reti __pure__, ovvero _le reti che_ ___per ogni transizione___ _hanno preset e postset disgiunti_.
-
-### Posto complementare
-Un posto complementare è un posto avente in uscita verso ognuna delle transizioni del posto considerato un **arco di ugual peso** ma di **direzione opposta**.
-
-Matematicamente è possibile scivere questa definizione nel seguente modo: \\
-un posto $$pc$$ è _complementare_ di $$p$$ se e solo se
-
-$$
-\begin{align}
-\forall t \in T \: \Big [ \exists \langle p, \, t \rangle \in F &\Longleftrightarrow \exists \langle t, \, pc \rangle \in F \quad W(\langle p,\, t \rangle) = W(\langle t, \, pc \rangle) \Big ] \\
-\land \
-\forall t \in T \: \Big [ \exists \langle t, \, p \rangle \in F
-&\Longleftrightarrow \exists \langle pc, \, t \rangle \in F \quad W(\langle pc, \, t \rangle) = W(\langle t, \, p \rangle) \Big ] .
-\end{align}
-$$
-
-Per ogni transizione appartenente a $$T$$ in uscita da $$p$$, quindi tale per cui esiste una relazione di flusso dal posto $$p$$ alla transizione $$t$$ deve esistere un flusso che va dalla transizione $$t$$ al posto complementare $$pc$$ avente lo stesso peso. \\
-Inoltre, per le transizioni in ingresso al posto $$p$$ (quindi per ogni transizione $$t$$ appartenente a $$T$$ in ingresso a $$p$$) tali per cui esista un flusso da $$t$$ al posto $$p$$, deve esistere un flusso che va dal posto complementare $$pc$$ a $$t$$ di direzione opposta e avente lo stesso peso.
-
-Questa formula garantisce che la **somma** del numero di gettoni tra il posto e il suo complementare sia costante, permettendo quindi di formulare la **condizione di abilitazione** (lavorando sul preset della transizione) in modo da dipendere anche dal numero di gettoni presenti nel posto in arrivo.
-
-### Abilitazione con capacità
-Come è possibile definire la condizione di abilitazione nel caso di **reti con capacità sui posti**?
-
-La definizione di _abilitazione_ per reti con capacità sui posti è la seguente: \\
-$$t \in T$$ è __abilitata__ in $$M$$ se solo se:
-
-$$
-\begin{align*}
-\forall p \in \operatorname{Pre}(t) &\quad M(p) \geq W(\langle p, t \rangle) \\
-\forall p \in \operatorname{Post}(t) \setminus \operatorname{Pre}(t) &\quad M(p) + W(\langle t, p \rangle) \leq C(p) \\
-\forall p \in \operatorname{Post}(t) \cap \operatorname{Pre}(t) &\quad M(p) - W(\langle p, t \rangle) + W(\langle t, p \rangle) \leq C(p).
-\end{align*}
-$$
-
-Considerando l'immagine seguente, infatti, possiamo notare come la rete di sinistra abbia ancora una transazione abilitata, mentre quella di destra no.
-Nella seconda rete è come se **lo scatto venisse spezzato in due fasi**: la prima in cui vengono generati i gettoni nel posto (in questo caso $$p_3$$), la seconda invece in cui vengono tolti tanti gettoni quanto è il peso dell flusso da $$p_3$$ a $$t_1$$. \\
-Nella prima rete invece questo non accade, è come se si verificasse tutto nello stesso istante.
-
-{% responsive_image path: 'assets/14_esempio-abilitazione-reti-capacita.png' %}
-
-A questo punto, ci si potrebbe chiedere se fosse possibile generare la situazione equivalente nel caso di una rete $$\pt$$ classica: la risposta è **no**, ad eccezione del caso in cui si usano delle reti con posti complementari.
-Utilizzando i __posti complementari__ è infatti possibile rappresentare **solo le _reti pure equivalenti_**, ma _non tutte le reti in generale_: finché non sono presenti archi in entrata e uscita allo stesso posto dalla stessa transizione non sorge alcun tipo di problema.
-
-Come è possibile superare questa limitazione? 
-Si possono pensare due approcci:
-- si trova un altro approccio diverso dai posti complementari;
-- si cerca di dimostrare che una rete non pura ha sempre una equivalente rete pura; \\
-quindi, si procede a rimuovere la capacità utilizzando i posti complementari.
-
-Entrambe le soluzioni non sono così immediate.
-
-<!-- aggiungere esempio / marcature pure / pure-equivalenti / ecc .. -->
-<!-- Si è fermato a questo punto durante la lezione, nella lezione 20 non ha spiegato ancora quale approccio utilizzare -->
-## Archi inibitori
-
-Esiste un'altra estenzione delle reti di petri in cui si utilizzano gli __archi inibitori__, ovvero degli archi che indicano la situazione in cui una transizione ha bisogno che **non siano presenti gettoni nel posto** in modo che possa essere abilitata.
-Un _arco inibitore di peso $$n$$_ indica che la transazione collegata è abilitata se nel posto collegato sono presenti **meno di** $$n$$ gettoni.
-
-In caso di **rete limitata** la **potenza espressiva** di una rete che sfrutta gli archi inibitori **non cambia**, perché esistendo un limite massimo $$k$$ di gettoni all'interno della rete sarà sufficiente creare un posto complementare contente un numero di gettoni tali per cui la somma tra quest'ultimi e i gettoni presenti nel posto considerato sia minore di $$k$$. \\
-A questo punto è necessario che siano presenti due archi (uno in ingresso e uno in uscita) di peso $$k$$, in modo da permettere lo scatto della transizione solo nel caso in cui tutti i gettoni siano all'interno del posto complementare. \\
-In realtà **non è necessario** che tutta la rete sia limitata, è sufficiente che il singolo posto lo sia: è necessario garantire che qualunque sia lo _stato generale_ della rete, in quel preciso posto non ci siano più di $$k$$ gettoni.
-
-Nel caso di una rete **non limitata** invece non è sempre possibile avere una traduzione equivalente della rete di Petri: la **potenza espressiva** delle reti con gli archi inibitori **aumenta**.
-
-Il problema degli archi inibitori è che rendono **inutilizzabili** alcune **tecniche di analisi** che verranno affrontate successivamente.
-<!-- *nessuno* ne sentiva la mancanza :) -->
-
-## Eliminazione pesi sugli archi
-
-In precedenza è stato accennato che per ogni rete avente dei pesi sugli archi è possibile crearne una **equivalente** senza pesi sugli archi (ovvero avente tutti gli archi di peso 1). \\
-Per fare ciò è necessario considerare due casi distinti, ovvero quello con peso sugli archi in **ingresso ad una transizione** e quello con peso sugli archi **in uscita** ad una traniszione.
-
-### Pesi su archi in ingresso
-
-Per poter effettuare questa modifica è necessario avere lo **scatto di una nuova transizione** (in quanto ovviamente non è possibile collegare due archi dallo stesso posto alla stessa transizione), ma non basta.
-Dopo lo scatto di $$t_0$$ è infatti possibile che $$t_0^\text{BIS}$$ non scatti e la rete evolva senza che in $$p_1$$ ci sia il giusto numero di gettoni (problema di concorrenza).
-
-Per risolvere questo problema si sfrutta una sorta di __lock__, ovvero un posto collegato bidirezionalmente con tutte le transizioni della rete tranne per $$t_0$$, a cui è collegato solo in ingresso, e per $$t_0^\text{BIS}$$, a cui è collegato solo in uscita.
-In questo modo è come se lo scatto di $$t_0$$ sia scomposto logicamente in due parti: quando $$t_0$$ scatta viene attivato il lock in modo tale che nessun'altra transizione sia abilitata e, successivamente, lo scatto di $$t_0^\text{BIS}$$ lo rilascia.
-Questo ovviamente non obbliga $$t_0^\text{BIS}$$ a scattare immediatamente, però è certo che la rete non potrà evolvere in alcun altro modo e quindi non si creeranno marcature non esistenti nella rete originale.
-Questa soluzione non è molto elegante perchè esiste un posto avente in ingresso un arco per ogni transizione della rete.
-
-{% responsive_image path: 'assets/14_eliminazione-archi-ingresso.png' %}
-
-### Pesi su archi in uscita
-
-In questo caso il peso da rimuovere è su un arco che esce da un posto ed entra in una transizione, quindi è necessario che vengano **distrutti due gettoni** dallo stesso scatto. \\
-L'approccio da utilizzare è simile: è infatti presente un **posto globale** che fa da **lock** in modo da risolvere il problema di concorrenza tra $$t_8$$ e $$t_1$$.
-In questo caso però è presente un ulteriore problema, ovvero al momento dello scatto di $$t_8$$ il gettone in $$p_0$$ viene consumato, di conseguenza $$t_1$$ non può scattare. Inoltre il resto della rete rimane bloccata, in quanto all'interno del posto globale non è più presente il gettone che è stato consumato sempre dallo scatto di $$t_8$$. \\
-Questo **deadlock** può essere risolto aggiungendo un controllo sul posto $$p_0$$, in modo tale che possa scattare solo quando possiede due o più gettoni: in questo modo non può verificarsi la situazione in cui $$t_8$$ scatti senza un successivo scatto di $$t_1$$.
-
-Il meccanismo della rete inizia ad essere **molto complesso**; nell'esempio viene mostrato solo il caso in cui devono essere consumati due gettoni.
-In altri casi con più gettoni, o con situazioni differenti, la rete aumenterebbe ulteriormente di complessità. 
-Risulta quindi più facile pensare la rete in modo differente.
-
-La tenica descritta sopra non è infatti l'unica esistente per modellare il sistema: nonostante possa essere adatta per questo particolare esempio, è comunque possibile trovarne un'altra per modellare una rete senza fruttare i pesi o una loro **traduzione meccanica**.
-
-{% responsive_image path: 'assets/14_eliminazione-archi-uscita.png' %}
-
-### Reti $$\mathcal{C/E}$$
-Le reti $$\mathcal{C/E}$$ (condizioni eventi) sono delle particolari reti **più semplici**, in cui tutti gli archi hanno **peso uno** e tutti i posti hanno capacità massima uno.
-A prima vista, questo tipo di rete può risultare poco modellabile, ma è in realtà più semplice ed immediata da capire: infatti _i posti rappresentano delle condizioni_ che possono essere __vere__ o __false__ ed in base ad esse è possibile il verificarsi di certi eventi, rappresentati dalle transizioni.
-Ogni rete $$\pt$$ __limitata__ è **traducibile** in un'equivalente rete $$\mathcal{C/E}$$. \\
-Per le reti illimitate non è invece possibile trovare una traduzione, siccome non si possono rappresentare infiniti stati con un tipo di rete che per definizione è limitata.
-
-## Conservatività 
-La conservatività è una proprietà di una rete rispetto ad una funzione $$H$$ che assegna un peso ad ogni posto della rete, e ognuno di questi pesi è positivo. \\
-Esiste quindi una **funzione di pesi** $$H: P \rightarrow \mathbb N \setminus \{ 0 \}$$ tale per cui una rete $$\pt$$ con una marcatura $$M$$ si dice __conservativa rispetto ad $$H$$__ se e solo se:
-
-$$
-\forall M' \in R(\pt, \, M) \quad \sum_{p \in P} H(p) M'(p) = \sum_{p \in P} H(p) M(p).
-$$
-
-Ovvero, per ogni marcatura $$M'$$ raggiungibile dalla marcatura iniziale data una certa marcatura e una funzione $$H$$, si dice che la rete è conservativa se la sommatoria dei gettoni di ogni posto (quest'ultimi pesati attraverso la funzione $$H$$) è _**costante** per qualunque marcatura raggiungibile_.
-
-### Conservatività $$\Rightarrow$$ limitatezza
-
-Esiste inoltre un **legame** tra **conservatività** e **limitatezza**, ovvero _una rete che garantisce la conservatività è limitata, ma non è detto il viceversa_ (quindi _la limitatezza è una condizione necesaria ma non sufficiente per la conservatività_).
-
-#### Dimostrazione
-
-Assumendo che $$\sum_{p \in P} H(p) M(p)=k$$, allora
-
-$$
-\forall M' \in R(\pt, \, M) \quad \sum_{p \in P} H(p) M'(p) = k.
-$$
-
-Sapendo inoltre che $$\forall p \in P \quad H(p) > 0$$, allora ogni elemento della sommatoria ha un **contributo nullo o positivo**.
-Infatti, se non ci sono gettoni all'interno del posto il contributo della sommatoria sarà un numero positivo ($$H(p)$$) moltiplicato per 0, quindi nullo. 
-
-Quindi, se esiste almeno una marcatura di $$p$$ cui numero di gettoni è diverso da 0, il suo contributo è positivo ma limitato da $$k$$.
-Questo vale per ogni posto all'interno della rete, riconducendosi di conseguenza alla definizione di <a href="#limitatezza">limitatezza</a>.
-<span markdown="1" style="float: right;">$$\blacksquare$$</span>
-
-### Rete strettamente conservativa
-La _conservatività stretta_ è un particolare caso di conservatività definibile come segue: una rete $$\pt$$ conservativa rispetto alla funzione $$H$$ che assegna pesi tutti uguali a 1 si dice _strettamente conservativa_.
-
-$$
-\forall M' \in R(\pt, \, M) \quad \sum_{p \in P} M'(p) = \sum_{p \in P} M(p).
-$$
-
-La sommatoria del numero di gettoni per ogni posto in una _qualsiasi marcatura_ è **costante**, ovvero è uguale alla sommatoria dei gettoni della marcatura iniziale per ogni posto. 
-In altre parole, dopo lo scatto di una transazione viene forzata la **distruzione del gettone in ingresso** e la **generazione di un'altro in uscita**. 
-
-Matematicamente questo concetto si può esprimere anche tramite questa espressione:
-
-$$
-\forall t \in T \quad \sum_{p \in \operatorname{Pre}(t)} W(\langle p, \,  t \rangle) = \! \sum_{p \in \operatorname{Post}(t)} \! W(\langle t, \, p \rangle)
-$$
-
-Per ogni transizione $$t$$ la somma dei pesi degli archi che collegano ogni elemento del preset di $$t$$ alla transizione $$t$$ deve essere uguale alla sommatoria dei pesi degli archi che collegano la transizione $$t$$ con ogni posto nel postset di $$t$$.
-
-Le due espressioni sopra esprimono lo stesso concetto, ma la prima si riferisce alle **marcature** (stati) analizzando dinamicamente calcolando gli stati raggiungibili mentre l'altra all'**aspetto topologico** della rete (ovvero i pesi degli archi).
-
-Si precisa che per quanto riguarda la seconda formula, le espressioni da considerare sono quelle __non morte__ (di grado $$\geq 1$$).
-La seconda è anche più generale rispetto alla prima, ma potrebbe erroneamente considerare **non** strettamente conservative reti che **invece lo sono**.
-
-## Stato base e rete revertibile
-Una marcatura $$M'$$ si dice __stato base__ di una rete se per ogni marcatura $$M$$ in $$R(M_0)$$, $$M'$$ è raggiungibile da $$M$$, ovvero _qualunque_ sia lo stato attuale della rete è **sempre possibile** raggiungere la marcatura $$M'$$.
-
-Quando la marcatura iniziale $$M_0$$ è lo stato base della rete per ogni marcatura $$M$$ in $$R(M_0)$$ allora la rete si dice __reversibile__, ovvero lo stato iniziale è uno stato base.
diff --git a/_posts/2022-12-19-analisi-reti-di-petri.md b/_posts/2022-12-19-analisi-reti-di-petri.md
deleted file mode 100644
index 7d0800714f99e8909866a049ec6abfbc5e630ba7..0000000000000000000000000000000000000000
--- a/_posts/2022-12-19-analisi-reti-di-petri.md
+++ /dev/null
@@ -1,987 +0,0 @@
----
-layout: post
-title: "[15] Analisi di reti di Petri"
-date: 2022-12-19 14:40:00 +0200
-toc: true
----
-# Analisi delle reti di Petri
-
-Le reti di Petri sono state introdotte per poter **analizzare un sistema** ancora prima di avere il codice.
-Alcune domande da porsi sono:
-
-- può essere raggiunta una determinata marcatura?
-- è possibile una certa sequenza di scatti?
-- esiste uno stato di deadlock all'interno della rete?
-- la rete (o una certa transizione) è viva? E di che grado?
-
-Per rispondere a queste domande esistono diverse tecniche, suddivise in:
-
-- **tecniche dinamiche**:
-  - albero (grafo) delle marcature raggiungibili (chiamato anche **grafo di raggiungibilità**);
-  - albero (grafo) di copertura delle marcatura raggiungibili (chiamato anche **grafo di copertura**);
-- **tecniche statiche**:
-  - identificazione delle **P-invarianti** (caratteristiche invarianti riguardanti i posti);
-  - identificazione delle **T-invarianti** (caratteristiche invarianti riguardanti alle transizioni).
-
-Le tecniche dinamiche ragionano sugli **stati raggiungibili** durante l'esecuzione della rete di Petri (o di un programma), mentre le statiche sulla **topologia della rete**.
-
-## Albero di raggiungibilità
-
-Per generare l'_albero di raggiungibilità_ di una rete di Petri si può applicare il seguente **algoritmo**.
-
-<style>
-  .algorithm p {
-    margin-bottom: 0;
-  }
-</style>
-
-<ol class="algorithm">
-  <li markdown="1">
-  **crea la radice** dell'albero corrispondente alla marcatura iniziale $$M_0$$ ed etichettala come _nuova_;
-  </li>
-  <li markdown="1">
-  **_<u>finché</u>_ esistono nodi etichettati come _"nuovi"_** esegui:
-  <ol>
-  <li markdown="1">
-  **seleziona** una marcatura $$M$$ etichettata come _"nuova"_; \\
-    prendila in considerazione e **rimuovi l'etichetta** _"nuova"_.
-  </li>
-  <li markdown="1">
-  ***<u>se</u>*** la **marcatura** $$M$$ è **identica** ad una marcatura di un altro nodo allora:
-  - **etichetta** $$M$$ come **"duplicata"**;
-  - ***<u>continua</u>*** passando alla prossima iterazione.
-  </li>
-  <li markdown="1">
-  ***<u>se</u>*** nella **marcatura** $$M$$ non è abilitata **nessuna transizione** allora:
-  <ul>
-  <li markdown="1">
-  **etichetta** $$M$$ come **"finale"**;
-  </li>
-  <li markdown="1">
-  _situazione di deadlock_.
-  </li>
-  </ul>
-  ***<u>altrimenti</u>*** esegui:
-  <ul markdown="1">
-  <li markdown="1">
-  ***<u>finché</u>* esistono transizioni abilitate** in $$M$$ esegui:
-  <ul>
-  <li markdown="1">
-  ***<u>per ogni</u> transizione* $$t$$ abilitata** in $$M$$ esegui:
-  <ol>
-  <li markdown="1">
-  **crea** la **marcatura** $$M'$$ prodotta dallo **scatto** di $$t$$;
-  </li>
-  <li markdown="1">
-  **crea** un nuovo **nodo** corrispondente alla marcatura $$M'$$;
-  </li>
-  <li markdown="1">
-  **aggiungi** un **arco** nell'albero al nodo corrispondente di $$M$$ al nodo di $$M'$$;
-  </li>
-  <li markdown="1">
-  **etichetta** la **marcatura** $$M'$$ come **"nuova"**.
-  </li>
-  </ol>
-  </li>
-  </ul>
-  </li>
-  </ul>
-  </li>
-  </ol>
-  </li>
-</ol>
-
-### Esempio
-
-Di seguito è mostrata una consegna di un esercizio riguardo gli alberi di raggiungibilità.
-
-> Modellare tramite una rete di Petri l'accesso ad una risorsa condivisa tra quattro lettori e due scrittori, ricordandosi che i lettori possono accedere contemporaneamente, mentre gli scrittori necessitano di un accesso esclusivo.
-
-Come primo approccio, si possono creare due reti, una per i lettori e una per gli scrittori.
-È possibile successivamente procedere modellando la _Risorsa_ condivisa collegando le diverse parti create.
-
-{% responsive_image path: assets/15_esempio-1-albero-raggiungibilita.png %}
-
-Essendo presente un solo gettone nel posto _Risorsa_, i **lettori** non sono
-in grado di accedervi contemporaneamente.
-Per risolvere questo problema, si può aumentare il numero di gettoni all'interno di _Risorsa_ a 4.
-Per evitare che gli scrittori possano accedere alla _Risorsa_ mentre viene letta, è possibile aggiungere un peso pari a 4 sugli archi da "_Risorsa"_ a _"S\_inizia"_ e da _"S\_finisce"_ a _"Risorsa"_.
-
-Così facendo, per accedere alla _Risorsa_ uno **scrittore** dovrà attendere che tutti i token saranno depositati in essa, garantendo che nessun'altro sta utilizzando la risorsa.
-
-Il **risultato finale** è il seguente.
-
-{% responsive_image path: assets/15_esempio-1-albero-raggiungibilita-rete-completa.png %}
-
-#### Costruzione dell'albero di raggiungibilità
-
-<div style="display: none">
-$$
-\require{color}
-\def\node#1{\fcolorbox{black}{white}{#1}}
-\def\nodenew#1{\fcolorbox{lime}{white}{#1}}
-$$
-</div>
-
-Una volta creata la rete finale, è possibile **generare** l'albero di raggiungibilità seguendo l'**algoritmo precedente**.
-
-Il primo passo è creare il **nodo radice** corrispondente alla marcatura iniziale e marcarlo come <span style="color: green"><i>nuovo</i></span>: $$\nodenew{40420}$$.
-
-Successivamente, occorre procedere _per ogni nodo marcato come nuovo_.
-In questo caso l'unico nodo marcato come _nuovo_ è $$\nodenew{40420}$$.
-Dopo aver rimosso l'etichetta _nuovo_ si verifica che, partendo dalla radice dell'albero, non siano già presenti altri nodi uguali.
-Essendo $$\node{40420}$$ esso stesso la radice (e unico nodo dell'albero), si procede.
-
-A questo punto, per ogni transizione abilitata nella marcatura presa in considerazione ($$\node{40420}$$) la si fa **scattare** generando le altre marcature marcate come _nuovo_ ($$\nodenew{40011}$$ e $$\nodenew{31320}$$) che quindi si **collegano** con un arco alla marcatura originale ($$\node{40420}$$).
-
-La situazione attuale è la seguente.
-
-{% responsive_image path: assets/15_esempio-1-albero-prima-giro-algoritmo.png %}
-
-Si procede quindi con l'algoritmo ripetendo i passi fino ad arrivare in una situazione in cui **non esistono più nodi nuovi**, marcando nel mentre come duplicati tutti i nodi che si re-incontrano nonostante siano già presenti almeno una volta nell'albero.
-
-La **situazione finale** sarà la seguente.
-
-{% responsive_image path: assets/15_esempio-1-albero-finale.png %}
-
-L'albero di raggiungibilità sopra in figura è a ora **completo** e rappresenta tutti gli _stati_ raggiungibili.
-
-Grazie a questo albero, se si volesse verificare che gli scrittori sono in **mutua esclusione** con i lettori, basterà controllare se esiste una marcatura in cui il secondo e il quinto numero (rispettivamente _"LettoriAttivi"_ e _"ScrittoriAttivi"_) sono entrambi contemporaneamente maggiori di zero.
-Si può verificare in modo esaustivo (model checking) guardando tutti i nodi dell'albero.
-Inoltre si può verificare se gli **scrittori** si **escludono a vicenda**, controllando se in ogni marcatura l'ultimo numero (_"ScrittoriAttivi"_) è maggiore di uno.
-Si infine verificare l'assenza di **deadlock**, data dalla presenza o meno di nodi terminali.
-
-_Collassando_ i nodi aventi la stessa marcatura, si può verificare dall'albero di raggiungibilità se la rete è **viva**.
-
-{% responsive_image path: assets/15_grafo-di-raggiungibilita.png %}
-
-La rete è anche **reversibile** in quanto ogni stato è uno _stato base_ ed è quindi possibile raggiungere da ogni stato tutti gli altri stati. \\
-Avendo questo grafo è quindi facile capire che la rete è **viva**, in quanto sono rappresentate tutte le transizioni all'interno del grafo, e siccome il sistema è reversibile è sempre possibile riportarsi ad una situazione in cui si può far scattare una transizione.
-
-### Limiti
-
-- Per poter creare un albero di raggiungibilità è necessario enumerare tutte le possibili marcature raggiungibili, di conseguenza **la rete deve essere obbligatoriamente limitata**: non sarebbe altrimenti possibile elencare tutti i nodi.
-- la **crescita** (esponenziale) del numero degli stati globali può risultare velocemente **ingestibile** per una rete limitata.
-
-Inoltre:
-
-- Questa tecnica di analisi non è in grado di rilevare se una rete è limitata o meno;
-- Nel caso in cui si sappia già che la rete è limitata:
-  - l'albero di raggiungibilità non perde informazioni ed è la esplicitazione degli stati della rete (Quindi ne è di fatto la FSM corrispondente).
-
-## Albero di copertura
-
-A questo punto risulterà normale chiedersi se sia possibile creare una struttura dati (albero e grafo) anche per le **reti illimitate**, cui nodi rappresenteranno _gruppi di stati_ potenzialmente infiniti. \\
-È bene introdurre il concetto di **copertura** prima di procedere.
-
-_Una marcatura $$M$$ **copre** una marcatura $$M'$$ se e solo se:_
-
-$$
-\forall p \in P \quad  M(p) \geq M'(p).
-$$
-
-Ovvero _se per ogni posto in $$P$$, la marcatura $$M(p)$$ è maggiore o uguale a $$M'(p)$$_.
-
-Al contrario, $$M$$ si dice **copribile** da $$M'$$ se e solo se:
-
-$$
-\exists M\smash{''} \! \in R(M') \: \vert \: M'' \! \textit{ copre } M.
-$$
-
-Grazie al concetto di _copertura_ è possibile ridefinire quello di **transizione morte**: \\
-una transizione $$t$$ si dice **morta** se e solo se data la sua _marcatura minima_ $$M$$ (ovvero il minor numero di gettoni necessario in ogni posto nel suo preset per abilitarla) questa **non è copribile** a partire dalla marcatura corrente.
-In caso contrario la transizione $$t$$ è almeno 1-viva.
-
-Si conclude quindi che se una marcatura ne copre un'altra, tutte le azioni possibili nella prima **sono possibili** anche nella seconda.
-È quindi possibile modificare l'_albero di raggiungibilità_ in modo tale che, quando viene creato un nodo è necessario verificare se tra i suoi predecessori ne esiste uno che lo copre, allora a questo punto nei posti dove c'è copertura propria (ovvero $$M(p) \geq M'(p)$$) si mette $$\omega$$. \\
-Il **simbolo** $$\omega$$ rappresenta un numero **grande a piacere** (e non _qualsiasi_), che può aumentare all'infinito: questo aspetto è da tenere in considerazione quando bisogna cercare quali transizioni sono abilitate da esso.
-Questo tipo di notazione ($$\omega$$) viene introdotta per limitare l'aumento spropositato di nodi nel diagramma, comprimendo marcature uguali se non per $$\omega$$.
-
-Se una marcatura $$M$$ copre una precedente $$M'$$, infatti, è possibile **ripetere gli scatti** delle transizioni in $$M'$$ per arrivare ad $$M$$; se al termine di questa operazione sono presenti più gettoni in un posto, allora è possibile crearne un numero grande a piacere.
-
-È importante notare come le transizioni che erano **abilitate** in una certa marcatura $$M'$$ lo saranno anche in una marcatura diversa che copre $$M'$$, a meno che non ci siano **archi inibitori**.
-
-È ora possibile definire l'**algoritmo** per la creazione di un albero di copertura, comunque simile in molti punti al precedente:
-
-<ol class="algorithm">
-  <li markdown="1">
-  **crea la radice** dell'albero corrispondente alla marcatura iniziale $$M_0$$ ed etichettala come _nuova_;
-  </li>
-  <li markdown="1">
-  **_<u>finché</u>_ esistono nodi etichettati come _"nuovi"_** esegui:
-  <ol>
-  <li markdown="1">
-  **seleziona** una marcatura $$M$$ etichettata come _"nuova"_; \\
-    prendila in considerazione e **rimuovi l'etichetta** _"nuova"_.
-  </li>
-  <li markdown="1">
-  ***<u>se</u>*** la **marcatura** $$M$$ è **identica** ad una marcatura di un altro nodo allora:
-  - **etichetta** $$M$$ come **"duplicata"**;
-  - ***<u>continua</u>*** passando alla prossima iterazione.
-  </li>
-  <li markdown="1">
-  ***<u>se</u>*** nella **marcatura** $$M$$ non è abilitata **nessuna transizione** allora:
-  <ul>
-  <li markdown="1">
-  **etichetta** $$M$$ come **"finale"**;
-  </li>
-  </ul>
-  ***<u>altrimenti</u>*** esegui:
-  <ul markdown="1">
-  <li markdown="1">
-  ***<u>finché</u>* esistono transizioni abilitate** in $$M$$ esegui:
-  <ul>
-  <li markdown="1">
-  ***<u>per ogni</u> transizione* $$t$$ abilitata** in $$M$$ esegui:
-  <ol>
-  <li markdown="1">
-  **crea** la **marcatura** $$M'$$ prodotta dallo **scatto** di $$t$$;
-  </li>
-  <li markdown="1">
-  ***<u>se</u>*** sul cammino dalla **radice** ($$M_0$$) alla **marcatura** $$M$$ \\
-  **esiste** una **marcatura** $$M''$$ **coperta** da $$M'$$ allora
-  <ul>
-  <li markdown="1">
-  **aggiungi** $$\omega$$ in tutte le posizioni corrispondenti a coperture proprie;
-  </li>
-  </ul>
-  </li>
-  <li markdown="1">
-  **crea** un nuovo **nodo** corrispondente alla marcatura $$M'$$;
-  </li>
-  <li markdown="1">
-  **aggiungi** un **arco** nell'albero al nodo corrispondente di $$M$$ al nodo di $$M'$$;
-  </li>
-  <li markdown="1">
-  **etichetta** la **marcatura** $$M'$$ come **"nuova"**.
-  </li>
-  </ol>
-  </li>
-  </ul>
-  </li>
-  </ul>
-  </li>
-  </ol>
-  </li>
-</ol>
-
-Dall'albero generato da questo algoritmo è possibile arrivare al **grafo di copertura**.
-
-Inoltre, ripetendo l'algoritmo precedente su una rete limitata viene generato un grafo di copertura senza $$\omega$$ e quindi equivalente a un albero di raggiungibilità.
-
-L'algoritmo **termina** in ogni caso: è sufficiente **osservare** l'albero risultante per stabilire se la rete considerata è limitata oppure no.
-
-### Esempio
-
-Partendo dalla rete di Petri sottostante ed applicando l'**algoritmo** appena descritto è possibile arrivare ad un **albero di copertura**.
-
-{% responsive_image path: assets/15_esempio-albero-copertura-rete.png %}
-
-Come visto nell'esempio della creazione di un albero di raggiungibilità, il primo passo da fare è creare il nodo radice corrispondente alla marcatura iniziale ($$\nodenew{100}$$) e marcarlo come nuovo. \\
-Successivamente, è necessario considerare l'unico nodo esistente ($$\node{100}$$) e iterare tra le sue transizioni.
-In questo caso, è abilitata la transizione $$t_1$$ che porta a una marcatura $$M' = \nodenew{101}$$.
-
-A questo punto si può notare come la radice sia una **marcatura coperta da $$M'$$**, in quanto:
-
-- $$M \: \vert \: 1 \geq 1 \: \vert \: M'$$;
-- $$M \: \vert \: 0\geq 0 \: \vert \: M'$$;
-- $$M \: \vert \: 1 > 0 \: \vert \: M'$$.
-
-Nel nodo corrispondente alla marcatura $$M'$$ è quindi possibile sostituire l'unica **copertura propria** (quella con il $$>$$ e non il $$\geq$$) con il simbolo $$\omega$$ e marcare il nodo.
-Questa è l'unica parte dell'algoritmo differente da quello che genera l'albero di raggiungibilità: il resto dell'esempio è quindi completato dall'immagine sottostante.
-
-{% responsive_image path: assets/15_esempio-albero-copertura-albero.png %}
-
-Tramite lo stesso procedimento attuato per gli alberi di raggiungibilità, è possibile trasformare il precedente albero in un **grafo di copertura**.
-
-{% responsive_image path: assets/15_esempio-albero-copertura-grafo.png %}
-
-### Considerazioni
-
-- se $$\omega$$ non compare mai nell'albero di copertura la rete è **limitata**;
-- una rete di Petri è **binaria** se nell'albero di copertura compaiono solo 0 e 1;
-- una transizione è **morta** (0-viva) se non compare mai come etichetta di un arco dell'albero di copertura;
-- condizione necessaria affinché una marcatura $$M$$ sia **raggiungibile** è l'esistenza di un nodo etichettato con una marcatura che copre $$M$$ (non sufficiente: _le marcature coperte non sono necessariamente raggiungibili_);
-- non è possibile stabilire se una rete è **viva**.
-
-### Esempio particolare
-
-È doveroso un ulteriore esempio particolare nel caso di reti **non vive**. \\
-Data una _rete non viva_ (come nella figura sotto) dall'albero di copertura **non è possibile** evincere se la rete è effettivamente viva o no: infatti se il nodo $$\node{01$\omega$}$$ è duplicato, quindi non verrà più espanso.
-A questo punto non è possibile aggiungere all'interno dell'albero il nodo $$\node{010}$$, in cui la rete raggiunge un deadlock.
-Questo però significa che questo albero di copertura è uguale a quello della stessa rete senza arco che collega $$p_3$$ a $$t_4$$, che in quel caso è una rete viva.
-Detto ciò si può affermare che tramite l'albero di copertura non è possibile dire se una rete è viva oppure no.
-
-{% responsive_image path: assets/15_esempio-particolare.png %}
-
-#### Da albero di copertura a rete
-
-Passare dall'albero di copertura alla rete di Petri è un'operazione che rispetto all'inverso crea più **incertezza**.
-L'albero di copertura permette infatti di rappresentare reti potenzialmente illimitate, è quindi normale avere come risultato reti di cui non si conosce la struttura: molte reti potrebbero essere associate **allo stesso albero**.
-
-Nel seguente esempio si può notare come la rete ricavata presenta degli _archi tratteggiati_: **potrebbero essere presenti**, oppure no.
-Inoltre, sono assenti anche i pesi negli archi.
-Tale mancanza di informazioni è dovuta in gran parte dalla presenza di $$\omega$$: un nodo con all'interno $$\omega$$ rappresenta **diverse marcature**.
-
-{% responsive_image path: assets/15_esempio-da-albero-a-rete.png %}
-
-È importante notare come le marcature **sicuramente raggiungibili** siano quelle i cui nodi nell'albero di copertura non contengono $$\omega$$: delle altre non si può essere certi.
-
-# Rappresentazione matriciale
-
-Prima di procedere con la spiegazione delle tecniche di analisi statiche, è necessario introdurre una nuovo modo per rappresentare le reti di Petri: la **rappresentazione matriciale**.
-Essendo tutte rappresentazioni _formali_, _non ambigue_ e _complete_, data una qualsiasi rete rappresentata graficamente o in forma logica, è possibile **trasformarla automaticamente** in una rete in forma matriciale, e viceversa.
-
-Il vantaggio principale della rappresentazione matriciale è la **maggiore semplicità** ed **efficienza** nel **trattamento matematico** delle reti.
-
-Le matrici che verranno utilizzate sono diverse, tra cui:
-
-- $$I$$: rappresenta gli **archi in ingresso**, ovvero le coppie di flusso che da un posto vanno nelle transizioni;
-- $$O$$: rappresenta gli **archi in uscita**, ovvero le coppie di flusso che da una transizione vanno nei posti;
-- vettore $$m$$: rappresenta la **marcatura** dei posti.
-
-## Definizione parte statica
-
-### Matrici $$I$$ e $$O$$
-
-Diversamente dalla rappresentazione logica in cui venivano utilizzati degli indicatori alfanumerici per riferirsi ai posti e alle transizioni, nella rappresentazione matriciale viene assegnato un **indice** ad ogni posto e ad ogni transizione.
-Ogni indice deve essere possibilmente **continuo** (senza salti) e **biunivoco**: ogni indice corrisponde ad un posto e ogni posto corrisponde ad un indice.
-
-- indice dei **posti**: $$p: 1..\vert P \vert \rightarrow P$$
-- indice delle **transizioni**: $$t: 1..\vert T \vert \rightarrow T$$
-
-La **dimensione** delle due matrici è $$\vert P \vert \times \vert T \vert$$: la **cardinalità dei posti** corrisponde al numero di righe e il **numero delle transizioni** corrisponde al numero delle colonne.
-
-Per ogni flusso uscente dal posto $$i$$-esimo ed entrate nella transizione $$j$$-esima, l'elemento $$I[i][j]$$ equivale al **peso** di tale flusso, oppure $$0$$ se il flusso non esiste.
-In sintesi:
-
-$$
-\forall i \in 1..\vert P \vert , \, \forall j \in 1..\vert T \vert \quad I[i][j] = \begin{cases}
-W(\langle p(i), \, t(j) \rangle) &\text{se} \ \langle p(i), \, t(j) \rangle \in F, \\
-0 &\text{altrimenti}.
-\end{cases}
-$$
-
-Analogamente, per la matrice degli output $$O$$:
-
-$$
-\forall i \in 1..\vert P \vert , \, \forall j \in 1..\vert T \vert \quad O[i][j] = \begin{cases}
-W(\langle t(j), \, p(i) \rangle) &\text{se} \ \langle t(j), \, p(i) \rangle \in F, \\
-0 &\text{altrimenti}.
-\end{cases}
-$$
-
-Per indicare il vettore colonna $$k$$ da una matrice $$X$$ spesso verrà utilizzata la notazione $$X[.][k]$$.
-
-{% responsive_image path: assets/15_esempio-rappresentazione-matriciale-I-O.png %}
-
-### Marcatura $$m$$
-
-Per ogni posto, il vettore $$m$$ di dimensione $$\vert P \vert$$ indica la **marcatura corrente**.
-
-$$
-\forall i \in 1..\vert P \vert \quad m[i] = M(p(i))
-$$
-
-Che **differenza** c'è tra il vettore $$m$$ e $$M$$? Entrambi logicamente indicano la **stessa cosa**, ma:
-
-- gli indici di $$m$$ sono nell'insieme $$1..\vert P \vert$$;
-- gli indici di $$M$$ sono nell'insieme $$P$$.
-
-## Definizione parte dinamica
-
-### Abilitazione di una transizione
-
-La transizione $$j$$-esima è **abilitata in una marcatura** (espressa dal vettore $$m$$) se e solo se il _vettore colonna_ della sua matrice di **input** $$I[.][j]$$ è minore o uguale alla marcatura corrente $$m$$:
-
-$$
-\boxed{m \ [ \ t (j) >} \Longleftrightarrow I[.][j] \leq m \\
-\textit{o se proprio vogliamo essere precisi...} \\
-\boxed{m \ [ \ t(j) >} \Longleftrightarrow \forall i \in 1..\vert P \vert \quad I[i][j] \leq m[i].
-$$
-
-In sostanza, si controlla che il numero dei gettoni di ogni posto $$p(i)$$ del _preset_ sia maggiore o uguale del peso dell'arco che collega $$p(i)$$ alla transizione.
-
-{% responsive_image path: assets/15_esempio-marcature-abilitate.png %}
-
-### Scatto di una transizione
-
-Lo **scatto** di una transizione $$j$$ in una marcatura $$m$$ produce una marcatura $$m'$$ che si ricava sottraendo elemento per elemento al vettore di partenza la colonna $$j$$-esima della matrice di input e quindi sommando al risultato la colonna j-esima della matrice output.
-
-$$
-\boxed{\boxed{m [ \ t(j) >} \: m'} \Longleftrightarrow m' = m - I[.][j] + O[.][j] \\
-\textit{o se proprio vogliamo essere precisi...} \\
-\boxed{\boxed{m [ \ t(j) >} \: m'} \Longleftrightarrow \forall i \in 1..\vert P \vert \quad m'[i] = m[i] - I[i][j] + O[i][j]. \\
-$$
-
-{% responsive_image path: assets/15_esempio-scatto-transizione.png %}
-
-È importante notare come nell'operazione sopra due operandi su tre sono matrici costanti ($$I$$ e $$O$$): è quindi possibile **pre-calcolare** $$O - I$$ per efficienza.
-
-### Matrice di incidenza $$C$$
-
-La matrice $$O - I$$ presentata sopra è infatti chiamata **matrice di incidenza** e si indica con la lettera $$C$$.
-È utile per ottimizzare l'operazione _scatto_ di una rete in forma matriciale.
-In formule:
-
-$$
-\forall i \in 1..\vert P \vert, \, \forall j \in 1.. \vert T \vert \quad C[i][j] = O[i][j] - I[i][j].
-$$
-
-{% responsive_image path: assets/15_esempio-matrice-incidenza.png %}
-
-$$C$$ **non sostituisce** le matrici di input $$I$$ e output $$O$$, in quanto $$I$$ è ancora necessaria per calcolare l'abilitazione di una transizioni.
-Per le **reti non pure**, infatti, il valore presente in un qualsiasi posto della matrice potrebbe essere dato da una _qualsiasi combinazione_ di pesi relativi ad archi in ingresso ed uscita, in quanto per la stessa posizione $$\langle i, \, j \rangle$$ entrambe le matrici potrebbero assumere un valore.
-
-### Sequenze di scatti
-
-Si consideri una **sequenza** di **$$n$$ scatti** che porti la rete da una marcatura iniziale $$M$$ a una marcatura $$M^n$$. \\
-Ripetendo il seguente processo per $$n$$ scatti
-
-$$
-\boxed{\boxed{M [ \ t_1 >} \: M' \vphantom{M''}}, \; \boxed{\boxed{M' [ \ t_2 >} \: M''} \rightarrow \boxed{\boxed{M [ \ t_1t_2 >} \: M''},
-$$
-
-si rinomini la sequenza ottenuta nel seguente modo:
-
-$$
-\boxed{\boxed{M [ \ s >} \: M^{(n)}}.
-$$
-
-Esiste un **legame diretto** tra la marcatura iniziale e quella finale, che non preveda eseguire i **singoli passi**?
-A livello di matrici, l'esecuzione in sequenza di $$x_1$$ volte di $$t_1$$, $$x_2$$ volte di $$t_2$$ fino a $$x_n$$ volte di $$t_n$$ è **fattorizzabile**.
-Definendo un vettore $$s$$ tale per cui
-
-$$
-\forall j \in 1..\vert T \vert \quad s[j] = \text{# di volte in cui $t(j)$ scatta}
-$$
-
-è facile notare come l'**ordine di scatto non conta**.
-Calcolando quindi $$Cs$$ è quindi possibile calcolare l'**effetto netto** dell'intera sequenza di scatti, svolgendo un'unica operazione.
-Sommando $$Cs$$ alla marcatura iniziare $$M$$, si ottiene lo stato della marcatura finale $$M^{(n)}$$.
-
-$$
-M^{(n)} = M + C s.
-$$
-
-È opportuno specificare che $$s$$ non è in grado di determinare l'**esistenza** o l'**ordine** della sequenza presa in considerazione.
-Non è quindi possibile sapere se $$s$$ corrisponde a una _sequenza ammissibile_ di scatti, ma è facile escluderlo: se $$M^{(n)}$$ contiene numeri negativi, allora $$s$$ corrisponde sicuramente ad una **sequenza inammissibile**.
-In generale, se anche in un solo passo intermedio $$M^{(i)}$$ è negativo, allora la sequenza considerata non è ammissibile.
-
-In conclusione, è possibile effettuare questo calcolo solo se si è **certi** che la sequenza di scatti sia **ammissibile**.
-
-Di seguito è presente un **esempio** che potrebbe chiarire le idee.
-
-{% responsive_image path: assets/15_esempio-sequenza-scatti.png %}
-
-# Analisi statica
-
-È ora possibile introdurre due tecniche di **analisi statica** che si pongono come obiettivo la ricerca di **invarianti** all'interno della rete.
-Più nello specifico, esistono:
-
-- **$$P$$-invarianti**: invarianti relative alla **marcatura dei posti**;
-- **$$T$$-invarianti**: invarianti relative alla **sequenza di scatto**.
-
-## $$P$$-invarianti
-
-Una $$P$$-invariante è una caratteristica relativa alla marcatura dei posti che **non cambia**; viene rappresentata da un **vettore di pesi** $$h$$ di dimensione $$\vert P \vert$$.
-
-Il vettore $$h$$ ricorda la funzione $$H: P \rightarrow \mathbb N \setminus \{ 0 \}$$ dalla definizione di **rete conservativa**, con l'unica differenza che gli elementi di $$h$$ possono essere nulli o negativi. \\
-Nel caso in cui una $$P$$-invariante abbia tutti i pesi maggiori di zero allora $$h \equiv H$$: la rete sarebbe quindi conservativa e quindi anche **limitata**.
-
-Tramite l'analisi delle $$P$$-invarianti è quindi possibile stabilire se una rete è conservativa e quindi limitata, fornendo un'**alternativa** al metodo dell'albero di copertura.
-
-Per ogni marcatura $$m'$$ _raggiungibile_ da $$m$$, l'**invariante** è il prodotto vettoriale della marcatura con $$h$$.
-
-$$
-\forall m' \text{ raggiungibile da }m \quad hm = hm'
-$$
-
-Se $$m'$$ è raggiungibile da $$m$$, allora esiste una **sequenza di scatti** ammissibile $$s$$ tale per cui
-
-$$
-m' = m + C s,
-$$
-
-è quindi possibile moltiplicare entrambi i membri per $$h$$ in modo da avere
-
-$$
-hm = h(m + Cs) \\
-hm = hm + hCs
-$$
-
-quindi, semplificando i due $$hm$$,
-
-$$
-\boxed{hCs = 0}.
-$$
-
-Ritornando alle assunzioni iniziali, tale proprietà vale solo se esiste una **sequenza di scatti ammissibile**: le informazioni su $$m$$ non sono andate perse.
-
-Nell'ultima formula è presente la matrice $$C$$ (**nota**), il vettore di pesi $$h$$ (**incognita**) e il vettore $$s$$ (**variabile libera**).
-La relazione vale infatti **per ogni** sequenza di scatti ammissibile $$s$$.
-La **formula precisa** è quindi:
-
-$$
-\forall s \quad hCs = 0 \\
-\text{con $s$ rappresentante una sequenza di scatti ammissibile.}
-$$
-
-Assumendo per un momento che $$hC = 0$$, allora qualsiasi sia $$s$$ il risultato è sempre zero, **perdendo informazione** su quest'ultima. \\
-Analogamente, in una rete che possiede una **transizione morta** la corrispondente posizione in $$s$$ sarà sempre zero causando l'azzeramento anche della relativa posizione nel risultato.
-
-Non è quindi **necessario** che $$hC = 0$$ per far sì che $$hCs = 0$$, ma è sicuramente **sufficiente**.
-
-In conclusione, considerando solo $$hC = 0$$ è possibile **escludere** la **componente dinamica** dalla proprietà ragionando solo in base alle informazioni topologiche ($$C$$) della rete.
-Trovare l'$$h$$ che rende $$hC = 0$$ è quindi **condizione sufficiente** ma non necessaria per cui $$h$$ è una $$P$$-invariante, tenendo a mente che esistono comunque $$h$$ che non rendono $$hC = 0$$ ma potrebbero essere $$P$$-invarianti.
-
-I $$P$$-invarianti determinati con l'espressione $$hC = 0$$ non dipendono dalla marcatura iniziale ma solo dalla **topologia** della rete: se venisse considerato anche $$s$$ sarebbero $$P$$-invarianti per qualunque evoluzione della rete _a partire dalla marcatura $$m$$_.
-
-Il sistema $$hC = 0$$ è un **sistema di equazioni lineare**, risolvibile con varie tecniche presentate successivamente.
-
-### Copertura di $$P$$-invarianti
-
-Una **combinazione lineare** di $$P$$-invarianti (e quindi di soluzioni del sistema) è anch'essa una \\
-$$P$$-invariante.
-
-Una $$P$$-invariante $$h$$ avente tutti i pesi $$\geq 0$$ è detta **semi-positiva**.
-Se un posto ha _peso positivo_ in una \\
-$$P$$-invariante semi-positiva, allora  è **limitato** nel numero di gettoni massimi che può contenere. \\
-Se così non fosse, infatti, il contributo nella sommatoria vista precedentemente di $$h[i]m[i]$$ (con $$h[i] \geq 0$$ e $$m[i] > 0$$) sarebbe **potenzialmente illimitato**. \\
-Se un posto ha peso nullo, potrebbe quindi essere **illimitato**.
-
-Avere pesi dei posti **negativi** non fornisce nessuna informazione sulla limitatezza degli stessi nella rete.
-
-Infine, se **per ogni posto** esiste una $$P$$-invariante semi-positiva il cui peso del posto è positivo, allora la rete è **limitata**.
-Matematicamente:
-
-$$
-\begin{align*}
-&\forall i \in 1..\vert P \vert, \, \exists h \in \mathbb{R}^{\vert P \vert} \quad hC = 0 \land h[.] \geq 0 \land h[i] > 0 \\
-&\Rightarrow \text{$C$ rappresenta una rete limitata.}  
-\end{align*}
-$$
-
-Si può anche dire che se esiste una **combinazione lineare** di $$P$$-invarianti tale per cui il $$P$$-invariante risultante è **strettamente positivo**, allora vuol dire che la funzione $$H : P \rightarrow \mathbb N^+$$ (che restituisce proprio quei pesi trovati) è una delle funzioni per cui la rete risulta **conservativa**.
-
-### Esempio
-
-Di seguito è illustrato un esempio sulle proprietà viste delle $$P$$-invarianti.
-
-{% responsive_image path: assets/15_esempio-p-invarianti.png %}
-
-Date le matrici $$I$$, $$O$$ e $$C = O - I$$ sopra è necessario risolvere il sistema $$hC = 0$$
-
-$$
-h \begin{bmatrix}
--1 & 1 & 0 & 0 \\
-1  & -1 & 0 & 0 \\
--1 & 1 & -4 & 4 \\
-0 & 0 & -1 & 1 \\
-0 & 0 & 1 & -1
-\end{bmatrix} \! = 0,
-$$
-
-che si trasforma nel seguente sistema di equazioni lineari:
-
-$$
-\begin{cases}
--h_0 &+h_1 &-h_2 & & \hphantom{+h_4} = 0 \\
-+h_0 &-h_1 &+h_2 & & \hphantom{+h_4} = 0 \\
-& &-4h_2 &-h_3 &+h_4 = 0 \\
-& &+4h_2 &+h_3 &-h_4 = 0.
-\end{cases}
-$$
-
-È facilmente notabile che la prima e la seconda equazioni sono uguali a meno di una costante moltiplicativa: sono quindi **linearmente dipendenti**, insieme alla terza e alla quarta. \\
-Chiedendo quindi a Wolfram|Alpha di risolvere il sistema
-
-$$
-\begin{cases}
--h_0 &+h_1 &-h_2 & & \hphantom{+h_4} = 0 \\
-& &-4h_2 &-h_3 &+h_4 = 0,
-\end{cases}
-$$
-
-otteniamo le seguenti **basi**:
-
-$$
-\{ \langle -1, 0, 1, 0, 4 \rangle, \, \langle 1, 0, -1, 4, 0 \rangle, \, \langle 1, 1, 0, 0, 0 \rangle \},
-$$
-
-che se combinate linearmente generano **infinite soluzioni**.
-
-Tra le tre basi ottenute, l'ultima è **particolare** in quanto ha tutti gli elementi semi-positivi: di conseguenza i posti corrispondenti alle prime due posizioni (con pesi strettamente positivi) sono **limitati**. \\
-Rimane comunque difficile stabilire se la **rete** è limitata oppure no.
-
-### Algoritmo di Farkas (1902)
-
-E se esistesse un algoritmo che predilige la ricerca di **basi minime** per un sistema di equazioni, privilegiando quelle **semipositive**?
-L'algoritmo in questione è l'**algoritmo di Farkas**.
-
-{% responsive_image path: assets/15_algoritmo-farkas.png %}
-
-Sia $$n$$ il numero di righe e $$m$$ il numero di colonne della matrice di incidenza $$C$$. \\
-L'algoritmo inizia creando una matrice $$D_0$$ ottenuta da $$C$$ alla quale viene appesa una matrice di identità $$E_n$$ di dimensione $$n \cdot n$$.
-Quindi, **per ogni colonna** $$i$$ da $$1$$ a $$m$$ si considerano le **coppie di righe** $$\langle d_1, \, d_2 \rangle$$ aventi nella $$i$$-esima riga numeri di segno opposto (non necessariamente uguali in valore assoluto).
-
-Per **ogni coppia di righe** $$\langle d_1, \, d_2 \rangle$$:
-
-- si crea una riga temporanea $$d$$ ottenuta **combinando linearmente** la linea $$d_1$$ moltiplicata per il valore assoluto dell'$$i$$-esimo elemento della riga $$d_2$$ e sommando il viceversa. \\
-Così facendo, l'$$i$$-esimo argomento della riga $$d$$ è uguale a **zero**;
-- per evitare instabilità numerica dovuta a numeri troppo grandi si divide $$d$$ per il **massimo comun divisore** dei suoi elementi, assegnando il risultato a $$d'$$;
-- si estende la matrice $$D_{i-1}$$ aggiungendo una nuova ultima riga $$d'$$.
-
-Una volta terminato il ciclo sulla coppia di righe, si **scartano** tutte le righe della matrice $$D_{i-1}$$ cui $$i$$-esimo elemento è diverso da $$0$$.
-Infine, al termine del ciclo esterno si eliminano le prime $$m$$ colonne di $$D_{m}$$, essendo azzerate.
-Nella matrice risultante (corrispondente alla matrice $$E_n$$) sono presenti i $$P$$-invarianti.
-
-### Continuazione dell'esempio con Farkas
-
-Nell'esempio iniziato in precedenza si era arrivati ad un punto in cui si necessitava ottenere **basi semi-positive** e quindi $$P$$-invarianti semi-positivi: per fare ciò si può applicare l'algoritmo sopra descritto.
-
-Si inizia creando la matrice $$D_0 = [C \: \vert \: E_n]$$:
-
-$$
-D_0 = \begin{bmatrix}
--1  &1  &0  &0  &1 &0  &0  &0  &0 \\
- 1  &-1 &0  &0  &0 &1  &0  &0  &0 \\
--1  &1  &-4 &4  &0 &0  &1  &0  &0 \\
- 0  &0  &-1 &1  &0 &0  &0  &1  &0 \\
- 0  &0  &1  &-1 &0 &0  &0  &0  &1
-\end{bmatrix}.
-$$
-
-Osservando la **prima colonna** ($$i = 1$$) si nota che sono presenti due coppie di righe aventi segno opposto: la prima e la seconda, la seconda e la terza.
-
-A questo punto si possono **combinare linearmente** le coppie appendendo i risultati come **ultima riga**:
-
-$$
-D_0 = \begin{bmatrix}
--1  &1  &0  &0  &1 &0  &0  &0  &0 \\
- 1  &-1 &0  &0  &0 &1  &0  &0  &0 \\
--1  &1  &-4 &4  &0 &0  &1  &0  &0 \\
- 0  &0  &-1 &1  &0 &0  &0  &1  &0 \\
- 0  &0  &1  &-1 &0 &0  &0  &0  &1 \\
- 0  &0  &0  &0  &1  &1  &0  &0  &0 \\
- 0  &0  &-4 &4  &0  &1  &1  &0  &0
-\end{bmatrix}.
-$$
-
-Le prime tre righe contengono nella colonna $$i$$-esima (la prima) elementi non nulli; si **scartano**:
-
-$$
-D_1 = \begin{bmatrix}
- 0  &0  &-1 &1  &0 &0  &0  &1  &0 \\
- 0  &0  &1  &-1 &0 &0  &0  &0  &1 \\
- 0  &0  &0  &0  &1  &1  &0  &0  &0 \\
- 0  &0  &-4 &4  &0  &1  &1  &0  &0
-\end{bmatrix}.
-$$
-
-Si procede iterativamente senza ulteriori azioni fino alla terza colonna, dove sono presenti **due coppie di righe** aventi segni opposti in posizione $$i$$: la prima e la seconda, la prima e la quarta.\\
-Applicando gli stessi passaggi di prima, la matrice $$D_3$$ che si ottiene è la seguente:
-
-$$
-D_3 = \begin{bmatrix}
- 0  &0  &0  &0  &1  &1  &0  &0  &0 \\
- 0  &0  &0  &0  &0  &1  &1  &0  &4 \\
- 0  &0  &0  &0  &0  &0  &0  &1  &1
-\end{bmatrix}.
-$$
-
-Infine, considerando la matrice $$D_m$$ senza le prime colonne nulle, si ottengono le seguenti basi di $$h$$:
-
-$$
-\{ \langle 1, 1, 0, 0, 0 \rangle, \, \langle 0, 1, 1, 0, 4 \rangle, \, \langle 0, 0, 0, 1, 1 \rangle \}.
-$$
-
-#### Interpretazione dei risultati ottenuti
-
-È facile notare come la rete sia **limitata**, in quanto per ogni posizione (_posto_) esiste almeno un $$P$$-invarianti semipositivi cui valore in tale posizione è **strettamente positivo**.
-
-Conoscendo ora possibili valori per $$h$$, nella relazione $$hm = hm_0$$ l'unica incognita al momento è $$m$$: la **marcatura generica** che è possibile raggiungere.
-
-Considerando il **primo $$P$$-invariante** $$h_1 = \langle 1, 1, 0, 0, 0 \rangle$$ e la marcatura iniziale $$m_0 = \langle 4, 0, 4, 2, 0 \rangle$$ si ottiene la **relazione** $$h_1m = hm_0 = 4$$ riguardante i seguenti posti:
-
-$$
-\text{LettoriPronti} + \text{LettoriAttivi} = 4. \tag{$h_1$}
-$$
-
-I posti cui peso è $$1$$ (proveniente da $$h_1$$) sono $$\text{LettoriPronti}$$ e $$\text{LettoriAttivi}$$, mentre il $$4$$ dipende dal numero di gettoni in $$\text{LettoriPronti}$$ in $$m_0$$: la somma dei due è garantita essere **costante**. \\
-In generale, i **termini** a sinistra dipendono da $$h$$, quelli a destra da $$m_0$$.
-
-Per $$h_2 = \langle 0, 0, 0, 1, 1 \rangle$$ il procedimento è lo stesso:
-
-$$
-\text{ScrittoriPronti} + \text{ScrittoriAttivi} = 2. \tag{$h_2$}
-$$
-
-Il terzo risultato, per $$h_3 = \langle 0, 1, 1, 0, 4  \rangle$$ è il **più interessante**:
-
-$$
-\text{LettoriAttivi} + \text{Risorsa} + 4 \cdot \text{ScrittoriAttivi} = 4 \tag{$h_3$}.
-$$
-
-In tutti i risultati è **implicito** che tutti gli **operandi** devono essere **interi maggiori o uguali a zero**.
-
-Con dell'algebra è possibile riscrivere l'**ultimo risultato** ($$h_3$$) nel seguente modo:
-
-$$
-\begin{align}
-\frac{4 \cdot \text{ScrittoriAttivi}}{4} &= \frac{4 - \text{LettoriAttivi} - \text{Risorsa}}{4} \\
-\text{ScrittoriAttivi} &= 1 - \frac{\text{LettoriAttivi}}{4} -\frac{\text{Risorsa}}{4} \\
-\text{ScrittoriAttivi} &= 1 - \frac{\text{LettoriAttivi} + \text{Risorsa}}{4}.
-\end{align}
-$$
-
-Dall'ultima espressione è possibile determinare che gli $$\text{ScrittoriAttivi}$$ sono o **zero** o **uno**, in quanto $$\frac{\text{LettoriAttivi} + \text{Risorsa}}{4}$$ è necessariamente positivo.
-A questo punto "LettoriAttivi" + "Risorsa" si sa essere un valore positivo, che diviso per 4 rimane un numero positivo, a meno che non siano entrambi 0. \\
-È quindi garantito matematicamente che gli scrittori sono in **mutua esclusione**.
-
-Procedendo similmente per i lettore, si ottiene che:
-
-$$
-\text{LettoriAttivi} = 4 \cdot (1 - \text{ScrittoriAttivi}) -  \text{Risorsa}.
-$$
-
-Si può quindi concludere che:
-
-- $$\text{ScrittoriAttivi} \leq 1$$;
-- $$\text{LettoriAttivi} \leq 4$$;
-- $$\text{LettoriAttivi} > 0 \Longrightarrow \text{ScrittoriAttivi} = 0$$;
-- $$\text{ScrittoriAttivi} > 0 \Longrightarrow \text{LettoriAttivi} = 0$$.
-
-## $$T$$-invarianti
-
-I $$T$$-invarianti sono concettualmente **molto simili** ai $$P$$-invarianti, ma pongono alcuni vincoli di **invariabilità** sulle **sequenze di scatti**, ovvero:
-
-- si **possono ripetere** ciclicamente;
-- portano alla situazione iniziale (**stato base**).
-
-Partendo dall'equazione $$m' = m + Cs$$, poniamo il **vincolo** $$m' = m$$ in quanto la sequenza deve tornare alla marcatura iniziale.
-Le **soluzioni** del sistema sono quindi:
-
-$$
-Cs = 0,
-$$
-
-con $$C$$ costante e $$s$$ un **vettore di incognite**, rappresentante una sequenza ammissibile.
-
-Se si risolve il sistema e si trova un vettore $$s$$ rappresentante una sequenza di scatti ammissibile, allora tale sequenza è **ciclica** per cui $$s$$ è un $$T$$-invariante.
-
-A differenza dei $$P$$-invarianti (trovarne uno è _condizione sufficiente_ purché sia valido), per un $$T$$-invariante soddisfare l'equazione è **condizione necessaria** ma non sufficiente per la **validità** della sequenza.
-
-Se una rete è **limitata** e copribile da $$T$$-invarianti, allora è dimostrabile che è anche **viva**.
-
-# Controllori con specifica a stati proibiti
-
-Tramite le reti di Petri si possono **modellare dei controllori** che forzano o limitano certi comportamenti del sistema: se si desidera cioè che la rete rispetti una certa **invariante** si introduce un controllare che la forzi.
-_Controllare_ significa **assicurarsi** che vengano rispettate certe proprietà.
-
-È possibile definire gli **stati** come situazioni che _si possono verificare_, e le **transizioni** come _eventi che si verificano_.
-Lo scopo è **controllare** che le transizioni possano svolgere certe operazioni, oppure no.
-
-Esistono due **classi di problemi** che limitano la capacità espressiva dei controllori:
-
-- **non tutte le transizioni sono osservabili**: il controllore non ne ha le capacità, oppure è un'attività troppo onerosa;
-- l'osservazione di alcune situazioni ne **comporta il cambiamento**.
-
-Inoltre, **non tutto è controllabile**: non si può chiedere ad una centrale nucleare in surriscaldamento di non esplodere, ma si possono attivare i sistemi di sicurezza.
-
-Nel modello del **controllore a stati proibiti**, l'attività di _controllo_ si traduce formalmente in una **combinazione lineare** delle marcature che deve rimanere sotto una certa soglia. \\
-Si vincola quindi per un **sottoinsieme di posti** che la combinazione lineare di una marcatura $$M$$ con un **vettore dei pesi** $$L$$ sia minore o uguale (e non solo _uguale_ come nei $$P$$-invarianti) di una soglia data:
-
-$$
-LM \leq b.
-$$
-
-Come abbiamo visto nel corso di _Ricerca Operativa_, è **sempre** possibile riportare un **sistema di disequazioni** ad un sistema di equazioni **inserendo variabili aggiuntive** (_**slack**_) semipositive:
-
-$$
-LM + x = b \: \vert \: x \geq 0.
-$$
-
-## Mutua esclusione
-
-Il problema della mutua esclusione è l'**accesso esclusivo** a zona critica da parte di più soggetti.
-Nel seguente esempio si vuole imporre che non sia possibile avere gettoni contemporaneamente in $$P_1$$ e $$P_3$$.
-
-{% responsive_image path: assets/15_mutua-esclusione-situazione-iniziale.png %}
-
-Matematicamente il vincolo si può esprimere con la seguente **disequazione**.
-
-$$
-P_1 + P_3 \leq 1
-$$
-
-La tecnica del _controllore a stati proibiti_ aggiunge tanti **posti di controllo** quanti sono il **numero di disequazioni** (e quindi il numero di variabili di _slack_) per modificare il comportamento delle transizioni.
-
-In questo caso, per trasformare la disequazione in un'equazione si aggiunge una variabile di slack, rappresentante il nuovo **posto controllore** $$P_c$$.
-
-$$
-P_1 + P_3 + P_c = 1
-$$
-
-{% responsive_image path: assets/15_aggiunta-posto-controllore.png %}
-
-Per collegare $$P_c$$ alle diverse transizioni occorre aggiungere una riga $$C_c$$ nella **matrice di incidenza** $$C_s$$:
-
-$$
-C_\text{nuova} = \begin{bmatrix}
-  C_s \\
-  C_c
-\end{bmatrix}.
-$$
-
-Inoltre, bisogna aggiungere la marcatura iniziale $$M_{0c}$$ del posto $$P_c$$ alla **marcatura iniziale** del sistema $$M_{0s}$$:
-
-$$
-M_{0} = \begin{bmatrix}
-  M_{0s} \\
-  M_{0c}
-\end{bmatrix}.
-$$
-
-Riscrivendo quindi il vincolo tramite le matrici otteniamo:
-
-$$
-LM_s + M_c = b.
-$$
-
-Sia $$\begin{bmatrix} L I\end{bmatrix}$$ la giustapposizione tra $$L$$ e la **matrice identità** $$I$$ e $$M$$ la giustapposizione di $$M_s$$ e $$M_c$$, allora:
-
-$$
-\begin{bmatrix}
-  L I
-\end{bmatrix}
-M = b.
-$$
-
-L'espressione sopra ricorda la definizione di $$P$$-invariante ($$hm = 0$$).
-Volendo forzare che $$[L I]$$ sia un'invariante, riprendiamo quindi la relativa definizione:
-
-$$
-\begin{bmatrix}
-  L I
-\end{bmatrix}
-C = 0,
-$$
-
-che, rifacendosi al vincolo originale, si può a sua volta riscrivere come
-
-$$
-L C_s + I C_c = 0 \\
-\boxed{C_c = -LC_s}.
-$$
-
-Le righe da aggiungere al sistema $$C_c$$ sono quindi **uguali** a $$-LC_s$$, dove:
-
-- $$C_s$$ è la **matrice di incidenza** del **sistema** originale;
-- $$L$$ è il **vincolo desiderato**, fissato;
-- $$C_c$$ la si trova con un **semplice calcolo matriciale**.
-
-### Sintesi del controllore
-
-Continuando l'esempio precedente, l'obiettivo è trovare .
-
-$$
-
-\begin{align*}
-C_s &= \begin{bmatrix}
-  0  &-1    &0   &1 \\
-  0   &1    &0  &-1 \\
- -1   &0    &1   &0 \\
-  1   &0   &-1   &0
-\end{bmatrix} \quad
-L = \begin{bmatrix}
-  0  &1  &0  &1
-\end{bmatrix} \\
-
--LC_s &= \begin{bmatrix}
-  -1  &-1  & \phantom{-} 1  & \phantom{-} 1
-\end{bmatrix}.
-\end{align*}
-$$
-
-{% responsive_image path: assets/15_archi-posto-controllore.png %}
-
-Il vettore $$-LC_s$$ definisce gli **archi in ingresso** e **in uscita** dalle transizioni per il **posto controllore** $$P_c$$: \\
-il posto ha in ingresso $$T_0$$ e $$T_1$$ (gli elementi con -1) mentre in uscita $$T_2$$ e $$T_3$$ (gli elementi con 1).
-
-Da questi risultati è possibile ottenere anche la **marcatura iniziale** del posto controllore ($$M_{0_c}$$):
-
-$$
-LM_{0s} + M_{0c} = b \\
-\boxed{M_{0c} = b - LM_{0s}}.
-$$
-
-Essendo tutti termini noti, è facile rispondere che la **marcatura iniziale** di $$P_c$$ è uguale a 1.
-
-In **conclusione**, le due formule principali da conoscere sono le seguenti:
-
-- $$\boxed{C_c = -LC_s}$$ per calcolare le **righe** da aggiungere alla **matrice di incidenza** $$C_s$$;
-- $$\boxed{M_{0c} = b - LM_{0s}}$$ per calcolare la **marcatura iniziale** del posto controllore $$P_c$$.
-
-### Esempio
-
-Riprendendo il classico esempio dei **lettori** e **scrittori**, lo scopo di questo esercizio è collegare le due parti assicurando l'accesso esclusivo alla risorsa.
-
-{% responsive_image path: assets/15_esempio_mutua_esclusione_lettori_scrittori.png %}
-
-Dovendo imporre la **mutua esclusione** tra lettori e scrittori, poniamo i seguenti vincoli:
-
-$$
-\begin{cases}
-\text{LettoriAttivi} + \text{ScrittoriAttivi} \leq 1 \\
-\text{LettoriAttivi} + 4 \cdot \text{ScrittoriAttivi} \leq 4 .
-\end{cases}
-$$
-
-Il primo vincolo è incluso nel secondo, quindi possiamo ignorarlo. \\
-Date le **seguenti informazioni**, possiamo realizzare nella rete i vincoli sopra.
-
-$$
-M_0 = \begin{bmatrix}
-  4  &0  &2  &0
-\end{bmatrix},
-\quad
-C = \begin{bmatrix}
-  -1  &1    &0   &0 \\
-  1   &-1    &0  &0 \\
-  0   &0    &1   &-1 \\
-  0   &0   &-1   &1
-\end{bmatrix}
-\tag{Dati della rete}
-$$
-$$
-LM \leq b \tag{Vincolo}
-$$
-$$
-L = \begin{bmatrix} 0 &1 &0 &4 \end{bmatrix}
-\quad
-b = 4
-\tag{Parametri del vincolo}
-$$
-
-È sufficiente quindi sfruttare le formule viste prima per trovare la **nuova riga della matrice di incidenza** e la **marcatura iniziale** di $$P_0$$.
-
-$$
-\begin{align}
-C_c = -LC_s &= \begin{bmatrix}
-  -1  &1  &4  &-4
-\end{bmatrix} \\
-M_{0_c} = b - LM_{0_s} &= 4.
-\end{align}
-$$
-
-# Reti con priorità
-
-Ad ogni transizione è associata una **priorità**: quando in una marcatura $$n$$ transizioni sono abilitate, la scelta della prossima da far scattare è **determinata** dalla sua priorità.
-
-Date le opportune priorità, è quindi possibile **guidare** la progressione della rete verso la soluzione richiesta.
-
-Ci sono due svantaggi principali a questo approccio:
-
-- rischio di creare di **cicli infiniti**;
-- si perde la _località di decisione_ della abilitazione di una transizione: non è quindi più possibile fare analisi locale.
diff --git a/_posts/2023-01-09-reti-temporizzate.md b/_posts/2023-01-09-reti-temporizzate.md
deleted file mode 100644
index ec135c84738926717be60b88ee7f1193a3909ed8..0000000000000000000000000000000000000000
--- a/_posts/2023-01-09-reti-temporizzate.md
+++ /dev/null
@@ -1,550 +0,0 @@
----
-layout: post
-title: "[16] Reti di Petri temporizzate"
-date:   2023-01-09 14:30:00 +0200
-toc: true
----
-
-# Reti Temporizzate
-
-Abbiamo visto come le reti di Petri siano un modello estremamente potente per modellare un'infinita varietà di situazioni anche molto diverse tra loro.
-Tuttavia, alcune categorie di problemi richiedono un approccio più mirato, ovvero un'__estensione delle reti di Petri__ specifica per il loro studio: è questo il caso dei __sistemi Hard Real-time__, di cui ora tratteremo approfonditamente.
-
-In molte applicazioni il __tempo__ è un fattore essenziale: si pensi per esempio ad un termostato intelligente che deve accendere e spegnere i termosifoni di una casa in base ad un programma giornaliero oppure ad un autovelox, che in base al tempo di andata e ritorno di un'onda elettromagnetica dev'essere in grado di calcolare la velocità di un veicolo. \\
-Ma non tutti i sistemi basati sul tempo sono uguali: alcuni di essi richiedono infatti il rispetto assoluto di una serie di __vincoli temporali stretti__, ovvero requisiti sul tempo di esecuzione di certe operazioni che devono essere rispettati per evitare gravi conseguenze.
-Considerando per esempio il sistema di controllo di una centrale nucleare, qualora si inizi a rilevare un'aumento eccessivo delle temperature nel reattore tale software dev'essere in grado di reagire entro un certo tempo strettissimo, pena l'esplosione dell'apparato.
-Sistemi di questo tipo prendono il nome, come già detto, di __sistemi Hard Real-time__, dove l'aggettivo "Hard" indica proprio la durezza richiesta nel rispetto dei vincoli temporali.
-
-Visto il loro tipico impiego in situazioni di rischio o di pericolo, i committenti di sistemi di questo tipo potrebbero voler avere __prova del loro corretto funzionamento__ prima ancora che questi vengano installati.
-I modelli finora descritti potrebbero però non essere sufficienti: non è per esempio abbastanza un'_analisi stocastica_ della rete, in quanto in virtù dei rischi a cui un malfunzionamento del sistema esporrebbe bisogna essere assolutamente __certi__ del suo corretto funzionamento, certezza che può essere ottenuta solo con un __modello deterministico__. \\
-Ecco quindi che come strumento di specifica e comunicazione col cliente vengono sviluppate una serie di estensioni alle reti di Petri progettate specificamente per trattare il concetto di _tempo_ e _ritardo_: tra queste distingueremo in particolare le __reti Time Basic__ (_Ghezzi et al., 1989_), oggi le più usate.
-
-## Modelli temporali
-
-Esistono una serie di proposte per modellare il concetto di __tempo__ (_deterministico_) all'interno delle reti di Petri.
-Esse si dividono sostanzialmente in due grandi categorie:
-
-- quelle che introducono __ritardi sui posti__;
-- quelle che introducono __ritardi sulle transizioni__.
-
-### Tempo sui posti
-
-Il tempo associato a ciascun posto rappresenta il __tempo che un gettone deve rimanere in tale posto prima di essere considerato per l'abilitazione__ di transizioni che hanno tale posto nel proprio preset.
-
-{% responsive_image path: assets/16_tempo-sui-posti.png %}
-
-Dopo lo scatto di una transizione i gettoni generati in un posto non fanno cioè funzionalmente parte della sua marcatura prima che sia passato un dato intervallo di tempo $$\Delta$$.
-Tale $$\Delta$$ può quindi essere considerato la __durata minima di permanenza__ del gettone in tale posto, bloccando così quella porzione di stato del sistema per un certo periodo.
-
-### Tempo sulle transizioni
-
-Quando si associa un tempo ad una transizione è bene indicare che cosa esso rappresenti.
-Il tempo di una transizione può infatti rappresentare due concetti molto differenti:
-
-- la __durata della transizione__, ovvero il tempo richiesto dopo lo scatto della transizione perché vengano generati i gettoni nel suo postset (una sorta di _ritardo di scatto_);
-- il __momento dello scatto__ della transizione, che può essere espresso in modo diverso a seconda del modello.
-
-Esistono a dire il vero anche modelli misti che permettono di specificare sia la durata di una transizione che il suo tempo di scatto.
-
-#### Equivalenza tra tempi sui posti e sulle transizioni
-
-È facile dimostrare che sia le reti che definiscono tempi sui posti che quelle che definiscono tempi sulle transizioni, sia come durata che come momento dello scatto, sono __funzionalmente equivalenti__, ovvero permettono di rappresentare lo stesso insieme di sistemi.
-
-Ciò è testimoniato dal fatto che, come mostra la figura sottostante, ogni rete avente tempo sui posti può essere trasformata in una rete con durata delle transizioni semplicemente aggiungendo una transizione di "ritardo" e separando il posto in due.
-Ovviamente vale anche il viceversa.
-
-{% responsive_image path: assets/16_tempo-posti-to-durata.png %}
-
-Similmente, reti con durata delle transizioni possono essere trasformate in reti con tempi di scatto per le transizioni modellando esplicitamente con un posto il ritardo con cui vengono generati gettoni nel postset della transizione originale.
-
-{% responsive_image path: assets/16_durata-to-tempo-scatto.png %}
-
-#### <big>Tempi di scatto</big>
-
-Ritornando un attimo sui nostri passi, diamo ora un'occhiata migliore a come si possono definire i tempi di scatto di una transizione.
-
-Nella definizione dei tempi di scatto delle transizioni esistono infatti una serie di alternative molto differenti.
-Innanzitutto, i tempi possono essere:
-
-- __unici__, ovvero ogni transizione scatta (o può scattare) in _uno e un solo_ specifico momento;
-- __multipli__, ovvero ogni transizione scatta (o può scattare) in _uno in un insieme_ di momenti.
-    A seconda del modello considerato tali insiemi possono essere veri e propri __insiemi matematici__ (_es. reti TB_) oppure __intervalli__.
-
-Si noti come i primi possono essere visti come casi particolari dei secondi. \\
-Considerando ciò, gli insiemi (anche unitari) di tempi di scatto si distinguono poi in due categorie:
-
-- __insiemi costanti__, ovvero tali per cui l'insieme dei tempi di scatto è __definito staticamente__ ed è sempre uguale indipendentemente dall'evoluzione della rete;
-- __insiemi variabili__, ovvero tali per cui l'insieme dei tempi di scatto può __variare dinamicamente__ in base allo stato della rete o a porzioni di esso (_es. reti TB e HLTPN_).
-
-Anche in questo caso i primi possono essere visti come un caso particolare dei secondi, in cui cioè l'insieme _potrebbe_ variare ma non varia mai. \\
-Infine, i tempi di scatto stessi possono essere divisi in base a come vengono definiti:
-
-- __tempi relativi__, ovvero espressi _solo_ in termini relativi al tempo di abilitazione della transizione (_es. "2 ms dopo l'abilitazione"_);
-- __tempi assoluti__, ovvero espressi in termini relativi a tempi assoluti e ai tempi associati ai gettoni che compongono l'abilitazione (_es. "dopo 3 minuti dall'avvio del sistema" o "dopo 4 ms dal tempo associato all'ultimo gettone nell'abilitazione"_) (_es. reti TBe TCP_).
-
-Nuovamente, i primi possono essere visti come un sottoinsieme dei secondi.
-
-Tutto questo insieme di variabili permette di definire reti temporizzate basate su tempi di scatto delle transizioni anche molto diverse tra di loro.
-Avremo per esempio le _reti Time Petri_, che utilizzano tempi di scatto relativi, multipli e a intervalli costanti; le _reti Time Petri colorate_, simili alle precedenti ma che usano tempi assoluti; le _reti Time Petri ad alto livello_, che usano insiemi variabili, e molte altre.
-
-Tra tutte queste tipologie, tuttavia, ci concentreremo sulle __reti Time Basic__.
-In virtù delle inclusioni di cui abbiamo già detto tali reti saranno quindi le più generali possibile e, dunque, anche le più interessanti.
-
-# Reti Time Basic
-
-Prima di darne una vera e propria definizione matematica iniziamo a introdurre le __reti Time Basic__ (__TB__) in modo informale.
-
-Introdotte per la prima volta da Ghezzi e dai suoi collaboratori nel 1989, le reti TB associano __insiemi variabili__ di tempi di scatto __assoluti__ alle transizioni: ciascuna transizione possiede cioè un insieme di tempi in cui _potrebbe_ scattare, definito in maniera dinamica a seconda dello stato.
-Tali tempi di scatto potrebbero poi essere definiti sia in termini assoluti che in termini dei __tempi associati ai gettoni__.
-
-{% responsive_image path: assets/16_TBN-intro.png %}
-
-Nelle reti TB infatti i gettoni non sono più anonimi, ma caratterizzati ciascuno da un __timestamp__ che indica il __momento in cui sono stati creati__ ($$\operatorname{t}(posto)$$).
-A differenza delle normali reti di Petri i gettoni sono quindi __distinguibili__: questo non significa che due gettoni non possano avere lo stesso timestamp, ma solo che non tutti i gettoni sono uguali (_mentre gettoni generati dalla stessa transizione o da transizioni diverse scattate in parallelo avranno invece lo stesso timestamp_).
-
-Per ogni transizione viene poi introdotto il concetto di __tempo di abilitazione__ ($$\bf{enab}$$), ovvero il __momento in cui la transizione viene abilitata__: poiché una transizione è abilitata quando tutti i posti nel suo preset contengono tanti gettoni quanto il peso dell'arco entrante in essa, il tempo di abilitazione di una transizione sarà pari al __massimo tra i timestamp__ dei gettoni che compongono la __tupla abilitante__. \\
-Poiché i posti nel preset della transizione potrebbero contenere più gettoni di quelli necessari per farla scattare, una transizione potrebbe avere __più tempi di abilitazione diversi__ in base ai gettoni considerati per la tupla abilitante.
-
-Ovviamente i __tempi di scatto__ delle transizioni __non potranno essere minori__ del tempo di abilitazione, in quanto una transizione non può scattare prima di essere abilitata.
-Gli insiemi dei tempi di scatto potranno invece _dipendere_ dal tempo di abilitazione: così, per esempio, una transizione potrebbe scattare 2 secondi dopo essere stata abilitata, oppure tra 3 e 5 minuti dall'abilitazione. \\
-A tal proposito, molto spesso i tempi di scatto saranno rappresentati come __intervalli__ $$[min,max]$$ piuttosto che come insiemi: nei nostri esempi adotteremo questa convenzione, ma è bene tenere in mente che tali insiemi potrebbero avere qualunque possibile forma.
-
-## Definizioni matematiche
-
-Facciamo un po' di chiarezza introducendo delle definizioni rigorose per tutto quanto citato nell'introduzione. \\
-Una rete Time Basic è una __6-tupla__ del tipo $$\langle P, T, \Theta, F, tf, m_0 \rangle$$, dove:
-
-- $$P, T, F$$ sono identici all'insieme dei posti, delle transizioni e al flusso delle normali reti di Petri;
-- $$\Theta$$ (_theta_) è il __dominio temporale__, ovvero l'insieme numerico che contiene le rappresentazioni degli istanti di tempo;
-- $$tf$$ è una funzione che associa ad ogni transizione $$t \in T$$ una __funzione temporale__ $$\operatorname{tf_{t}}$$ che data in input la __tupla abilitante__ $$\bf{en}$$, ovvero l'__insieme dei timestamp__ dei gettoni scelti per l'abilitazione nel preset, restituisce un __insieme di tempi di scatto possibili__:
-
-  $$\operatorname{tf_{t}}(en) \subseteq \Theta$$
-
-  Per esempio, se per una transizione $$t$$ i tempi di scatto sono nell'intervallo $$[min, max]$$, allora $$\operatorname{tf_{t}}(en) = \{r \, \vert \, min \leq r \leq max\}$$.
-
-- $$m_0$$ è un multiset che esprime la __marcatura iniziale__: si tratta cioè di una funzione che ad ogni __posto__ associa un insieme di coppie __timestamp-molteplicità__ che indicano il numero di gettoni con tale timestamp all'interno del posto:
-
-  $$m_0 : P \rightarrow \{ (\theta, \operatorname{mul}(\theta)) \, \vert \, \theta \in \Theta \}$$
-
-  Tutte le __marcature__ esprimibili per le reti Time Basic assumeranno la forma di simili funzioni.
-
-Con questi costrutti matematici siamo in grado di descrivere completamente lo stato di una rete Time Basic.
-Tuttavia sorge ora spontanea una domanda: dovendo modellare il concetto di tempo, come __evolve__ una rete TB?
-
-## Evoluzione
-
-Dovendo modellare lo __scorrere del tempo__, le reti Time Basic dovranno operare una serie di accortezze per quanto riguarda la loro evoluzione.
-
-Abbiamo per esempio già detto che il tempo di scatto di una transizione dovrà necessariamente essere maggiore del suo tempo di abilitazione, e che tale tempo di scatto sarà pari al timestamp dei gettoni generati dalla transizione.
-Tuttavia, questo non è abbastanza: il concetto di tempo è particolarmente sfuggente e, soprattutto, __difficile da definire in maniera univoca__. \\
-Al contrario, per le reti Time Basic vengono definite diverse __semantiche temporali__, ovvero diverse interpretazioni del concetto di "tempo" che richiederanno il rispetto di una serie di assiomi durante l'evoluzione della rete.
-Tali interpretazioni, ciascuna utile per modellare diversi sistemi e requisiti di tempo, variano anche in complessità; in questo corso partiremo dunque dalla semantica più semplice per poi costruire su di essa quelle più complesse.
-
-### Semantica temporale debole (WTS)
-
-Informalmente, la __semantica temporale debole__ (_Weak Time Semantic_, __WTS__) impone che una transizione possa scattare _solo_ in uno degli __istanti identificati dalla sua funzione temporale__ e __non possa scattare _prima_ di essere stata abilitata__.
-
-Tuttavia, ___una transizione non è costretta a scattare___ anche se abilitata: essa _potrebbe_ scattare, ma non è forzata a farlo.
-Questo permette di modellare eventi solo __parzialmente definiti__, ovvero che potrebbero accadere sotto determinate condizioni ma non è possibile dire se lo faranno o no: esempi notevoli sono guasti o decisioni umane, eventi cioè non completamente prevedibili.
-Si noti che a differenza dei modelli stocastici delle reti di Petri in questo caso non ci interessa la _probabilità_ con cui gli eventi potrebbero accadere, ma solo che potrebbero accadere.
-
-Per imporre questa interpretazione del concetto di tempo l'evoluzione di una rete Time Basic deve seguire i seguenti __assiomi temporali__:
-
-- (__A1__) __Monotonicità rispetto alla marcatura iniziale__: tutti i tempi di scatto di una sequenza di scatto devono essere __non minori__ ($$\geq$$) di uno qualunque dei timestamp dei gettoni della marcatura iniziale. \\
-  Ogni marcatura deve cioè essere __consistente__, ovvero non contenere gettoni prodotti "nel futuro".
-
-- (__A3__) __Divergenza del tempo__ (_non-zenonicità_): __non__ è possibile avere un __numero infinito__ di scatti in un intervallo di __tempo finito__. \\
-  Questo assioma serve ad assicurarsi che il tempo __avanzi__!
-  Esso assicura cioè che il tempo non si possa fermare e soprattutto che esso non possa essere suddivisibile in infinitesimi: il sistema evolve soltanto quando il tempo va avanti.
-
-Le sequenze di scatti che soddisfano questi due assiomi vengono dette __sequenze ammissibili in semantica debole__.
-
-### Semantica temporale monotonica debole (MWTS)
-
-Come i più attenti avranno notato, nell'elencare gli assiomi necessari per la semantica temporale debole abbiamo saltato un ipotetico assioma A2.
-Ebbene, ciò non è un caso: esiste infatti un'estensione della semantica WTS che aggiunge tra i propri requisiti il rispetto di tale assioma.
-
-Si tratta della __semantica temporale monotonica debole__ (_Monotonic WTS_, __MWTS__), e differisce dalla semantica WTS perché impone necessariamente che i tempi di scatto delle transizioni all'interno di una sequenza siano monotoni non decrescenti, forzando così il fatto che nell'intera rete __il tempo non possa tornare indietro__. \\
-Più formalmente, la semantica introduce il seguente assioma:
-
-- (__A2__) __Monotonicità dei tempi di scatto di una sequenza__: tutti i tempi di scatto di una sequenza di scatti devono essere ordinati nella sequenza in maniera __monotonicamente non decrescente__ ($$\geq$$). \\
-  Anche questo serve a garantire la proprietà intuitiva di __consistenza__, evitando cioè che il tempo torni indietro.
-  Non richiedendo però che i tempi siano disposti in modo strettamente crescente ma ammettendo che nella sequenza lo __stesso tempo sia ripetuto__ si lascia aperta la possibilità che nella rete più transizioni scattino in contemporanea, oppure che due transizioni scattino in tempi talmente ravvicinati che la granularità temporale del modello non è in grado di rilevare la differenza.
-
-Le sequenze di scatti che soddisfano gli assiomi A1, A2 e A3 vengono dette __sequenze ammissibili in semantica monotonica debole__. \\
-Sebbene sembri una differenza da nulla, imporre la monotonicità dei tempi di scatto ha in realtà ripercussioni piuttosto grandi: in una rete che segue la MWTS quando si analizzano gli scatti è necessario non solo fare un'analisi locale del preset e del tempo di abilitazione e scatto della transizione, ma anche assicurarsi che non ci sia nessuna transizione nella rete in grado di scattare prima.
-Si __perde cioè la caratteristica di località__, introducendo la necessità di mantenere un'informazione comune sull'__ultimo scatto__ nella rete.
-
-#### WTS $$\equiv$$ MWTS
-
-Fortunatamente per noi esiste un teorema che afferma che _per ogni sequenza di scatti ammissibile in semantica debole $$S_{WTS}$$ __esiste__ una sequenza di scatti ammissibile in semantica monotonica debole $$S_{MWTS}$$ __equivalente__ ottenibile per semplice __permutazione__ delle occorrenze degli scatti._
-
-Non si tratterà di sequenze uguali, ma entrambe le sequenze produrranno la __stessa marcatura finale__.
-Questo è un enorme vantaggio, in quanto ciò ci permette di infischiarcene della monotonicità degli scatti durante l'analisi della rete, potendo così sfruttare la __località__ e conseguentemente le __tecniche di analisi per le reti di Petri__ (ad alto livello) già viste in precedenza.
-
-##### <big><u>Esempio di traduzione</u></big>
-
-Si prenda in esame la rete in figura:
-
-{% responsive_image path: assets/16_esempio-wts-mwts.png %}
-
-Assumendo i timestamp iniziali di tutti i gettoni uguali a zero, si consideri la seguente sequenza ammissibile WTS di scatti:
-
-$$ \text{T1 scatta al tempo 12} \rightarrow \text{T3 scatta al tempo 14} \rightarrow \text{T2 scatta al tempo 4}$$
-
-Tale sequenza non rispetta la monotonicità, in quanto T2 scatta "nel passato" dopo lo scatto di T3, e produce la marcatura $$\langle0, 0, 1, 0, 1\rangle$$.
-Tuttavia, riordinando la sequenza come:
-
-$$ \text{T2 scatta al tempo 4} \rightarrow \text{T1 scatta al tempo 12} \rightarrow \text{T3 scatta al tempo 14}$$
-
-è possibile ottenere una marcatura identica ma con una sequenza che rispetta ora la monotonicità, essendo cioè ammissibile in semantica temporale monotonica debole.
-
-### Semantica temporale forte (STS)
-
-Finora abbiamo lasciato aperta la possibilità che una transizione pur _potendo_ scattare non lo facesse.
-Questa alternativa non è però contemplata in molti modelli temporizzati, in cui il __determinismo__ gioca un forte ruolo: spesso si vuole che se una transizione può scattare, allora __deve__ scattare entro il suo massimo tempo di scatto ammissibile.
-
-Per forzare questo comportamento viene creata una semantica temporale apposita, che prende il nome di __semantica temporale forte__ (_Strong Time Semantic_, __STS__): essa impone che _una transizione __deve scattare__ ad un suo possibile tempo di scatto __a meno che non venga disabilitata__ prima del proprio massimo tempo di scatto ammissibile_.
-Aggiungere quest'ultima clausola permette alle transizioni di non dover prevedere il futuro: se esse fossero programmate per scattare in un certo istante ma prima di esso lo scatto di un'altra transizione le disabilitasse non si richiederebbe che esse tornino indietro nel tempo per scattare all'ultimo istante di tempo _utile_.
-
-Essendo un ulteriore irrigidimento rispetto alla semantica temporale monotonica debole, la STS dovrà sia rispettare gli assiomi A1, A2 e A3, sia la seguente nuova coppia di assiomi, che porta il totale a cinque:
-
-- (__A4__) __Marcatura forte iniziale__: il __massimo tempo di scatto__ di tutte le transizioni abilitate nella __marcatura iniziale__ dev'essere __maggiore o uguale del massimo timestamp__ associato ad un gettone in tale marcatura. \\
-  Questo assicura cioè che la marcatura iniziale sia __consistente con la nuova semantica__ temporale: un gettone dotato di timestamp superiore al tempo di scatto massimo di una transizione abilitata non sarebbe potuto essere generato _prima_ che la transizione scattasse (cosa che deve fare!), rendendo quindi la marcatura in questione non più quella iniziale.
-
-- (__A5__) __Sequenza di scatti forte__: una sequenza di scatti ammissibile in semantica __MWTS__ che parta da una __marcatura forte iniziale__ è una __sequenza di scatti _forte___ se per ogni scatto il tempo di scatto della transizione __non è maggiore__ del massimo tempo di scatto di un'altra transizione abilitata. \\
-  Si sta cioè accertando che ogni transizione scatti entro il suo tempo massimo se non viene disabilitata prima da un altro scatto: per fare ciò, si permette alle transizioni di scattare _solo_ se non ci sono altre transizioni abilitate che sarebbero già dovute scattare, costringendo quindi queste ultime a farlo per far continuare a evolvere la rete.
-
-Ecco dunque che sequenze di scatto che soddisfano gli assiomi A1, A2, A3, A4 e A5 vengono dette __sequenze ammissibili in semantica forte__.
-
-#### STS $$\not\equiv$$ MWTS
-
-In virtù dell'ultimo assioma si potrebbe pensare che esista un modo per trasformare ogni sequenza di scatti MWTS in una sequenza STS, realizzando così un'equivalenza.
-Purtroppo, però, non è così: una sequenza STS è sempre anche MWTS, ma __non è sempre vero il contrario__.
-
-Poiché infatti non è più possibile a causa dell'assioma A2 riordinare le sequenze per ottenerne altre di equivalenti, è possibile trovare numerose sequenze che sono MWTS ma non STS.
-Riprendendo la rete già vista in precedenza e assumendo anche in questo caso dei timestamp iniziali nulli per i gettoni:
-
-{% responsive_image path: assets/16_esempio-wts-mwts.png %}
-
-è facile vedere che la sequenza ammissibile in semantica monotonica debole:
-
-$$\text{T2 scatta al tempo 6} \rightarrow \text{T1 scatta al tempo 12} \rightarrow \text{T3 scatta al tempo 14}$$
-
-non è invece una sequenza ammissibile in semantica forte, in quanto lo scatto di T2 abilita la transizione T3, che dovrebbe quindi scattare entro il tempo 9 ($$enab = 6$$) ma non lo fa.
-
-### Semantica temporale mista
-
-Può però capitare che dover imporre una semantica temporale fissa per l'intera rete si riveli limitante nella modellazione di sistemi reali: questi potrebbero infatti includere sia agenti deterministici (_es. computer_) che agenti stocastici (_es. esseri umani_).
-
-Si introduce quindi una nuova __semantica temporale mista__ (_Mixed Time Semantic_), in cui la semantica temporale debole (monotonica) o forte viene associata alle __singole transizioni__ piuttosto che all'intera rete.
-In questo modo:
-
-- le __transizioni forti__ _dovranno_ scattare entro il loro tempo massimo a meno che non vengano disabilitate prima;
-- le __transizioni deboli__ _potranno_ scattare in uno qualunque dei loro possibili tempi di scatto.
-
-Essendo meno comuni, solitamente sono le transizioni deboli ad essere esplicitamente indicate graficamente nelle reti con una $$W$$ all'interno del rettangolo che le rappresenta: tutte le altre transizioni sono invece di default considerate forti.
-
-#### <big>Analisi di abilitazione in presenza di transizioni forti</big>
-
-Introdotta quindi la possibilità che esistano all'interno delle reti delle transizioni forti che devono necessariamente scattare entro il loro tempo massimo di scatto non è ora più tanto semplice fare __analisi di abilitazione__, vale a dire quel tipo di analisi che cerca di tracciare su una linea temporale gli intervalli durante i quali certe transizioni sono abilitate.
-
-Per capire perché, osserviamo la seguente rete Time Basic, che segue una semantica temporale mista:
-
-{% responsive_image path: assets/16_analisi-rete.png %}
-
-Analizzando localmente le singole transizioni, come se avessero tutte semantica temporale debole, si può ottenere il seguente diagramma temporale di abilitazione:
-
-{% responsive_image path: assets/16_analisi-1.png %}
-
-Tuttavia, questo diagramma è __scorretto__.
-La presenza di una transizione forte che deve scattare entro il tempo 10, ovvero T2, non ci permette di dire nulla oltre tale tempo, in quanto _il suo scatto potrebbe disabilitare altre transizioni_.
-Questo era vero anche per la transizione debole T1, ma il suo essere debole permetteva comunque di ignorare tale eventualità nella prospettiva che la transizione, pur potendo, non scattasse: questo tipo di ragionamento non è però purtroppo più possibile in semantica temporale forte.
-
-In sostanza, __le transizioni forti bloccano il nostro orizzonte temporale__. \\
-Ecco dunque che il vero diagramma temporale di abilitazione della rete è il seguente:
-
-{% responsive_image path: assets/16_analisi-2.png %}
-
-# Analisi delle reti Time Basic
-
-Definite le reti Time Basic in ogni loro aspetto è dunque arrivato il momento di __analizzarle__.
-
-Esattamente come le reti di Petri, infatti, le reti TB fanno parte di quei __linguaggi operazionali__ utilizzati per illustrare il funzionamento di un sistema senza entrare nei dettagli della sua effettiva implementazione ($$\neq$$ _linguaggi dichiarativi/logici, che si usano invece per costruire il sistema a partire dalle proprietà richieste_).
-Visto questo ruolo, è necessario possedere una serie di __strumenti di analisi__ specifici che permettano di "simulare" il funzionamento della rete per comprenderne l'evoluzione e le proprietà: ciò appare particolarmente evidente se si ricorda che le reti TB vengono spesso utilizzate per modellare sistemi _Hard Real-Time_ in cui si deve avere la __certezza__ del fatto che il sistema rispetterà tutta una serie di caratteristiche prima ancora di iniziare i lavori.
-
-## Reti TB come reti ad Alto Livello?
-
-Ma è davvero necessario sviluppare un'intera serie di nuove tecniche di analisi specifiche per le reti Time Basic, o è possibile riutilizzare almeno in parte metodologie già discusse?
-
-Si potrebbe infatti immaginare di considerare le reti TB come un tipo particolare di __reti ad Alto Livello__ (ER).
-Come abbiamo già accennato, questo tipo di reti permettono infatti ai gettoni di avere un __qualunque contenuto informativo__ e di definire le transizioni come una coppia __predicato-azione__: il predicato descrive la condizione di abilitazione della transizione in funzione dei valori dei gettoni nel preset, mentre l'azione determina che valore avranno i gettoni creati nel postset. \\
-Volendo modellare il __tempo come concetto derivato__, ovvero non delineato esplicitamente ma che emerga comunque dal _funzionamento_ della rete, si potrebbero quindi creare delle reti ad Alto Livello con le seguenti caratteristiche:
-
-- __contenuto informativo dei gettoni__: un'unica variabile temporale __chronos__ che contiene il timestamp della loro creazione;
-- __predicati delle transizioni__: funzioni che controllano i timestamp dei gettoni nel preset e i propri tempi di scatto per determinare l'abilitazione o meno;
-- __azioni delle transizioni__: generazione di gettoni dotati tutti dello _stesso dato temporale_, il quale è _non minore dei timestamp di tutti i gettoni nella tupla abilitante_.
-
-Con queste accortezze è possibile riprodurre i timestamp e le regole di scatto di una rete TB.
-Come sappiamo bene, tuttavia, questo non basta per modellare il _concetto_ di tempo: per avere un'espressività simile a quella delle reti Time Basic è infatti necessario anche il rispetto di una __semantica temporale__ e, in particolare, di quella più stringente, ovvero la semantica temporale forte (STS).
-
-Nelle reti ad Alto Livello bisognerà dunque far rispettare i cinque assiomi temporali perché la traduzione da reti TB a reti ER sia completa.
-Vediamo dunque come ciò potrebbe essere fatto:
-
-- gli assiomi __A1__ e __A3__ sono sufficientemente semplici da modellare all'interno dei predicati delle transizioni, rendendo così la __semantica temporale debole__ rappresentabile nelle reti ad Alto Livello;
-
-- l'assioma __A2__ è già più complesso da realizzare, in quanto richiede che lo scatto di una transizione generi dei gettoni con un timestamp maggiore di quello di tutti gli altri gettoni nella rete (_imponendo così che il tempo avanzi_).
-  Tuttavia tale limite può essere aggirato con l'aggiunta di un __posto globale__ contenente il __gettone dell'ultimo scatto__ e aggiunto al preset di ogni transizione: in questo modo il gettone nel posto globale rappresenterà il _tempo corrente della rete_ e imporrà che i nuovi gettoni generati abbiano timestamp maggiore di esso.
-  In questo modo una rete ER può realizzare anche la __semantica temporale monotonica debole__;
-
-- gli assiomi __A4__ e __A5__, invece, si rivelano __estremamente problematici__: essi richiedono infatti che ciascuna transizione conosca il massimo tempo di scatto di tutte le altre transizioni per "decidere" se poter scattare oppure no.
-  I predicati delle transizioni dovrebbero cioè avere in input l'intero stato della rete: sebbene questa cosa sia _teoricamente_ realizzabile con l'aggiunta di un posto globale in ingresso e uscita da ogni transizione in cui un gettone contenga come _contenuto informativo l'intero stato della rete_, nella pratica cio è __irrealizzabile__.
-
-Come si vede, dunque, la necessità di modellare la __semantica temporale forte__ fa sì che __non si possa ridurre le reti TB a un caso particolare delle reti ER__, perdendo così anche la possibilità di utilizzare le tecniche di analisi già sviluppate per esse.
-
-### Reti Time Petri ad Alto Livello (HLTPN)
-
-Appurato che non è possibile tracciare un'equivalenza tra le reti TB e le reti ER viene dunque introdotto un modello ancora più completo, che racchiuda al suo interno sia gli __aspetti funzionali__ delle reti ad Alto Livello sia gli __aspetti temporali__ delle reti Time Basic: si tratta delle __reti Time Petri ad Alto Livello__ (_High-Level Time Petri Nets_, __HLTPN__).
-
-{% responsive_image path: assets/16_HLTPNs.png %}
-
-Come appare chiaro dalla figura, all'interno delle reti HLTPN __gli aspetti funzionali possono dipendere da quelli temporali e viceversa__, espandendo così incredibilmente le capacità rappresentative del modello.
-Si tratta di reti ovviamente molto complesse, anche se a dire il vero gran parte della complessità giunge dall'analisi della componente temporale: se riusciremo ad analizzare le reti temporizzate potremo riuscire ad analizzare anche le reti HLTPN.
-
-## Analisi di raggiungibilità temporale
-
-Rassegnatici dunque alla necessità di creare nuove tecniche di analisi specifiche per le reti temporizzate iniziamo a parlare di __analisi dinamica__ a partire dall'__analisi di raggiungibilità__, ovvero la tecnica con cui nelle reti di Petri classiche eravamo in grado di __enumerare gli stati finiti raggiungibili__.
-
-Provando ad adottare lo stesso approccio nei confronti delle reti TB ci si rende però subito conto di un enorme __problema__: le reti temporizzate hanno sempre __infiniti stati__, in quanto lo scatto di una singola transizione può produrre un'infinità di stati di arrivo che si differenziano unicamente per il timestamp dei gettoni generati.
-Sebbene la marcatura sia identica, le informazioni temporali legate ai gettoni sono differenti, distinguendo così ciascuno di tali stati della rete. \\
-Bisogna inoltre considerare che per sua stessa natura il tempo avanza, rendendo così le reti temporizzate in grado di __evolvere all'infinito__: anche raggiungendo una marcatura che non abilita alcuna transizione, la rete continua ad evolvere in quanto il _tempo corrente_ continua ad avanzare.
-
-Dovendo costruire un __albero di raggiungibilità__ questo sarebbe quindi sicuramente __infinito__, anche se in un modo diverso rispetto a quanto già visto per le reti non limitate: in quel caso infatti i gettoni non erano distinguibili, cosa che ci aveva permesso di raggrupparne un numero qualsiasi sotto il simbolo $$\omega$$, mentre in questo caso le differenze nei timestamp impediscono un simile approccio.
-
-Al contrario, per ottenere per le reti TB un'analisi simile all'analisi di raggiungibilità delle reti classiche è necessario __ridefinire__ completamente il concetto di __stato raggiungibile__.
-
-### Stati simbolici
-
-Per riformulare il concetto stesso di raggiungibilità partiamo da innanzitutto da quello di __marcatura__: nelle reti temporizzate queste associavano infatti a ciascun posto un _multiset_ in cui ad ogni timestamp era associato il numero di gettoni con tale timestamp presente nel posto.
-
-Per evitare la difficoltà di distinguere tra gettoni con timestamp diversi viene introdotto nelle reti TB il concetto di __stato simbolico__, un oggetto matematico che sostituendo ai timestamp specifici degli identificatori simbolici dei gettoni permette di _rappresentare un __insieme di possibili stati__ con in comune lo stesso numero di gettoni in ciascun posto_ (esattamente come la marcatura delle reti classiche).
-
-Più formalmente, uno stato simbolico è una __tupla__ $$[\mu, C]$$, dove:
-
-- $$\mu$$ è la __marcatura simbolica__, che associa a ciascun posto un _multiset_ di __identificatori simbolici__ che rappresentano i timestamp dei gettoni in tale posto.
-  Timestamp uguali saranno rappresentati dallo stesso simbolo, anche se si trovano in posti diversi: questo permette di mantenere l'__identità__ tra timestamp;
-
-- $$C$$ è un sistema di __vincoli__ (_constraint_), ovvero equazioni e disequazioni che rappresentano le relazioni tra gli identificatori simbolici dei gettoni.
-  In questo modo è possibile mantenere le __relazioni__ tra i timestamp dei gettoni pur non rappresentando esplicitamente il loro valore.
-
-Un'esempio aiuterà a chiarire ogni dubbio.
-Immaginiamo di avere una rete TB con 3 posti $$(P1, P2, P3)$$, ciascuno con un solo gettone al loro interno, e la seguente marcatura iniziale: $$\langle \{0\}, \{1\}, \{0\} \rangle$$.
-Volendoci disfare dei timestamp espliciti dei gettoni, che tutto sommato ci interessano relativamente, dobbiamo __mantenere due informazioni__:
-
-- che i gettoni in $$P1$$ e $$P3$$ hanno lo _stesso timestamp_;
-- che il gettone in $$P2$$ ha _timestamp maggiore_ di 1 del timestamp dei gettoni negli altri due posti.
-
-Per fare ciò lo stato simbolico generato assegnerà lo stesso identificatore ai gettoni in $$P1$$ e $$P3$$ e espliciterà nei vincoli la relazione tra tempi.
-Otterremo dunque lo stato simbolico iniziale:
-
-$$\mu(P1)=\{\tau_0\}, \: \: \: \mu(P2)=\{\tau_1\}, \: \: \: \mu(P3)=\{\tau_0\}$$
-
-$$C_0: \: \: \: \tau_1=\tau_0+1$$
-
-Infine, ci si potrebbe accorgere che in realtà non ci interessa che il timestamp del gettone in $$P2$$ sia esattamente $$\tau_0+1$$, ma solamente che esso sia maggiore di $$\tau_0$$.
-Ecco dunque che spesso si mutano i vincoli in __disequazioni__:
-
-$$C_0: \: \: \: \tau_0<\tau_1$$
-
-### Albero di raggiungibilità temporale
-
-Utilizzando la definizione di stato simbolico appena vista è possibile costruire tramite un algoritmo un __albero di raggiungibilità temporale__, in cui gli stati distinguibili solo dai timestamp vengono condensati in stati simbolici che conservano però le _molteplicità_ dei gettoni nei posti e le _relazioni_ tra i timestamp.
-
-Prima di fare ciò, però, è necessario rinnovare un'assunzione già fatta in precedenza: anche in questa analisi assumeremo che le funzioni temporali $$tf_{t}$$ associate alle transizioni siano esprimibili come __intervalli con estremi inclusi__ $$\bf{[tmin_t, tmax_t]}$$, dove questi ultimi possono dipendere ovviamente dai timestamp dei token in ingresso nonché da tempi assoluti.
-
-Fatte queste premesse, possiamo partire a costruire effettivamente l'albero di raggiungibilità temporale di una rete TB secondo il seguente algoritmo:
-
-1. __Inizializzazione__: si trasforma la marcatura iniziale della rete in uno stato simbolico, introducendo una serie di vincoli che descrivano le (pre-)condizioni iniziali della rete.
-  Tale stato viene poi trasformato in un nodo, diventando la radice dell'albero, e aggiunto alla lista dei nodi da esaminare;
-
-2. __Scelta del prossimo nodo__: tra i nodi dell'albero non ancora esaminati si seleziona il prossimo nodo da ispezionare;
-
-3. __Identificazione delle abilitazioni__: in base allo stato simbolico rappresentato dal nodo si individuano le transizioni abilitate al suo interno;
-
-4. __Aggiornamento di marcatura e vincoli__: ciascuna transizione abilitata trovata viene fatta scattare generando un nuovo stato simbolico, che viene rappresentato nell'albero come nodo figlio del nodo considerato e aggiunto alla lista dei nodi da esaminare;
-
-5. __Iterazione__: si ritorna al punto 2.
-
-Di questo semplice algoritmo approfondiamo dunque le due fasi più interessanti: l'aggiornamento di marcatura e vincoli e l'identificazione delle abilitazioni.
-
-#### Aggiornamento di marcatura e vincoli
-
-Come si fa a __generare un nuovo stato simbolico__ a partire dallo stato simbolico corrente quando scatta una transizione abilitata?
-Sostanzialmente il processo si divide in due fasi: la __creazione e distruzione di gettoni__ e l'__espansione dei vincoli__.
-
-Il primo step è abbastanza semplice: è sufficiente distruggere i gettoni e i relativi simboli nel preset della transizione e generare nuovi gettoni nel suo postset, questi ultimi identificati tutti dallo __stesso nuovo identificatore simbolico__.
-
-La generazione di nuovi vincoli è invece più complessa.
-Essa deve infatti tenere in considerazione quattro diversi aspetti:
-
-- I __vecchi vincoli__ devono continuare a valere: essi esprimono infatti relazioni tra gli identificatori temporali che non possono essere alterate dallo scatto di una transizione;
-
-- Il __nuovo timestamp__ deve avere il __valore massimo__ nella rete: esso rappresenta infatti il tempo di scatto dell'ultima transizione scattata, e per monotonicità del tempo esso dovrà essere maggiore di tutti gli altri;
-
-- Il __nuovo timestamp__ dev'essere __compreso nell'intervallo__ dei possibili tempi di scatto della transizione scattata;
-
-- Il __nuovo timestamp__ dev'essere __minore del massimo tempo di scatto__ di tutte le __transizioni forti abilitate__ il cui intervallo di scatto non sia nullo: per la semantica temporale forte, infatti, se così non fosse la transizione non potrebbe scattare prima che tale transizione forte scatti (cambiando anche potenzialmente l'insieme delle transizioni abilitate).
-
-Tutte queste considerazioni devono essere condensate in un'__unica espressione logica__.
-Si dimostra quindi che _dato uno stato simbolico precedente avente vincoli $$C_n$$, detto $$maxT$$ il timestamp massimo all'interno della rete e $$\tau_{n+1}$$ l'identificatore simbolico dei gettoni generati dalla transizione $$t$$ è possibile definire i vincoli dello stato simbolico prodotto con la seguente __formula___:
-
-$$
-\boxed{
-\begin{align*}
-C_{n+1} = \: & C_n \wedge \tau_{n+1} \geq maxT \: \wedge tmin_t \leq \tau_{n+1} \leq tmax_t \\
-& \bigcap\limits_{t_{STS}}(tmax_{t_{STS}} < tmin_{t_{STS}} \vee tmax_{t_{STS}} < maxT \vee tmax_{t_{STS}} \geq \tau_{n+1} )
-\end{align*}
-}
-$$
-
-dove per $$t_{STS}$$ si intende una qualunque __transizione forte__ diversa da $$t$$; per __ciascuna__ di esse bisognerà infatti aggiungere la condizione tra parentesi, relativa appunto alla semantica STS.
-
-Tale catena di condizioni può ben presto diventare soverchiante, ma fortunatamente essa può essere semplificata sfruttando le __implicazioni__ e le proprietà degli operatori logici.
-In particolare:
-
-- se una condizione $$A$$ implica un'altra condizione $$B$$ con cui è in __AND__ ($$\wedge$$), allora la condizione __implicata__ $$B$$ può essere cancellata;
-- se una condizione $$A$$ implica un'altra condizione $$B$$ con cui è in __OR__ ($$\vee$$), allora la condizione __implicante__ $$A$$ può essere cancellata.
-
-#### Identificazione delle abilitazioni
-
-Al contrario di quanto ci si potrebbe aspettare, però, la creazione di questo nuova catena di vincoli non è relegata alla sola creazione di un nuovo stato simbolico, ma è invece necessaria anche per __identificare le transizioni abilitate__.
-Avendo infatti introdotto degli identificatori simbolici che mascherano i timestamp dei gettoni, capire se una transizione sia abilitata o meno non è più così facile.
-
-Tuttavia, è possibile dimostrare che _la __soddisfacibilità__ del vincolo generato da un eventuale scatto della transizione __implica__ la sua __abilitazione___: se esiste cioè un assegnamento di timestamp agli identificatori simbolici che __rende vero__ il vincolo allora la transizione è abilitata e può scattare.
-Il motivo di ciò appare evidente quando ci si accorge che nella generazione del vincolo abbiamo già tenuto in conto di tutti gli aspetti che avremmo osservato per stabilire se la transizione fosse abilitata o meno.
-
-{% responsive_image path: assets/16_esempio_albero_raggiungibilita_reti_temporizzate-grafico.png %}
-
-Proprio riguardo la soddisfacibilità viene poi fatta una distinzione a livello grafico nell'albero: essendo gli stati simbolici _insiemi_ di marcature è possibile che una transizione sia abilitata in alcune di esse mentre è disabilitata in altre. \\
-Quando questo succede, lo stato generato dalla transizione __potrebbe essere uno stato finale__, in quanto potrebbe aver disabilitato tutte le transizioni: ciò si comunica graficamente con un __nodo circolare__, mentre i nodi (e quindi gli stati) i cui vincoli sono __sempre soddisfacibili__ si indicano con dei __nodo rettangolari__.
-
-{% responsive_image path: assets/16_esempio_albero_raggiungibilita_reti_temporizzate.png %}
-
-Alcuni operano poi una distinzione sulle frecce che collegano i nodi dell'albero: una freccia con punta nera indica che la transizione è sempre possibile, mentre una freccia con alla base un pallino bianco indica che per rendere possibile la transizione è stato violato qualche parte del vincolo, per cui non è detto che la transizione sia possibile in nessuna delle marcature rappresentate dallo stato simbolico.
-
-#### Proprietà _bounded_
-
-Eseguendo l'algoritmo a mano per un paio di iterazioni ci si rende ben presto conto di una cosa: il processo __non termina__!
-
-Questo è dovuto al fatto che __non avendo una forma normale__ per i vincoli che permetta di confrontare tra di loro gli stati simbolici non è possibile stabilire se si sia già visitato o meno uno stato: i vincoli si allungano così sempre di più, creando sempre stati simbolici nuovi (almeno sulla carta).
-
-Si ottiene cioè un __albero infinito__.
-Nonostante ciò, tale albero è comunque particolarmente utile perché permette di __verificare proprietà entro un certo limite di tempo__: si parla per esempio di __bounded liveness__ e __bounded invariance__, delle caratteristiche molto preziose sopratutto per lo studio dei sistemi Hard Real-Time.
-
-### Dall'albero al grafo aciclico
-
-Esattamente come per le reti di Petri classiche, costruito l'albero di raggiungibilità ci piacerebbe ristrutturarlo per trasformarlo in un __grafo__ che illustri più concisamente l'evoluzione del sistema rappresentato dalla rete.
-Di certo non possiamo sperare di ottenere un _grafo ciclico_ in quanto per sua stessa natura _il tempo non può tornare indietro_, ma c'è qualche chance di ottenere un __grafo aciclico__?
-
-Abbiamo già detto che a causa di come sono costruiti i nuovi stati simbolici è impossibile riottenere più volte lo stesso esatto stato.
-Ammettendo tuttavia di __dimenticare la storia__ di come si è giunti in un certo nodo (ovvero l'insieme di transizioni che hanno portato ad esso) si potrebbe sperare di __ritrovare alcuni stati__ che pur caratterizzati da storie diverse possiedono la _stessa marcatura simbolica_ e lo _stesso insieme di vincoli sugli identificatori simbolici_ presenti al suo interno.
-
-Vediamo dunque una serie di tecniche che permettono, al costo di __perdere una serie di informazioni__, di individuare le _somiglianze_ tra diversi stati simbolici in modo da raggrupparli in una serie di "super-stati" che fungano da nodi per il grafo che intendiamo costruire.
-
-#### Semplificare i vincoli: l'algoritmo di Floyd
-
-Per dimenticare la storia di come si è giunti in un certo stato simbolico è innanzitutto necessario __semplificare i vincoli__: come abbiamo visto nella formula precedente, ogni nuovo stato simbolico ereditava infatti i vincoli del precedente, cosa che permette di distinguere marcature identiche a cui si è giunti in modo diverso.
-
-È dunque necessario __esprimere i vincoli solo in termini della marcatura corrente__: possiamo infatti considerare i vincoli sugli identificatori simbolici non più presenti nella rete come sostanzialmente inutili.
-Tuttavia, non basta cancellarli per risolvere la questione: sebbene il simbolo a cui fanno riferimento sia scomparso, essi potrebbero ancora esprimere __vincoli indiretti__ sugli identificatori ancora presenti nella marcatura, che vanno ovviamente mantenuti. \\
-Si immagini per esempio di avere i vincoli $$B - A \leq 5$$ e $$C - B \leq 6$$ e che l'identificatore $$B$$ sia ormai scomparso dalla rete.
-Sebbene si riferiscano a un simbolo ormai non più presente, tali vincoli contengono ancora delle informazioni: sommando le due disequazioni membro a membro si ottiene infatti che $$C - A \leq 11$$, un vincolo su variabili presenti che era espresso _indirettamente_.
-
-{% responsive_image path: assets/16_Floyd-grafo-cut.png %}
-
-Per rimappare in modo semplice gli effetti dei vincoli sulle variabili non più presenti nella marcatura su quelle presenti si utilizza spesso l'__algoritmo di Floyd__, che funziona come segue:
-
-1. Si riconducono tutti i vincoli alla __forma__ $$\bf{A - B \leq k}$$, dove $$A$$ e $$B$$ sono identificatori simbolici e $$k$$ è una costante numerica; per esempio:
-
-    $$A+2 \leq B \leq A+5 \: \: \: \longrightarrow \: \: \: A - B \leq -2 \: \text{ e } \: B - A \leq 5$$
-
-    $$B \leq C \leq B+6 \: \: \: \longrightarrow \: \: \: B - C \leq 0 \: \text{ e } \: C- B \leq 6$$
-
-2. Si costruisce una __matrice__ in cui ad ogni riga e colonna corrisponde un identificatore simbolico e l'intersezione tra la riga $$A$$ e la colonna $$B$$ corrisponde al __valore $$\bf{k}$$ tale per cui__ $$\bf{A - B \leq k}$$ in base ai vincoli ricavati al punto precedente, mentre per i valori non noti si scrive semplicemente un __punto di domanda__;
-
-3. Si __riempiono tutti i posti contrassegnati da punti di domanda__ utilizzando la seguente formula:  
-
-    $$\boxed{m[i,j] = m[i,k] + m[k,j]}$$
-
-    che mima la somma membro a membro delle disequazioni che rappresentano i vincoli.
-    In questo modo è possibile scoprire i __vincoli indiretti__ tra variabili;
-
-4. Si __esplicitano i vincoli indiretti__ relativi agli identificatori simbolici presenti nella marcatura corrente e __si eliminano__ i vincoli che contengono gli identificatori non inclusi.
-
-{% responsive_image path: assets/16_Floyd-matrici.png %}
-
-Applicato l'algoritmo di Floyd, ottenuti i vincoli impliciti ed eliminati i vincoli contenenti identificatori simbolici è possibile semplificare di molto l'insieme dei vincoli di nodi del grafo, identificando anche le prime _somiglianze_ tra nodi.
-
-#### Inclusione tra stati
-
-Il raggruppamento offerto dalla semplificazione dei vincoli tramite l'algoritmo di Floyd non è però sufficiente a ottenere grafi aciclici soddisfacenti.
-Si consideri per esempio la rete in figura:
-
-{% responsive_image path: assets/16_esempio-inclusione.png %}
-
-Come si vede, essa genera una serie di nodi nel grafo di raggiungibilità tutti diversi nonostante essi abbiano la __stessa marcatura__ e l'unica differenza sia data dalla costante nel vincolo.
-Per semplificare situazioni come queste viene introdotto il concetto di __inclusione__ (o _contenimento_) __tra stati__: sapendo infatti che gli stati simbolici rappresentano _insiemi_ di marcature è opportuno chiedersi se alcuni di essi possano essere _sottoinsiemi_ di altri.
-
-Ecco dunque che si dice che _uno stato $$A$$ è __contenuto__ in un altro stato $$B$$ se e solo se __tutte__ le marcature rappresentate da $$A$$ sono rappresentate anche da $$B$$_.
-Ciò avviene quando:
-
-- $$A$$ e $$B$$ hanno lo __stesso assegnamento di timestamp__;
-- __i vincoli di $$A$$ implicano i vincoli di $$B$$__, ovvero $$C_A \rightarrow C_B$$.
-
-Decidiamo quindi di rappresentare nel grafo __solo gli stati contenuti__, introducendo però una distinzione grafica: la punta bianca della freccia indica che spostandosi lungo di essa si arriva ad un __sottoinsieme__ del "super-stato" di arrivo, ovvero uno stato avente _vincoli più stringenti_ di quelli mostrati.
-
-{% responsive_image path: assets/16_esempio-inclusione-2.png %}
-
-#### Tempi relativi
-
-Osservando l'evoluzione di una rete Time Basic ci si può poi accorgere di un ulteriore fatto: se le funzioni temporali delle transizioni __non fanno riferimento a tempi assoluti__, ovvero a specifici istanti di esecuzione della rete, per comprendere come la rete può evolvere a partire da una certa marcatura è __sufficiente osservare i vincoli relativi tra i timestamp__.
-
-Si prenda per esempio in considerazione la rete in figura:
-
-{% responsive_image path: assets/16_esempio-tempi-relativi-crop.png %}
-
-ci si accorge che mantenere il riferimento ai tempi assoluti $$0$$ e $$10$$ introdotti dai vincoli iniziali farebbe sì che vengano generati __infiniti stati__ anche considerando la possibilità di inclusione.
-Poiché _l'unica transizione presente nella rete non fa alcun riferimento a tempi assoluti_, si può quindi __eliminare i vincoli legati a tempi assoluti__ ottenendo il secondo grafo in figura: esso rappresenta alla perfezione l'evoluzione della rete (che può far scattare la transizione $$T1$$ un numero infinito di volte) pur ignorando i vincoli sul valore iniziale del timestamp dell'unico gettone presente.
-
-#### Tempi anonimi
-
-Si può infine introdurre un'ulteriore astrazione: ci si rende infatti conto che _se il timestamp associato ad un gettone in una marcatura M non verrà __mai più utilizzato__ per stabilire come evolverà la rete a partire da quella marcatura_, allora è possibile __anonimizzare__ il tempo di tale gettone.
-L'identificatore simbolico del gettone viene cioè sostituito da un __identificatore anonimo__ $$\tau_A$$ e i vincoli che lo coinvolgono cancellati: questo permette di riconoscere la somiglianza tra stati simbolici che, pur diversi a livello di timestamp dei gettoni, __evolvono nello stesso modo__.
-
-Si consideri per esempio la rete in figura:
-
-{% responsive_image path: assets/16_esempio-time-anonymous-crop.png %}
-
-All'interno di questa rete, il timestamp del gettone in $$P2$$ non ha alcuna influenza sull'evoluzione della rete: esso funge infatti unicamente da _zero relativo_ per determinare il timestamp del gettone in $$P1$$, che sarà maggiore di esso di tante unità quanto il numero di volte che è scattata la transizione $$T1$$.
-Il gettone può dunque essere reso anonimo, eliminando l'unico vincolo che, a conti fatti, non aggiunge nulla alla nostra conoscenza della rete.
-
-Non esiste una vera e propria regola formale per capire quali gettoni siano anonimizzabili, ma esistono una serie di __euristiche__ che possono suggerire tale eventualità: così, per esempio, è molto probabile che i __gettoni morti__ (gettoni in posti che non appartengono al preset di alcuna transizione) possano essere resi anonimi.
-
-#### Conclusioni
-
-L'utilizzo delle tecniche di __raggruppamento degli stati simbolici__ appena viste permette di costruire dei grafi di raggiungibilità più coincisi per le reti Time Basic, ma non senza __sacrificare__ una serie di __informazioni__.
-Infatti:
-
-- la tecnica di __inclusione__ introduce la possibilità che nel grafo esistano dei __cammini non percorribili__ dovuti al fatto che muovendosi tra _sottoinsiemi_ degli stati rappresentati è possibile che lo stato simbolico reale in cui ci si trova non permetta una certa transizione che è invece possibile a parte degli stati rappresentati dal nodo;
-- con l'abolizione dei __vincoli assoluti__ si perdono informazioni sulle __relazioni precise__ tra gli stati (anche se è possibile arricchire le informazioni sugli archi per non perderne troppe);
-- __anonimizzando__ alcuni gettoni potrebbe non essere sempre possibile verificare la __raggiungibilità__ di una marcatura definita da vincoli sui timestamp.
-
-Si tratta di un equo prezzo da pagare per una rappresentazione semplice ed efficace dell'evoluzione della rete.
-
-### Albero di copertura temporale?
-
-Avevamo detto che sulle reti TB non era possibile utilizzare la tecnica di analisi di copertura vista per le normali reti di Petri a causa della __distinguibilità__ tra gettoni dovuta ai rispettivi timestamp.
-
-Tuttavia, l'introduzione della possibilità di __anonimizzare i gettoni__ è in grado di far riconsiderare tale conclusione: i gettoni anonimi sono infatti tutti __equivalenti__ e indistinguibili, motivo per cui potrebbero essere rappresentati globalmente solo dal loro __numero__ $$\omega_{\tau A}$$.
-
-Non approfondiamo la questione, ma esiste un'ipotesi non dimostrata che suppone che _le reti TB non limitate siano non limitate __solo__ sul numero di gettoni anonimi_ in quanto in caso contrario bisognerebbe avere una rete che si interessi del passato all'infinito.
diff --git a/_sass/_base.scss b/_sass/_base.scss
deleted file mode 100644
index 0883c3cdb5d1a672ff356b05ade8b0e8d26abdaf..0000000000000000000000000000000000000000
--- a/_sass/_base.scss
+++ /dev/null
@@ -1,206 +0,0 @@
-/**
- * Reset some basic elements
- */
-body, h1, h2, h3, h4, h5, h6,
-p, blockquote, pre, hr,
-dl, dd, ol, ul, figure {
-    margin: 0;
-    padding: 0;
-}
-
-
-
-/**
- * Basic styling
- */
-body {
-    font: $base-font-weight #{$base-font-size}/#{$base-line-height} $base-font-family;
-    color: $text-color;
-    background-color: $background-color;
-    -webkit-text-size-adjust: 100%;
-    -webkit-font-feature-settings: "kern" 1;
-    -moz-font-feature-settings: "kern" 1;
-    -o-font-feature-settings: "kern" 1;
-    font-feature-settings: "kern" 1;
-    font-kerning: normal;
-}
-
-
-
-/**
- * Set `margin-bottom` to maintain vertical rhythm
- */
-h1, h2, h3, h4, h5, h6,
-p, blockquote, pre,
-ul, ol, dl, figure,
-%vertical-rhythm {
-    margin-bottom: $spacing-unit / 2;
-}
-
-
-
-/**
- * Images
- */
-img {
-    max-width: 100%;
-    vertical-align: middle;
-}
-
-
-
-/**
- * Figures
- */
-figure > img {
-    display: block;
-}
-
-figcaption {
-    font-size: $small-font-size;
-}
-
-
-
-/**
- * Lists
- */
-ul, ol {
-    margin-left: $spacing-unit;
-}
-
-li {
-    > ul,
-    > ol {
-         margin-bottom: 0;
-    }
-}
-
-
-
-/**
- * Headings
- */
-h1, h2, h3, h4, h5, h6 {
-    font-weight: $base-font-weight;
-}
-
-
-
-/**
- * Links
- */
-a {
-    color: $brand-color;
-    text-decoration: none;
-
-    &:visited {
-        color: darken($brand-color, 15%);
-    }
-
-    &:hover {
-        color: $text-color;
-        text-decoration: underline;
-    }
-}
-
-
-
-/**
- * Blockquotes
- */
-blockquote {
-    color: $grey-color;
-    border-left: 4px solid $grey-color-light;
-    padding-left: $spacing-unit / 2;
-    font-size: 18px;
-    letter-spacing: -1px;
-    font-style: italic;
-
-    > :last-child {
-        margin-bottom: 0;
-    }
-}
-
-
-
-/**
- * Code formatting
- */
-pre,
-code {
-    font-size: 15px;
-    border: 1px solid $grey-color-light;
-    border-radius: 3px;
-    background-color: #eef;
-}
-
-code {
-    padding: 1px 5px;
-}
-
-pre {
-    padding: 8px 12px;
-    overflow-x: auto;
-
-    > code {
-        border: 0;
-        padding-right: 0;
-        padding-left: 0;
-    }
-}
-
-
-
-/**
- * Wrapper
- */
-.wrapper {
-    max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit} * 2));
-    max-width:         calc(#{$content-width} - (#{$spacing-unit} * 2));
-    margin-right: auto;
-    margin-left: auto;
-    padding-right: $spacing-unit;
-    padding-left: $spacing-unit;
-    @extend %clearfix;
-
-    @include media-query($on-laptop) {
-        max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit}));
-        max-width:         calc(#{$content-width} - (#{$spacing-unit}));
-        padding-right: $spacing-unit / 2;
-        padding-left: $spacing-unit / 2;
-    }
-}
-
-
-
-/**
- * Clearfix
- */
-%clearfix {
-
-    &:after {
-        content: "";
-        display: table;
-        clear: both;
-    }
-}
-
-
-
-/**
- * Icons
- */
-.icon {
-
-    > svg {
-        display: inline-block;
-        width: 16px;
-        height: 16px;
-        vertical-align: middle;
-
-        path {
-            fill: $grey-color;
-        }
-    }
-}
diff --git a/_sass/_layout.scss b/_sass/_layout.scss
deleted file mode 100644
index a43cf743fe986c6d6bf5c83f3eed093d4b84fe67..0000000000000000000000000000000000000000
--- a/_sass/_layout.scss
+++ /dev/null
@@ -1,286 +0,0 @@
-/**
- * Site header
- */
-.site-header {
-    border-top: 5px solid $grey-color-dark;
-    border-bottom: 1px solid $grey-color-light;
-    min-height: 56px;
-
-    // Positioning context for the mobile navigation icon
-    position: relative;
-}
-
-.site-title {
-    font-size: 26px;
-    font-weight: 300;
-    line-height: 56px;
-    letter-spacing: -1px;
-    margin-bottom: 0;
-    float: left;
-
-    &,
-    &:visited {
-        color: $grey-color-dark;
-    }
-}
-
-.site-nav {
-    float: right;
-    line-height: 56px;
-
-    .menu-icon {
-        display: none;
-    }
-
-    .page-link {
-        color: $text-color;
-        line-height: $base-line-height;
-
-        // Gaps between nav items, but not on the last one
-        &:not(:last-child) {
-            margin-right: 20px;
-        }
-    }
-
-    @include media-query($on-palm) {
-        position: absolute;
-        top: 9px;
-        right: $spacing-unit / 2;
-        background-color: $background-color;
-        border: 1px solid $grey-color-light;
-        border-radius: 5px;
-        text-align: right;
-
-        .menu-icon {
-            display: block;
-            float: right;
-            width: 36px;
-            height: 26px;
-            line-height: 0;
-            padding-top: 10px;
-            text-align: center;
-
-            > svg {
-                width: 18px;
-                height: 15px;
-
-                path {
-                    fill: $grey-color-dark;
-                }
-            }
-        }
-
-        .trigger {
-            clear: both;
-            display: none;
-        }
-
-        &:hover .trigger {
-            display: block;
-            padding-bottom: 5px;
-        }
-
-        .page-link {
-            display: block;
-            padding: 5px 10px;
-
-            &:not(:last-child) {
-                margin-right: 0;
-            }
-            margin-left: 20px;
-        }
-    }
-}
-
-
-
-/**
- * Site footer
- */
-.site-footer {
-    border-top: 1px solid $grey-color-light;
-    padding: $spacing-unit 0;
-}
-
-.footer-heading {
-    font-size: 18px;
-    margin-bottom: $spacing-unit / 2;
-}
-
-.contact-list,
-.social-media-list {
-    list-style: none;
-    margin-left: 0;
-}
-
-.footer-col-wrapper {
-    font-size: 15px;
-    color: $grey-color;
-    margin-left: -$spacing-unit / 2;
-    @extend %clearfix;
-}
-
-.footer-col {
-    float: left;
-    margin-bottom: $spacing-unit / 2;
-    padding-left: $spacing-unit / 2;
-}
-
-.footer-col-1 {
-    width: -webkit-calc(35% - (#{$spacing-unit} / 2));
-    width:         calc(35% - (#{$spacing-unit} / 2));
-}
-
-.footer-col-2 {
-    width: -webkit-calc(20% - (#{$spacing-unit} / 2));
-    width:         calc(20% - (#{$spacing-unit} / 2));
-}
-
-.footer-col-3 {
-    width: -webkit-calc(45% - (#{$spacing-unit} / 2));
-    width:         calc(45% - (#{$spacing-unit} / 2));
-}
-
-@include media-query($on-laptop) {
-    .footer-col-1,
-    .footer-col-2 {
-        width: -webkit-calc(50% - (#{$spacing-unit} / 2));
-        width:         calc(50% - (#{$spacing-unit} / 2));
-    }
-
-    .footer-col-3 {
-        width: -webkit-calc(100% - (#{$spacing-unit} / 2));
-        width:         calc(100% - (#{$spacing-unit} / 2));
-    }
-}
-
-@include media-query($on-palm) {
-    .footer-col {
-        float: none;
-        width: -webkit-calc(100% - (#{$spacing-unit} / 2));
-        width:         calc(100% - (#{$spacing-unit} / 2));
-    }
-}
-
-
-
-/**
- * Page content
- */
-.page-content {
-    padding: $spacing-unit 0;
-}
-
-.page-heading {
-    font-size: 20px;
-}
-
-.post-list {
-    margin-left: 0;
-    list-style: none;
-
-    > li {
-        margin-bottom: $spacing-unit;
-    }
-}
-
-.post-meta {
-    font-size: $small-font-size;
-    color: $grey-color;
-}
-
-.post-link {
-    display: block;
-    font-size: 24px;
-}
-
-
-
-/**
- * Posts
- */
-.post-header {
-    margin-bottom: $spacing-unit;
-}
-
-.post-title {
-    font-size: 42px;
-    letter-spacing: -1px;
-    line-height: 1;
-
-    @include media-query($on-laptop) {
-        font-size: 36px;
-    }
-}
-
-.post-content {
-    margin-bottom: $spacing-unit;
-
-    h1 {
-        font-size: 38px;
-
-        @include media-query($on-laptop) {
-            font-size: 34px;
-        }
-    }
-
-    h2 {
-        font-size: 32px;
-
-        @include media-query($on-laptop) {
-            font-size: 28px;
-        }
-    }
-
-    h3 {
-        font-size: 26px;
-
-        @include media-query($on-laptop) {
-            font-size: 22px;
-        }
-    }
-
-    h4 {
-        font-size: 20px;
-
-        @include media-query($on-laptop) {
-            font-size: 18px;
-        }
-    }
-
-    /* Markdown definitions */
-    dt {
-        font-weight: bold;
-        display: block;
-    }
-    dl {
-        font-size: .9rem;
-    }
-    dd {
-        font-size: .9rem;
-        margin-left: 2em;
-    }
-
-    /* Table stuff */
-    th, td {
-        padding: 4px;
-        border: 1px solid;
-    }
-    th {
-        text-align: center;
-    }
-    table {
-        border-collapse: collapse;
-        border: 1px solid;
-        width: 100%;
-    }
-
-    /* PlantUML horizontal alignment */
-    .plantuml-parent {
-        text-align: center;
-    }
-
-    .plantuml {
-        width: 40%;
-    }
-}
diff --git a/_sass/_syntax-highlighting.scss b/_sass/_syntax-highlighting.scss
deleted file mode 100644
index 8fac59776d54d4b29faccf049f4cf50b85b4e2c3..0000000000000000000000000000000000000000
--- a/_sass/_syntax-highlighting.scss
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * Syntax highlighting styles
- */
-.highlight {
-    background: #fff;
-    @extend %vertical-rhythm;
-
-    .highlighter-rouge & {
-      background: #eef;
-    }
-
-    .c     { color: #998; font-style: italic } // Comment
-    .err   { color: #a61717; background-color: #e3d2d2 } // Error
-    .k     { font-weight: bold } // Keyword
-    .o     { font-weight: bold } // Operator
-    .cm    { color: #998; font-style: italic } // Comment.Multiline
-    .cp    { color: #999; font-weight: bold } // Comment.Preproc
-    .c1    { color: #998; font-style: italic } // Comment.Single
-    .cs    { color: #999; font-weight: bold; font-style: italic } // Comment.Special
-    .gd    { color: #000; background-color: #fdd } // Generic.Deleted
-    .gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific
-    .ge    { font-style: italic } // Generic.Emph
-    .gr    { color: #a00 } // Generic.Error
-    .gh    { color: #999 } // Generic.Heading
-    .gi    { color: #000; background-color: #dfd } // Generic.Inserted
-    .gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific
-    .go    { color: #888 } // Generic.Output
-    .gp    { color: #555 } // Generic.Prompt
-    .gs    { font-weight: bold } // Generic.Strong
-    .gu    { color: #aaa } // Generic.Subheading
-    .gt    { color: #a00 } // Generic.Traceback
-    .kc    { font-weight: bold } // Keyword.Constant
-    .kd    { font-weight: bold } // Keyword.Declaration
-    .kp    { font-weight: bold } // Keyword.Pseudo
-    .kr    { font-weight: bold } // Keyword.Reserved
-    .kt    { color: #458; font-weight: bold } // Keyword.Type
-    .m     { color: #099 } // Literal.Number
-    .s     { color: #d14 } // Literal.String
-    .na    { color: #008080 } // Name.Attribute
-    .nb    { color: #0086B3 } // Name.Builtin
-    .nc    { color: #458; font-weight: bold } // Name.Class
-    .no    { color: #008080 } // Name.Constant
-    .ni    { color: #800080 } // Name.Entity
-    .ne    { color: #900; font-weight: bold } // Name.Exception
-    .nf    { color: #900; font-weight: bold } // Name.Function
-    .nn    { color: #555 } // Name.Namespace
-    .nt    { color: #000080 } // Name.Tag
-    .nv    { color: #008080 } // Name.Variable
-    .ow    { font-weight: bold } // Operator.Word
-    .w     { color: #bbb } // Text.Whitespace
-    .mf    { color: #099 } // Literal.Number.Float
-    .mh    { color: #099 } // Literal.Number.Hex
-    .mi    { color: #099 } // Literal.Number.Integer
-    .mo    { color: #099 } // Literal.Number.Oct
-    .sb    { color: #d14 } // Literal.String.Backtick
-    .sc    { color: #d14 } // Literal.String.Char
-    .sd    { color: #d14 } // Literal.String.Doc
-    .s2    { color: #d14 } // Literal.String.Double
-    .se    { color: #d14 } // Literal.String.Escape
-    .sh    { color: #d14 } // Literal.String.Heredoc
-    .si    { color: #d14 } // Literal.String.Interpol
-    .sx    { color: #d14 } // Literal.String.Other
-    .sr    { color: #009926 } // Literal.String.Regex
-    .s1    { color: #d14 } // Literal.String.Single
-    .ss    { color: #990073 } // Literal.String.Symbol
-    .bp    { color: #999 } // Name.Builtin.Pseudo
-    .vc    { color: #008080 } // Name.Variable.Class
-    .vg    { color: #008080 } // Name.Variable.Global
-    .vi    { color: #008080 } // Name.Variable.Instance
-    .il    { color: #099 } // Literal.Number.Integer.Long
-}
diff --git a/assets/custom.css b/assets/custom.css
new file mode 100644
index 0000000000000000000000000000000000000000..6aebbe896f2af3e4f44b8896d0150c73dbd34d06
--- /dev/null
+++ b/assets/custom.css
@@ -0,0 +1,6 @@
+/* Center images */
+img {
+    display: block;
+    margin-left: auto;
+    margin-right: auto;
+}
\ No newline at end of file
diff --git a/book.toml b/book.toml
new file mode 100644
index 0000000000000000000000000000000000000000..7ea1b69e65f5f5246d0d2be643ed26edf3ad768e
--- /dev/null
+++ b/book.toml
@@ -0,0 +1,25 @@
+[book]
+authors = ["Marco Aceti", "Matteo Mangioni", "Daniele Ceribelli", "Matteo Yon", "Francesco Protospataro", "Ilyaas Ouardi", "Armani Islam", "Mattia Mendecino", "Andrea Cambiaghi"]
+language = "it"
+multilingual = false
+src = "src"
+title = "Ingegneria del software"
+
+[preprocessor.plantuml]
+plantuml-cmd = "plantuml"
+clickable-img = true
+
+[output.html]
+mathjax-support = true
+additional-css = ["assets/custom.css"]
+curly-quotes = true
+git-repository-url = "https://gitlab.di.unimi.it/silab-gang/sweng"
+git-repository-icon = "fa-gitlab"
+edit-url-template = "https://gitlab.di.unimi.it/silab-gang/sweng/edit/master/guide/{path}"
+
+[output.html.print]
+enable = true
+page-break = false
+
+[output.html.fold]
+enable = true
diff --git a/css/main.scss b/css/main.scss
deleted file mode 100644
index 98d0d125f60c8a36b72c61462550df5b7494748a..0000000000000000000000000000000000000000
--- a/css/main.scss
+++ /dev/null
@@ -1,55 +0,0 @@
----
-# Only the main Sass file needs front matter (the dashes are enough)
----
-@charset "utf-8";
-
-
-
-// Our variables
-$base-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
-$base-font-size:   16px;
-$base-font-weight: 400;
-$small-font-size:  $base-font-size * 0.875;
-$base-line-height: 1.5;
-
-$spacing-unit:     30px;
-
-$text-color:       #111;
-$background-color: #fdfdfd;
-$brand-color:      #2a7ae2;
-
-$grey-color:       #828282;
-$grey-color-light: lighten($grey-color, 40%);
-$grey-color-dark:  darken($grey-color, 25%);
-
-// Width of the content area
-$content-width:    800px;
-
-$on-palm:          600px;
-$on-laptop:        800px;
-
-
-
-// Use media queries like this:
-// @include media-query($on-palm) {
-//     .wrapper {
-//         padding-right: $spacing-unit / 2;
-//         padding-left: $spacing-unit / 2;
-//     }
-// }
-@mixin media-query($device) {
-    @media screen and (max-width: $device) {
-        @content;
-    }
-}
-
-table {
-    margin-bottom: 15px;
-}
-
-// Import partials from `sass_dir` (defaults to `_sass`)
-@import
-        "base",
-        "layout",
-        "syntax-highlighting"
-;
diff --git a/docker-run.sh b/docker-run.sh
index de19ef95a1e388b4caf0f916847d7ff2814a7591..a4956be74dc1dddd2f5df9de6d5adcf3d85f9b09 100755
--- a/docker-run.sh
+++ b/docker-run.sh
@@ -5,8 +5,7 @@ docker run \
     --interactive \
     --tty \
     --mount type=bind,source="$(pwd)",target=/usr/src/app \
-    --publish 4000:4000 \
-    --publish 35729:35729 \
+    --publish 3000:3000 \
     --user $(id -u):$(id -g) \
     appunti_sweng \
-    --livereload 
+    serve --hostname 0.0.0.0 --port 3000
diff --git a/feed.xml b/feed.xml
deleted file mode 100644
index a6628bd842af95a7f423155dd95510941d3a78dc..0000000000000000000000000000000000000000
--- a/feed.xml
+++ /dev/null
@@ -1,30 +0,0 @@
----
-layout: null
----
-<?xml version="1.0" encoding="UTF-8"?>
-<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
-  <channel>
-    <title>{{ site.title | xml_escape }}</title>
-    <description>{{ site.description | xml_escape }}</description>
-    <link>{{ site.url }}{{ site.baseurl }}/</link>
-    <atom:link href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}" rel="self" type="application/rss+xml"/>
-    <pubDate>{{ site.time | date_to_rfc822 }}</pubDate>
-    <lastBuildDate>{{ site.time | date_to_rfc822 }}</lastBuildDate>
-    <generator>Jekyll v{{ jekyll.version }}</generator>
-    {% for post in site.posts limit:10 %}
-      <item>
-        <title>{{ post.title | xml_escape }}</title>
-        <description>{{ post.content | xml_escape }}</description>
-        <pubDate>{{ post.date | date_to_rfc822 }}</pubDate>
-        <link>{{ post.url | prepend: site.baseurl | prepend: site.url }}</link>
-        <guid isPermaLink="true">{{ post.url | prepend: site.baseurl | prepend: site.url }}</guid>
-        {% for tag in post.tags %}
-        <category>{{ tag | xml_escape }}</category>
-        {% endfor %}
-        {% for cat in post.categories %}
-        <category>{{ cat | xml_escape }}</category>
-        {% endfor %}
-      </item>
-    {% endfor %}
-  </channel>
-</rss>
diff --git a/index.html b/index.html
deleted file mode 100644
index 43d686559f065e879beaef2dac611b34af68b6da..0000000000000000000000000000000000000000
--- a/index.html
+++ /dev/null
@@ -1,23 +0,0 @@
----
-layout: default
----
-
-<div class="home">
-
-  <h1 class="page-heading">Appunti</h1>
-
-  <ul class="post-list">
-    {% for post in site.posts %}
-      <li>
-        <span class="post-meta">{{ post.date | date: "%-d %b %Y" }}</span>
-
-        <h2>
-          <a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
-        </h2>
-      </li>
-    {% endfor %}
-  </ul>
-
-  <p class="rss-subscribe">subscribe <a href="{{ "/feed.xml" | prepend: site.baseurl }}">via RSS</a></p>
-
-</div>
diff --git a/src/00_informazioni/00_index.md b/src/00_informazioni/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..a98fd81b234e0239809d13ef32026853eb2e8190
--- /dev/null
+++ b/src/00_informazioni/00_index.md
@@ -0,0 +1,25 @@
+# Appunti di Ingegneria del Software
+
+Questa repository è relativa ai contenuti del corso di Ingegneria del Software dei prof. Carlo Bellettini e Mattia Monga, per l'anno accademico 2022/23.
+
+Gli appunti sono presi in collaborazione secondo i principi dello sviluppo di software _open source_.
+Contribuire non è solo un modo per studiare, ma anche per utilizzare Git e alcuni concetti dell'Ingegneria del Software in un contesto reale.
+
+I _maintainer_ ufficiali sono stati:
+- Marco Aceti;
+- Daniele Ceribelli;
+- Matteo Mangioni.
+
+ma _chiunque_ può contribuire e proporre _Merge Request_. 
+
+Nel file [Autori](./01_contributori.md) sono presenti tutti i contributori per ogni lezione.
+
+Se utilizzi questi appunti per studiare, non solo è _galante_ contribuire ma è anche un modo per controllare e sistemare l'enormità di errori che prevediamo saranno presenti e per espandere o integrare nozioni e concetti.
+
+I docenti del corso sono a conoscenza di questo progetto e sembrano apprezzarlo.
+
+Tutti i contenuti sono rilasciati sotto licenza [Creative Commons BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/), consulta il file [LICENSE](https://github.com/MarcoBuster/sweng/tree/master/LICENSE) per ulteriori dettagli.
+
+La repository del codice è pubblicata sui seguenti _remoti_:
+- [**GitLab Dipartimento di Informatica**](https://gitlab.di.unimi.it/silab-gang/sweng.git);
+- [**GitHub di Marco Aceti**](https://github.com/MarcoBuster/sweng.git).
diff --git a/src/00_informazioni/01_contribuire.md b/src/00_informazioni/01_contribuire.md
new file mode 100644
index 0000000000000000000000000000000000000000..51e93c6013753a15bc227516679765d6e74b606b
--- /dev/null
+++ b/src/00_informazioni/01_contribuire.md
@@ -0,0 +1,151 @@
+# Come contribuire
+
+## Stack tecnologico
+
+Tutti i contenuti sono scritti in Markdown e quindi convertiti in HTML automaticamente da [mdbook](https://rust-lang.github.io/mdBook/). 
+Per strutture complesse, è possibile embeddare dell'HTML (e del CSS) nel file Markdown.
+
+> ### CONSIGLIATO: installazione di tutte le dipendenze tramite Docker
+> Prima di iniziare, è necessario avere Docker installato. Quindi:
+> 1. entra nel branch `master` e sincronizzalo con l'ultima versione remota:
+>
+>       ```
+>       $ git switch master
+>       $ git pull
+>       ```
+> 
+> 2. costruisci l'immagine Docker: 
+> 
+>       ```
+>       $ docker build -t appunti_sweng .
+>       ```
+> 3. crea un container ed eseguilo, ricondandoti di:
+>       - mappare le porte 3000/tcp sul tuo host;
+>       - mappare la cartella del progetto a `/usr/src/app` nel container;
+>       - mappare correttamente l'utente;
+>
+>       In ambiente UNIX, ho creato uno script che permette di fare tutte le cose di cui sopra con un comando. Per eseguirlo fare:
+>
+>       ```
+>       $ ./docker-run.sh
+>       ```
+
+Aprendo la pagina https://localhost:3000/ nel nostro browser potremo visualizzare un'anteprima della pagina HTML compilata, aggiornata ad ogni modifica del file Markdown originale.
+È estremamente consigliato arrivare a questo punto prima di continuare: non inviare patch prima di aver verificato che mdbook compili il file in una pagina sensata.
+
+Oltre a mdbook, è naturalmente necessario avere Git installato sulla propria macchina.
+Come editor di testo, consigliamo VSCode (meglio ancora [VSCodium](https://vscodium.com/)) ma qualsiasi va bene.
+
+## Regole base di Git
+
+- Utilizziamo GitLab e non GitHub perché abbiamo iniziato con GitLab e non abbiamo motivo per cambiare.
+- Abilita l'[autenticazione a due fattori](https://docs.gitlab.com/ee/user/profile/account/two_factor_authentication.html). 
+- Consigliamo l'utilizzo dell'[autenticazione SSH](https://docs.gitlab.com/ee/user/ssh.html) con GitLab. 
+- Imposta il tuo nome e cognome reale; per esempio: `git config --global user.name "Carlo Bellettini"`;
+- Utilizza la tua email universitaria, se vuoi; per esempio: `git config --global user.email "carlo.bellettini@unimi.it"`. Ricordati di aggiungere l'email al tuo account GitLab.
+- Consigliamo di impostare e attivare la [firma dei commit](https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/) tramite GPG.
+
+## The _"Silab Gang" Notes Engineering Development Process_
+Per ogni __argomento__ (spesso corrispondente a una _lezione_) N...
+![](https://i.imgur.com/tJhGSaN.png)
+
+Ad ogni argomento corrisponde una directory sotto la directory di mdbook `src/`. 
+
+### Organizzazione dei branch
+
+La gestione dei branch è simile a GitFlow, ma non è uguale.
+Osserviamo le tipologie di branch:
+- `master`: contiene l'ultima versione stabile di tutti gli appunti. Ad ogni commit viene azionata una GitLab CI che aggiorna la pagina su GitLab Pages. Solo i maintainer possono mergiare su questo branch. 
+- `lezioni/N` (ma forse avremmo dovuto chiamarli `argomenti/N`): contiene l'ultima versione _instabile_ di uno specifico argomento. 
+I contributori della lezione `N` pulleranno dal branch `lezioni/N` per sincronizzare i contributi degli altri nel proprio branch, e mergieranno (o chiederanno di mergiare) i propri contributi nel branch `lezioni/N`. Nel nome c'è un _leading zero_: `lezioni/04`, `lezioni/12`.
+- __branch utente__: iniziano con il nome dell'utente (lower case e breve) e sono utilizzati come _"sandbox"_ personale. 
+
+Tutti i branch devono essere creati partendo da `master`.
+È consentito e apprezzato il fast forward in caso di merge banali. 
+
+Essendo i branch `lezioni/N` condivisi, è __NECESSARIO__ aggiornare il branch con il remoto facendo `git pull`. Prima di mergiare in `lezioni/N`, quindi:
+1. entra nel branch `lezioni/N`: `git switch lezioni/N`;
+2. scarica le ultime modifiche: `git pull`;
+3. entra nel tuo branch: `git switch mio/branch`;
+4. mergia il branch `lezioni/N`: `git merge lezioni/N`;
+5. risolvi gli eventuali conflitti;
+6. entra nel branch `lezioni/N`;
+7. mergia il tuo branch: `git merge lezioni/N`;
+8. carica le tue modifiche: `git push lezioni/N`.
+
+> `lezioni/N` contiene sempre l'___ultima versione instabile___ e tutti i contributori la utilizzano come riferimento per quell'argomento.
+> I contributi non mergiati in `lezioni/N` non saranno considerati da nessuno e sono quindi inutili.
+> In ogni caso, non si committa mai direttamente a `lezioni/N` ma prima si passa sempre per un _branch utente_.
+
+### Esempio
+Lo studente Carlo Bellettini prende appunti durante la (sua?) lezione 5 e crea un branch `carlo/05-appunti`. Anche lo studente Mattia Monga prende appunti e pubblica le modifiche su `mattia/05-appunti`. 
+Carlo, da bravo contributore, si impegna a integrare gli appunti; crea il branch `carlo/05-integrazione` e mergia inanzitutto i suoi appunti (`carlo/05-appunti`) quindi quelli di Mattia (`mattia/05-appunti`). 
+
+Il secondo merge da parte di Carlo degli appunti di Mattia causerà sicuramente dei conflitti, che Carlo dovrà risolvere: non è codice, è testo, e due studenti prenderanno gli appunti in modo completamente diverso!
+Il concetto stesso di _integrazione_ è proprio questo. 
+
+Una volta terminato il lavoro, Carlo mergierà il suo branch `carlo/05-integrazione` in `lezioni/05`, quindi aprirà una Merge Request da `lezioni/05` verso il branch `master`.
+
+Inizia il processo di _review_: altri contributori (ovvero tutti a parte Carlo) controlleranno la correttezza e la __completezza__ (!) degli appunti proposti. 
+Se (ancora, per esempio) Marco trova dei problemi, può creare un proprio branch `marco/05-review` partendo dal branch `lezioni/05`, committare le proprie proposte e quindi rimegiarle in `lezioni/05`.
+
+Infine, una volta che tutti i reviewer sono contenti, la Merge Request viene mergiata in master e gli appunti vengono aggiunti in GitLab Pages.
+
+## Issues e Merge Requests
+
+Per coordinare il lavoro tra di noi, utilizziamo principalmente la funzione "Issue" di GitLab. 
+Tutte le issues sono elencate [qui](https://gitlab.com/silab-gang/sweng/-/issues).
+
+C'è una issue per ogni argomento. Ogni issue...
+- ha un titolo con il numero (corrispondente all'`N` nei nomi di branch) e al nome dell'argomento;
+- ha una descrizione, contentente i riferimenti alle lezioni relative all'argomento (come la data) e altre note opportune (_"il prof. ha spiegato il pattern Observer in questa lezione"_, ...);
+- ha un label per tracciare lo stato nel processo (Da Fare / In esecuzione / In attesa di review / Fatto); 
+- ha un epico per tracciare il progresso dei [4 macro argomenti](https://gitlab.com/silab-gang/sweng/-/milestones/) del corso.
+- ha un utente assegnato: solitamente è l'integratore principale della issue.
+
+Nelle issue si può discutere e coordinare il lavoro, ma le review si fanno nelle merge request.
+Le osservazioni sul _processo_ si fanno nell'issue, quelle sul _contenuto_ nella merge request.
+
+Le merge request sono collegate alla relativa issue semplicemente citandola.
+È possibile utilizzare la [revisione GitLab](https://docs.gitlab.com/ee/user/project/merge_requests/reviews/) per indicare i problemi: se trovi un problema sei invitato a risolverlo subito, per velocizzare il processo.
+
+# Convenzioni mdBook
+
+Il Markdown scritto su mdBook è particolare e richiede l'utilizzo di alcune convenzioni, specialmente per lavorare insieme.
+
+## Nomi di file e intestazione
+
+Tutti i _file_ vanno creati in una sottocartella della cartella `src/`. 
+Ogni file all'interno di `src/` DEVE avere come il seguente nome: `PROGRESSIVO_nome-argomento.md` dove PROGRESSIVO è il numero progressivo del file all'interno della cartella. I numeri progressivi iniziano da zero e hanno un _leading zero_.
+
+Ogni file deve essere poi aggiunto in [`SUMMARY.md`](https://rust-lang.github.io/mdBook/format/summary.html). 
+
+## Diagrammi UML
+
+(Quasi) tutti i diagrammi UML mostrati durante le lezioni dal prof. Bellettini sono generati utilizzando [PlantUML](https://plantuml.com/), uno strumento open source che genera diagrammi in formato vettoriale partendo da del semplice testo.
+È quindi perfetto per il nostro caso d'uso (_pun intendend_).
+
+La sintassi per generare un diagramma dal Markdown di mdBook è la seguente:
+
+    ```plantuml
+    @startuml
+    Object <|-- ArrayList
+    Object : equals()
+    ArrayList : Object[] elementData
+    ArrayList : size()
+    @enduml
+    ```
+
+Informazioni complete sulla sintassi con esempi sono sul sito di PlantUML.
+
+Oltre al plugin, per generare i diagrammi è necessario installare l'eseguibile `plantuml`. Nei sistemi UNIX-like: 
+1. in una cartella che vuoi (come nella HOME), scarica il file `.jar` con `$ wget https://github.com/plantuml/plantuml/releases/download/v1.2022.13/plantuml-1.2022.13.jar -O plantuml.jar`;
+2. crea un file chiamato `/usr/bin/plantuml` avente come contenuto
+```bash
+#!/bin/bash
+java -jar /path/to/plantuml.jar "$1" "$2"
+```
+3. rendi il file eseguibile: `$ sudo chmod +x /usr/bin/plantuml`.
+
+Se possibile, __cerca sempre di utilizzare un diagramma UML al posto di uno screenshot__.
+
diff --git a/src/00_informazioni/01_contributori.md b/src/00_informazioni/01_contributori.md
new file mode 100644
index 0000000000000000000000000000000000000000..05f6f8ec0a329bed971a1fe61a914d11614ccc4c
--- /dev/null
+++ b/src/00_informazioni/01_contributori.md
@@ -0,0 +1,36 @@
+# Autori
+
+## Maintainers
+I maintainers hanno la responsabilità di definire la suddivisione degli argomenti, approvare e mergiare le Merge Request in `master` e assegnare il lavoro ai contributori.
+
+Inoltre, possono avere ulteriori responsabilità specifiche.
+
+- __Marco Aceti__: direzione del progetto, definizione workflow, supporto tecnico, pipeline CI/CD;
+- __Matteo Mangioni__: stesura delle styleguides, responsabile dei contenuti;
+- __Daniele Ceribelli__: capo revisori.
+
+## Argomenti
+
+Si precisa che l'attività di __revisione__ non è una mera rilettura, bensì di __refactoring completo del testo__ privilegiando la forma, _senza modificare i contenuti_.
+
+L'ordine nelle celle non è casuale.
+
+| __#__ | __Titolo__ | __Integratori__ | __Revisori__ |
+|-|-|-|-|
+| 01 | Introduzione | Daniele Ceribelli, Marco Aceti | Matteo Mangioni |
+| 02 | Modelli di ciclo di vita del software | Daniele Ceribelli, Marco Aceti | Matteo Mangioni |
+| 03 | eXtreme Programming | Daniele Ceribelli | Marco Aceti, Matteo Mangioni |
+| 04 | Open source | Marco Aceti, Daniele Ceribelli | Matteo Mangioni |
+| 05 | Software Configuration Management | Marco Aceti | Armani Islam |
+| 06 | Git workflow | Andrea Cambiaghi | Marco Aceti |
+| 07 | Progettazione | Daniele Ceribelli | Marco Aceti, Andrea Cambiaghi, Armani Islam |
+| 09 | Patterns | Daniele Ceribelli, Matteo Mangioni | Matteo Mangioni |
+| 10 | Mocking | Ilyass Ouardi | Francesco Protospataro, Marco Aceti |
+| 11 | UML | Francesco Protospataro | Marco Aceti |
+| 12 | Verifica e convalida | Matteo Yon, Marco Aceti | Marco Aceti, Matteo Mangioni |
+| 13 | Testing e processi di review | Marco Aceti, Matteo Yon, Mattia Mendecino, Andrea Cambiaghi |Matteo Mangioni, Marco Aceti | 
+| 14 | Reti di Petri | Daniele Ceribelli | Marco Aceti, Matteo Yon |
+| 15 | Analisi di reti di Petri | Daniele Ceribelli | Marco Aceti
+| 16 | Reti di Petri temporizzate | Armani Islam, Matteo Mangioni | Matteo Mangioni |
+
+<!-- scrivi qui il tuo nome se intendi contribuire :) -->
diff --git a/src/00_informazioni/03_convenzioni.md b/src/00_informazioni/03_convenzioni.md
new file mode 100644
index 0000000000000000000000000000000000000000..9a61e304fdf22aefaee688f70f7bf5f3454dc05c
--- /dev/null
+++ b/src/00_informazioni/03_convenzioni.md
@@ -0,0 +1,103 @@
+# Convenzioni di stile e contenuto
+
+Gli appunti devono essere chiari, concisi ma __completi__.
+L'obiettivo è creare la _bibbia_ del corso: idealmente studiandola da zero si dovrebbe arrivare al 30L.
+
+In tale prospettiva proponiamo una guida alle fasi di integrazione e di review che chiarifichi che cosa dev'essere presente negli appunti e lo stile di scrittura consigliato.
+
+Naturalmente, queste indicazioni valgono per gli appunti proposti per il branch master: per gli appunti presi a lezione è assolutamente OK essere vaghi o brevi.
+
+## Guida all'integrazione
+
+La fase di integrazione degli appunti dovrebbe servire per riunire gli appunti di tutti i partecipanti in un unico documento.
+Per agevolare la fase di review e riscrittura, tuttavia, questo non può limitarsi a un semplice _merge_ dei rispettivi file: l'integratore ha il compito di fornire a colui che dovrà riscrivere gli appunti la miglior base possibile su cui lavorare. \
+Ecco dunque alcuni consigli utili in tal senso:
+
+- Assicurarsi che __CI SIA TUTTO__: idealmente la fase di review dovrebbe solo fare "refactoring" degli appunti senza aggiungere nessun concetto, per cui è espressa responsabilità dell'integratore assicurarsi che il risultato finale sia assolutamente completo in quanto nessuno controllerà più i contenuti.
+
+- __Una frase, una riga__: al termine di ciascuna frase (_ndr. una proposizione terminata da punto_) andare a capo in Markdown.
+Questo infatti non spezza il paragrafo, come si può vedere dalla preview, ma agevola moltissimo il versioning con Git in quanto ogni frase viene così trattata come una linea di codice indipendente dalle altre.
+
+- Assicurarsi che le stesse cose non siano dette in più punti diversi e, nel caso, integrarle tra di loro;
+
+- Tenere i propri appunti sottomano per accertarsi che ogni concetto citato a lezione sia riportato: è chiaro che all'esame viene chiesto _tutto_, compresi i riferimenti esterni, per cui occorre includere negli appunti ogni nozione rilevante;
+
+- Organizzare gli argomenti in maniera logica, evitando salti logici in avanti e in indietro per agevolare il lavoro di review;
+
+- Sfruttare le potenzialità di Markdown (_es. titoli di vario livello, tabelle, elenchi..._) e rispettarne per quanto possibile le convenzioni (_es. linea vuota dopo i titoli, nessuno spazio alla fine di una riga..._);
+
+- Tenere __sempre__ la preview di mdbook aperta per verificare che immagini e/o schemi vengano mostrati correttamente.
+
+## Guida alla review
+
+Gli appunti definitivi dovrebbero costituire un discorso omogeneo e fluido, come fossero un piccolo libro di testo. \
+Per fare ciò, ecco alcune accortezze di stile e consigli utili durante la fase di review e riscrittura: si tratta solo di indicazioni (_"Just rules"_ ^-^), per cui non sentitevi in dovere di seguirle alla lettera.
+
+### <big>Contenuto</big>
+
+- Immaginare sempre di stare parlando con chi non sa nulla della materia: leggendo gli appunti dall'inizio alla fine si dovrebbe essere in grado di comprendere tutto.
+È quindi importante:
+
+  - non citare concetti senza che siano stati già spiegati precedentemente: se invece sono già stati spiegati può essere utile richiamarli con una formula del tipo _"Come sappiamo..."_ o _"Come abbiamo già visto..."_ seguita da un breve accenno al concetto;
+
+  - non dare per scontato nessuna conoscenza;
+
+- Se qualcosa è preso pari pari dalle slide può essere un campanello d'allarme.
+Conviene dunque farsi le seguenti domande:
+
+  - La frase si sposa bene con lo stile del discorso?
+  Come potrei riscriverla in modo da rendere il fluire del discorso più omogeneo?
+
+  - Il concetto espresso non è affrontato da nessun'altra parte?
+  Se sì, tale ripetizione è davvero necessaria e funzionale?
+
+- Mantenere convenzione "una frase, una riga" adottata nella fase di integrazione (_vd. sopra_): specialmente nella fase di review è importante che modificare una singola frase non comporti modificare interi paragrafi.
+
+- Tenere i propri appunti sottomano per verificare ulteriormente che non manchi nulla: sebbene la fase di integrazione dovrebbe in teoria creare un documento completo di tutto, può capitare che qualcosa sia sfuggito.
+
+### <big>Stile</big>
+
+- Adottare una __sintassi semplice__: gli appunti dovrebbero essere completi ma facili da seguire;
+
+- Avere una qualche estensione di __controllo ortografico__ attiva (_es. Code Spell per vscode_);
+
+- Usare il più possibile l'__impersonale__: non _"possiamo fare X"_ ma _"si può fare X"_;
+
+- Sforzarsi di presentare gli argomenti nel modo più chiaro possibile, legandoli tra di loro in unico discorso logico.
+Per favorire questo approccio, ogni argomento dovrebbe essere affrontato nel modo seguente:
+
+    1. __Presentare il problema__: es. _"Spesso capita di dover gestire X, Y e Z"_;
+
+    2. __Discutere e analizzare il problema__: es. _"Il problema ha queste queste e queste caratteristiche, che non possiamo risolvere con quanto visto finora"_;
+
+    3. __Proporre la/le soluzione/i al problema e discuterle__, confrontandole se più di una: es. _"In un primo momento si potrebbe pensare di risolvere così; tuttavia questo approccio ha questi difetti. Ecco allora che si è pensato di fare quest'altro"_;
+
+    4. __Concludere con un breve riassunto__ su quanto visto, che servirà inoltre a introdurre il prossimo argomento: es. _"Abbiamo quindi visto come risolvere sta cosa; la soluzione pone però un nuovo problema..."_.
+
+- Preferire i discorsi omogenei agli elenchi: usare gli elenchi __SOLO__ quando necessari.
+Alcuni esempi dei pochi casi in cui un elenco è accettabile sono:
+
+  - un __elenco puntato__ quando si elencano più cose contrapposte tra di loro (_es. diversi approcci o soluzioni a un problema_)
+
+  - un __elenco numerato__ quando si specificano le varie fasi di un processo (_es. ciclo di vita del software_)
+
+- Utilizzare la __separazione in paragrafi__ in modo coscienzioso: all'interno di un paragrafo dovrebbe idealmente essere trattato _un unico concetto_.
+Diversi aspetti dello stesso concetto possono essere separati nello stesso paragrafo andando a capo (_con \ al termine della riga_), mentre quando si passa al concetto successivo è bene aprire un nuovo paragrafo.
+
+- Utilizzare la __corretta punteggiatura__.
+Può essere utile in tal senso rileggere mentalmente gli appunti appena scritti per assicurarsi che il discorso fluisca in modo scorrevole, ricordando che:
+
+  - la virgola (",") rappresenta una pausa brevissima utilizzata per riprendere fiato o per evidenziare tramite un inciso (_una frase compresa tra due virgole_) determinati concetti che espandono in modo significativo il discorso principale;
+
+  - i due punti (":") rappresentano una pausa breve e sono usati per introdurre elenchi o proposizioni strettamente correlate con quella principale;
+
+  - il punto e virgola (";") rappresentano una pausa media, e vanno utilizzati quando si vuole dare un legame debole alla proposizione con la precedente e al termine di ogni elemento di un elenco tranne l'ultimo (_dove invece si usa il punto_);
+
+  - le parentesi ("(...)") vengono utilizzate per incapsulare proposizioni che espandono la frase principale in modo non significativo: idealmente esse potrebbero essere saltate nella lettura senza togliere nulla al discorso.
+
+- Utilizzare il __grassetto__ per evidenziare concetti chiave e il _corsivo_ per sottolineare frasi importanti; all'interno delle parentesi si può inoltre utilizzare il corsivo per aumentare la rilevanza del contenuto.
+
+- Usare le congiunzioni correttamente per legare le frasi tra di loro (_es. dunque, perché, perciò, allora, in quanto..._);
+
+- __NON IGNORARE I COMMENTI__: si tratta di richieste di aiuto da parte di chi ha fatto l'integrazione, che chiede un consiglio su una determinata questione.
+È dunque importante che per il termine della review tale problema sia stato risolto: se vi trovate in difficoltà potete sempre chiedere sul gruppo!
diff --git a/src/01_introduzione/00_index.md b/src/01_introduzione/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..1751a715fb115a8f41183b0d446b8a57607d0e63
--- /dev/null
+++ b/src/01_introduzione/00_index.md
@@ -0,0 +1,7 @@
+# Ingegneria, qualità e processi
+
+In questa lezione verranno trattati i seguenti argomenti.
+- [**Informazioni logistiche**](01_logistica.md): orari e modalità d'esame.
+- [**Ingegneria del software**](02_ingegneria.md): di cosa si occupa la materia?
+- [**Qualità del software**](03_qualita.md): quali qualità _misurabili_ ha un software?
+- [**Processo di sviluppo**](04_processo.md): quali fasi e processi contraddistongono lo sviluppo di un software?
diff --git a/src/01_introduzione/01_logistica.md b/src/01_introduzione/01_logistica.md
new file mode 100644
index 0000000000000000000000000000000000000000..d8a7b566e4e67792ca735146099b89bf1fc525ee
--- /dev/null
+++ b/src/01_introduzione/01_logistica.md
@@ -0,0 +1,23 @@
+# Informazioni logistiche
+- Non ci sarà lo streaming però ci sono le videolezioni
+- Teoria
+    - Lun 14:30-17:00 Aula 403
+    - Mer 14:30-17:00 Aula 403
+- Laboratorio
+    - Gio 13:30-17:30 due turni equivalenti
+        - Turno A matricole pari
+        - Turno B matricole dispari
+    - Due persone per computer, a coppia
+- Non c'è libro di testo, ma consigliati: 
+    - Software Engineering (Carlo Ghezzi, Dino Mandridi)
+    - Design Patterns (Eric Freeman, Elisabeth Robison)
+    - Handbook of Software and Systems Engineering (Albert Endres, Dieter Rombath)
+
+## Esami
+- __Laboratorio__
+    - prova pratica di laboratorio di 4 ore
+    - OPPURE _per chi segue tutti i laboratori_ ci saranno due laboratori valutati A COPPIE
+- __Teoria__ 
+    - prova orale
+- Prima di fare l'orale bisogna fare il laboratorio
+- La prova di laboratorio vale all'infinito
diff --git a/src/01_introduzione/02_ingegneria.md b/src/01_introduzione/02_ingegneria.md
new file mode 100644
index 0000000000000000000000000000000000000000..3da0a627104ec863c19dc82e3fe9c2c2cabbe681
--- /dev/null
+++ b/src/01_introduzione/02_ingegneria.md
@@ -0,0 +1,25 @@
+# Ingegneria del software
+
+## Storia 
+Con la diffusione dei primi computer in ambito accademico, negli anni '50 e '60 si è subito colta la necessità di superare metodi di produzione "artigianale" del software: sebbene il cliente e il programmatore coincidessero e i programmi fossero prettamente matematici, si iniziavano già a vedere i primi problemi. Negli anni '70, si inizia dunque a pensare a dei metodi, dei processi e a degli strumenti che potessero migliorare e ___"assicurare"___ la __qualità del software__, sviluppando un approccio di tipo ingegneristico costituito da una serie di fasi.
+
+## Approccio ingegneristico
+1. __Target__: ci si prefigge un obiettivo da raggiungere.
+2. __Metric__: si definisce una metrica per misurare la qualità del software, ovvero quanto esso si avvicina al target prefissato.
+3. __Method, Process, Tool__: si provano una serie di metodi, processi e strumenti per avvicinarsi all'obiettivo.
+4. __Measurements__: si misura tramite la metrica stabilita se le strategie implementate sono state utili e quanto ci hanno avvicinato (o allontanato) all'obiettivo. A seconda dei risultati ottenuti vi sono due possibili strade:
+    - risultati soddisfacenti (_aumento delle metrica_) - accettiamo come buoni metodi e processi utilizzati.
+    - risultati insoddisfacenti (_diminuzione della metrica_)- ci sono dei peggioramenti o dei forti effetti collaterali, di conseguenza bisogna modificare il lavoro qualcosa: si possono cambiare target o metrica se ci si rende conto di non aver ben definito l'obiettivo, ma più comunemente bisogna rivedere i processi e metodi usati.
+
+Ma __che cosa si intede per target__? Gli obiettivi da raggiungere possono essere di due tipi: la risoluzione dei __problemi nella progettazione del software__ e l'assicurazione di una qualche __qualità che il software dovrà avere__. È dunque necessario chiedersi le seguenti domande:
+- Quali problemi ci sono?
+- Quali qualità deve avere il software?
+
+### Problemi principali
+Vediamo allora a questo punto alcuni dei problemi che possono insorgere durante lo sviluppo di software, partendo dal presupposto che una delle più grandi fonti di problemi sono le __persone__.  L'obiettivo della disciplina è infatti principalmente quello di risolvere i __problemi di comunicazione__, che possono essere:
+- tra il __programmatore__ e il __cliente__: sono esperti di domini diversi ed è difficile comprendersi;
+- tra un __programmatore__ e altri __programmatori__.
+
+Un'altra fonte di problemi sono le __dimensioni__ del software, che possono raggiungere valori molto elevati in termini di milioni di righe di codice e migliaia di _"anni uomo"_ di lavoro. Lo sviluppo software non è più piccolo e domestico, e questo crea chiaramente problemi di manutenzione e gestione della codebase.
+
+Il software è infine __facilmente malleabile__, ovvero modificabile nel tempo: il moltiplicarsi di versioni, evoluzioni e variazioni di target può creare non poche difficoltà.
diff --git a/src/01_introduzione/03_qualita.md b/src/01_introduzione/03_qualita.md
new file mode 100644
index 0000000000000000000000000000000000000000..0f2139aa06d15c1aa606964ef43fadbfcf573607
--- /dev/null
+++ b/src/01_introduzione/03_qualita.md
@@ -0,0 +1,150 @@
+# Qualità
+Per fare fronte ai problemi sopracitati si sviluppano allora una serie di processi per lo sviluppo software: essi non assicurano la bontà del programma finito, ma possono assicurare la presenza di _proprietà desiderabili_ del prodotto, dette __qualità__. Le qualità del prodotto, che costituiscono a conti fatti un "valore per le persone", si dividono innanzitutto in due tipi:
+- __qualità esterne__: qualità che vengono colte dal cliente;
+- __qualità interne__: qualità che vengono esclusivamente colte dallo sviluppatore.
+
+Le qualità interne influenzano molto le qualità esterne (per esempio se ho un codice ottimizzato ed efficiente, il mio software produrrà i risultati più velocemente). Prima di vedere quali siano le proprietà auspicabili in un software, però, facciamo un'importante distinzione a livello terminologico tra __requisiti e specifiche__:
+- I __requisiti__ sono quello che il cliente vuole che il software faccia. Spesso sono cambiati in corso d'opera oppure sono espressi in modo sbagliato, per cui è necessaria un'interazione continua. 
+- Le __specifiche__ sono ciò che è stato formalizzato dal programmatore a partire dai requisiti: si tratta di una definizione più rigorosa di che cosa dovrà fare il software. Si noti però che se i requisiti sono stati espressi in modo non corretto anche le specifiche risulteranno inesatte (vd. <a href="#g1">G1</a>).
+
+Fatta questa doverosa distinzione, vediamo quali sono le qualità che un software dovrebbe idealmente possedere.
+
+## Qualità del software
+
+Un software di qualità deve <b><i>funzionare</i></b>, <b><i>essere bello</i></b> e <b><i>"farmi diventare ricco"</i></b>.
+
+<table style="margin-bottom: 20px">
+<thead>
+    <tr>
+        <th>Un software deve...</th>
+        <th>Qualità</th>
+        <th>Descrizione</th>
+    </tr>
+</thead>
+<tbody>
+    <tr>
+        <th rowspan="3"><i>Funzionare</i></th>
+        <th>Correttezza</th>
+        <td>
+            Un software è corretto se soddisfa la specifica dei suoi <i>requisiti funzionali</i>. Si tratta di una proprietà <i>"matematica"</i> e relativamente dimostrabile in modo formale.
+        </td>
+    </tr>
+    <tr>
+        <th>Affidabilità</th>
+        <td>
+            Un software è affidabile quando ci si può fidare del suo funzionamento, ovvero ci si può aspettare che faccia ciò che gli è stato chiesto.
+            Se è molto difficile perseverare la correttezza, in quanto si tratta una proprietà assoluta, l'affidabilità è invece relativa: un software può essere affidabile (o <i>dependable</i>) nonostante contenga qualche errore.
+        </td>
+    </tr>
+    <tr>
+        <th>Robustezza</th>
+        <td>
+            Un software è robusto se si comporta in modo accettabile anche in circostanze non previste nella specifica dei requisiti, senza generare effetti troppo negativi.
+        </td>
+    </tr>
+    <tr>
+        <th rowspan="3"><i>Essere bello</i></th>
+        <th>Usabilità</th>
+        <td>
+            Un software è usabile (o <i>user-friendly</i>) se i suoi utenti lo ritengono facile da utilizzare.
+            Si possono fare degli esperimenti (le grandi aziende lo fanno) per testare e quantificare l’usabilità del software ponendolo di fronte a dei soggetti umani (vd. <a href="#nn23">NN23</a>).
+        </td>
+    </tr>
+    <tr>
+        <th>Prestazioni</th>
+        <td>
+            Ad ogni software è richiesto un certo livello di prestazioni. L'efficienza è una qualità interna e misura come il software utilizza le risorse del computer; la performance, d'altro canto, è invece una qualità esterna ed è basata sui requisiti dell'utente. Essa ha effetto sull'usabilità, e spesso viene considerata alla fine dello sviluppo software visto che vari avanzamenti tecnologici possono efficientare algoritmi e processi prima troppo costosi.
+        </td>
+    </tr>
+    <tr>
+        <th>Verificabilità</th>
+        <td>
+            Un software è verificabile se le sue proprietà sono verificabili facilmente: è importante essere in grado di poter dimostrare la correttezza e la performance di un programma, e in tal senso la <b>leggibilità</b> del codice è fondamentale. 
+            La verifica può essere fatta con metodi formali o informali, come il testing.
+            È considerata una qualità interna, ma alcune volte può diventare una qualità esterna: per esempio, in ambiti in cui la sicurezza è critica il cliente può chiedere la verificabilità di certe proprietà.
+        </td>
+    </tr>
+    <tr>
+        <th rowspan="2"><i>Farmi diventare ricco</i></th>
+        <th>Riusabilità</th>
+        <td>
+            Le componenti del software che costruiamo dovrebbero essere il più riutilizzabili possibile così da risparmiare tempo in futuro: ciò può essere fatto non legandole troppo allo specifico contesto applicativo del software che stiamo sviluppando.
+            Con la proprietà di riusabilità, utilizziamo un prodotto per costruire - anche con modifiche minori - un altro prodotto (vd. <a href="#mi15">MI15</a>).
+        </td>
+    </tr>
+    <tr>
+        <th>Manutenibilità</th>
+        <td>
+            Per <i>manutenzione software</i> si intendono tutte le modifiche apportate al software dopo il rilascio iniziale.
+            Questa proprietà può essere vista come due proprietà separate:
+            <ul style="margin-bottom: 0;">
+                <li><b>Riparabilità</b>: un software è riparabile se i suoi difetti possono essere corretti con una quantità di lavoro ragionevole.</li>
+                <li><b>Evolvibilità</b>: indica la capacità del software di poter evolvere aggiugendo funzionalità. È importante considerare questo aspetto fin dall'inizio: studi rilevano come l'evolvibilità decresce con il passare delle release (vd. <a href="#l27-28">L27-28</a>).</li>
+            </ul>
+        </td>
+    </tr>
+</tbody>
+</table>
+
+### Leggi rilevanti
+
+<a id="g1"></a>
+__Prima legge di R.Glass (G1)__. 
+> _La mancanza di requisiti è la prima causa del fallimento di un progetto._
+
+<a id="nn23"></a>
+__Legge di Nielsen-Norman (NN23)__.
+> _L'usabilità è misurabile._
+
+<a id="mi15"></a>
+__Legge di McIlroy (MI15)__. 
+> _Riutilizzare il software permette di incrementare la produttività e la qualità._
+
+<a id="l27-28"></a>
+__Leggi di M. Lehman (L27-28)__. 
+> _Un sistema che viene utilizzato cambierà._
+
+> _Un sistema che evolve incrementa la sua complessita a meno che non si lavori appositamente per ridurla._
+
+## Qualità del processo
+> _Un progetto è di qualità se segue un buon processo._
+
+Sappiamo che il prodotto è influenzato dal processo che viene utilizzato per svilupparlo, di conseguenza possiamo parlare anche di  __qualità del processo__.
+
+Anche un processo deve funzionare, essere essere bello e farmi diventare ricco, ma dobbiamo interpretare queste parole in maniera differente.
+
+Quali caretteristiche ha un processo di qualità?
+
+<table style="margin-bottom: 20px">
+<thead>
+    <tr>
+        <th>Un processo deve...</th>
+        <th>Qualità</th>
+        <th>Descrizione</th>
+    </tr>
+</thead>
+<tbody>
+    <tr>
+        <th rowspan="1"><i>Funzionare</i></th>
+        <th>Robustezza</th>
+        <td markdown="span">
+            Un processo deve poter resistere agli imprevisti, come la mancanza improvvisa di personale o il cambiamento delle specifiche.
+            Esistono certificazioni (<i>CMM: Capability Maturity Model</i>) che valutano la robustezza di alcuni processi aziendali e che vengono per esempio considerate nei bandi pubblici.
+        </td>
+    </tr>
+    <tr>
+        <th rowspan="1"><i>Essere bello</i></th>
+        <th>Produttività</th>
+        <td markdown="span">
+            La produttività di un team è molto meno della somma della produttività individuale dei suoi componenti. È una metrica difficile da misurare: conteggi come il numero di linee codice scritte o la quantità di <i>tempo-uomo</i> richiesta per un lavoro si rivelano spesso un po' fallaci (per esempio, la gravidanza umana non è un'attività parallelizzabile, ma si potrebbe dire che servono 9 mesi-donna per creare un bambino).
+        </td>
+    </tr>
+    <tr>
+        <th rowspan="1"><i>Farmi diventare ricco</i></th>
+        <th>Tempismo</th>
+        <td markdown="span">
+            Un processo deve consegnare il prodotto nei tempi stabiliti, in modo da rispettare i tempi del mercato. È spesso conveniente la tecnica dello <b>sviluppo incrementale</b>, ovvero la consegna frequente di parti sempre più grandi del prodotto (es. compilatore ADA): essa permette infatti di conquistare il cliente ancor prima di avere il prodotto finito. 
+        </td>
+    </tr>
+</tbody>
+</table>
diff --git a/src/01_introduzione/04_processo.md b/src/01_introduzione/04_processo.md
new file mode 100644
index 0000000000000000000000000000000000000000..0403061db11a74b220c3df889ff25e9053d93de2
--- /dev/null
+++ b/src/01_introduzione/04_processo.md
@@ -0,0 +1,116 @@
+# Il processo di produzione del software
+Il processo che seguiamo per costruire, consegnare, installare ed evolvere il prodotto software, dall'idea fino alla consegna e al ritiro finale del sistema, è chiamato __processo di produzione software__.
+
+Innanzitutto occorre riconoscere diverse problematiche.
+- I __requisiti__ imposti dal cliente possono cambiare spesso.
+- Produrre software __non è _solo_ scrivere codice__ (alla Programmazione I).
+- Bisogna risolvere i __problemi di comunicazione__ tra tutte le diverse figure in gioco (tra sviluppatori, tra progettista e sviluppatori, ecc).
+- Bisogna essere __rigorosi__, anche se può essere difficile. Ci sono lati positivi e negativi: la rigorisità può facilitare la comprensione di ciò che bisogna fare ma implica  al contempo molta fatica extra, e viceversa.
+    > __Ipotesi di Bauer-Zemanek (BZh3)__: _Metodi formali riducono in maniera significativa gli errori di progettazione, oppure permettono di eliminarli e risolverli prima._
+
+    Trovare gli errori prima della fase di sviluppo permette di facilitarne la risoluzione e di risparmiare sia tempo che soldi: tanto prima si individua un errore, tanto più facile sarà risolverlo.
+- Ci sono __tanti aspetti__ da considerare, che andranno affrontati uno alla volta. Per parlare di aspetti diversi ho bisogno di metodi comunicazione diversi, che interessano ruoli diversi in tempi diversi (_Aspect Oriented Programming_).
+
+Tenendo a mente tutto queste problematiche è necessario decidere come organizzare l'attività di sviluppo software in modo da mitgarle. Per modellare un ciclo di vita del software, occorre dunque in primo luogo __identificare le varie attività necessarie__ e quindi:
+- deciderne le precedenze temporali;
+- decidere chi le debba fare.
+
+In particolare, ci si pone due domande:
+- cosa devo fare adesso?
+- fino a quando e come?
+
+L'ingegneria del software prova a rispondere a queste domande per individuare quali siano le fasi necessarie per sviluppare un software e quale sia la loro migliore disposizione temporale. È dunque bene specificare da subito che lo sviluppo di un programma non è solo coding: tale presupposto genera conseguenze disastrose.
+
+Inizialmente, infatti, nell'ambito dello sviluppo software è stato adottato il modello ___code-and-fix___, che consisteva nei seguenti passi:
+1. scrivi il codice;
+2. sistemalo per eliminare errori, migliorare funzionalità o aggiungere nuove funzionalità.
+
+Ben presto però questo modello si è dimostrato pesantemente inefficace in gruppi di lavoro complessi, specialmente quando il cliente non era più lo sviluppatore stesso ma utenti con poca dimestichezza con i computer, generando codice estremamente poco leggibile e manutenibile.
+
+Per organizzare meglio l'attività di sviluppo e non ricadere negli errori del passato gli ingegneri del software hanno dunque individuato diverse __fasi__ del ciclo di vita di un software che, combinate, permettessero di produrre del software di qualità. Diamo dunque un'occhiata a quelle principali.
+
+## Le fasi del ciclo di vita del software
+
+### Studio di fattibilità
+Lo studio di fattibilità è l'attività svolta prima che il processo di sviluppo inizi, per decidere se dovrebbe iniziare _in toto_.
+L'__obiettivo__ è quello di produrre un __documento in linguaggio naturale__ presentante diversi scenari di sviluppo con soluzioni alternative, con una discussione sui trade-off in termini di benefici e costi attesi.
+
+Più specificatamente, il documento include:
+- uno studio di diversi scenari di realizzazione, scegliendo:
+    - le architetture e l'hardware necessario;
+    - se sviluppare in proprio oppure subappaltare ad altri.
+- stima dei costi, tempi di sviluppo, risorse necessarie e benfici delle varie soluzioni.
+
+È spesso difficile fare un'analisi approfondita, a causa del poco tempo disponibile o di costi troppo elevati: spesso viene commissionata esternamente.
+
+### Analisi e specifica dei requisiti
+L'analisi e specifica dei requisiti è l'attività più critica e fondamentale del processo di produzione del software.
+L'obiettivo è la stesura di un ___documento di specifica___ <!-- ... -->.
+
+
+In questa fase i progettisti devono:
+- comprendere il __dominio applicativo__ del prodotto, dialogando con il cliente e la controparte tecnica;
+- identificare gli __stakeholders__, ovvero tutte le figure interessate al progetto, e studiarne le richieste. Spesso non si tratta di figure omogenee (può essere il _top manager_ fino al segretario) e le loro necessità sono molto diverse;
+- capire quali sono le __funzionalità richieste__: la domanda più importante che deve porsi il programmatore è il _cosa_ non il _come_; al cliente non devono infatti interessare gli aspetti tecnici e le scelte architetturali interne. Le __specifiche__ vanno quindi viste dal punto di vista del cliente.
+- stabilire un __dizionario comune__ tra cliente e sviluppatore che può anche far parte della specifica per agevolare la comunicazione;
+- definire __altre qualità__ eventualmente richieste dal cliente: per esempio, _"la centrale non deve esplodere"_ non è un dettaglio implementativo, ma un requisito. Queste ulteriori qualità, che non sempre sono solo esterne, si dicono __requisiti non funzionali__.
+
+Lo scopo del _documento di specifica_ è duplice: da una parte, deve essere analizzato e approvato da __tutti gli stakeholders__ in modo da verificare il soddisfacimento delle aspettative del cliente, e dall'altra è usato dai programmatori per sviluppare una soluzione che le soddisfi, fungendo da punto di partenza per il design.
+È un documento contrattuale e deve essere scritto in modo formale per evitare contestazioni contrattuali e ambiguità.
+
+Deve essere presente anche un __piano di test__, ovvero una collezione di collaudi che certificano la correttezza del lavoro: se questi test hanno esito positivo il lavoro viene pagato, altrimenti il progetto non viene accettato. A differenza dei lavori di altri tipi di ingegneria, per esempio l'ingegneria civile, dove il collaudo è diretto, nell'ingegneria del software è molto difficile collaudare tutti i casi e gli stati possibili.
+
+Un altro output di questa fase può essere anche il __manuale utente__, ovvero la _"vista esterna"_ (ciò che il cliente vuole vedere, evitando i dettagli implementativi) del sistema da progettare.
+
+> __Legge di David__: Il valore dei modelli che rappresentano il software da diversi punti di vista dipendono dal punto di vista preso (assunto), ma non c'è nessuna vista che è la migliore per ogni scopo.
+
+### Progettazione (design)
+Il _design_ è l'attività attraverso la quale gli sviluppatori software strutturano l'applicazione a diversi livelli di dettaglio.
+Lo scopo di questa fase è quello di scrivere un __documento di specifica di progetto__ contenente la descrizione dell'architettura software (i diversi linguaggi e viste). 
+
+Durante questa fase occorre quindi:
+- scegliere un'__architettura software di riferimento__;
+- __scomporre__ in moduli o oggetti gli incarichi e i ruoli: si tratta del cosiddetto _object oriented design_, non necessariamente accompagnato da object oriented programming;
+- __identificare i patterns__, ovvero problemi comuni a cui è già stata trovata una soluzione generale giudicata come _"bella"_ dalla comunità degli sviluppatori (ne vedremo un paio più avanti nel corso). I pattern favoriscono alcune qualità, come il design.
+
+### Programmazione e test di unità
+In questa fase le _"scatole nere"_ - i moduli o oggetti definiti al punto precedente - vengono realizzate e per ognuna di esse vengono definiti dei __test unitari__ che ne mostrano la correttezza.
+Vi è infatti spesso la brutta abitudine di non fare il testing durante lo sviluppo di ciascun componente, ma solamente alla fine di tutto: questa usanza è molto pericolosa perché un problema scoperto solo alla fine è molto più oneroso da risolvere.
+
+I singoli moduli vengono testati indipendentemente, anche se alcune funzioni da cui dipendono non sono ancora sono state implementate: per risolvere tale dipendenza si utilizzano allora moduli fittizzi (___stub___) che emulino le funzionalità di quelli mancanti.
+Altri moduli, detti ___driver___, forniscono invece una situazione su cui applicare il modulo che sto testando.
+Nei linguaggi più utilizzati esistono framework che facilitano le suddette operazioni al programmatore.
+
+L'obiettivo di questa fase è avere un __insieme di moduli__ separati __sviluppati indipendentemente__ con un'interfaccia concordata e __singolarmente verificati__.
+
+### Integrazione e test di sistema
+In questa fase i moduli singolarmente implementati e testati vengono __integrati__ insieme a formare il software finito. In alcuni modelli di sviluppo (come nello sviluppo incrementale) questa fase viene accorpata alla precedente.
+
+Nei test, i moduli _stub_ e _driver_ vengono sostituiti con i componenti reali formando un sistema sempre più grande fino ad ottenere il risultato richiesto.
+È poi fondamentale testare che l'intero programma funzioni una volta assemblato (non basta che le singole parti funzionino!): test di questo tipo vengono detti __test di integrazione__. 
+
+L'integrazione può essere adottata seguendo un approccio _top down_ o _bottom up_. La fase finale è l'___alpha testing___, ovvero il testing del sistema in condizioni realistiche.
+
+### Consegna, installazione e manutenzione
+Dopo aver completato lo sviluppo, il software deve essere __consegnato__ ai clienti. Prima di consegnarlo a tutti, si seleziona un gruppo di utenti per raccogliere ulteriore feedback; questa fase è chiamata ___beta testing___.
+
+L'__installazione__ (deployment) definisce il _run-time_ fisico dell'architettura del sistema. Per esempio, un servizio di rete potrebbe necessitare di apparecchiatura server da installare e particolari configurazioni.
+
+Infine, la __manutenzione__ può essere definita come l'insieme delle attività finalizzate a modificare il sistema dopo essere stato consegnato al cliente. La manutenzione può essere di tipo:
+- __correttivo__: sistemare errori nel sistema;
+- __adattivo__: adattare il software ai nuovi requisiti (vd. _evolvibilità_);
+- __perfettivo__: migliorare certi aspetti interni al programma senza modificare gli aspetti esterni. Serve per migliorare la futura manutenzione riducendo il cosiddetto _debito tecnico_.
+
+Come già detto, è necessario sviluppare avendo in mente la futura manutenzione di ciò che si sta scrivendo: infatti, il __costo__ della manutenzione concorre al costo del software in una misura spesso superiore al 60%.
+
+L'_output_ di questa fase è un __prodotto migliore__.
+
+### Altre attività
+Alle attività sopracitate se ne aggiungono altre:
+- __Documentazione__: può essere vista come attività trasversale. Per esempio, un documento di specificazione contenente diagrammi UML e una descrizione narrativa che spiega le motivazione dietro certe decisioni può essere il risultato principale della fase di progettazione.
+È un'attività spesso da procastinare, perché le specifiche possono cambiare spesso. In alcuni gruppi esistono delle figure che si occupano di questa attività, anche se può essere pericoloso: non tutti possono capire ciò che un programmatore ha creato.
+- __Verifica e controllo qualità__ (Quality Assurance): nella maggior parte dei casi, la verifica è svolta attraverso review e ispezioni. L'obiettivo è anticipare il prima possibile la scoperta e la sistemazione degli errori in modo da evitare di consegnare sistemi difettosi. Andrebbe fatta costantemente e non tutta alla fine.
+- __Gestione del processo__: gestione incentivi (premi di produzione), responsabilità, formazione del personale, perfezionamento del processo con l'esperienza guadagnata, eccetera. 
+- __Gestione delle configurazioni__: gestione delle relazioni inter-progettuali, ovvero delle risorse di sviluppo non appartenenti ad un singolo progetto. Un esempio potrebbe essere una libreria condivisa tra più progetti, i quali vorrebbero modificare la libreria stessa.
+
+Tutte queste diverse attività saranno specificate successivamente entrando nel dettaglio.
diff --git a/src/01_introduzione/logistica.md b/src/01_introduzione/logistica.md
new file mode 100644
index 0000000000000000000000000000000000000000..3be148c4cbc42ad87ac5ba22a4505aa06aef3839
--- /dev/null
+++ b/src/01_introduzione/logistica.md
@@ -0,0 +1 @@
+# Informazioni logistiche
diff --git a/src/02_ciclo-vita/00_index.md b/src/02_ciclo-vita/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..5a73f82683e656ff8a96a561fe36f3ea4f7c3f1c
--- /dev/null
+++ b/src/02_ciclo-vita/00_index.md
@@ -0,0 +1,24 @@
+# Modelli di ciclo di vita del software
+
+In questa lezione vedremo i principali modelli di ciclo di vita del software, ovvero famiglie di processi di sviluppo che si distinguono per il modo in cui le fasi di produzione viste nella scorsa lezione vengono organizzate in un processo unitario. 
+Ognuno di tali modelli avrà i propri pro e i propri contro, ed è bene da subito capire che non esiste il modello giusto per ogni situazione.
+
+- [**Modelli sequenziali**](./01_modelli-sequenziali.md)
+    - modello a cascata
+    - modello a V
+- [**Modelli iterativi**](./02_modelli-iterativi.md)
+    - modello a cascata con singola retroazione
+    - modello a fontana
+- [**Modelli incrementali**](./03_modelli-incrementali.md)
+    - modello prototipale
+    - pinball life-cycle
+    - metamodello a spirale
+    - modelli trasformazionali
+    - modello COTS
+- [**Metodologie Agile**](./04_metodologie-agile.md)
+    - manifesto
+    - lean
+    - kanban
+    - scrum
+    - crystal
+    - extreme programming
\ No newline at end of file
diff --git a/src/02_ciclo-vita/01_modelli-sequenziali.md b/src/02_ciclo-vita/01_modelli-sequenziali.md
new file mode 100644
index 0000000000000000000000000000000000000000..741b3909f08bc315d803278321ac71c9755695bd
--- /dev/null
+++ b/src/02_ciclo-vita/01_modelli-sequenziali.md
@@ -0,0 +1,89 @@
+# Modelli sequenziali
+
+Il modo più semplice e immediato di organizzare le fasi del ciclo di vita di un software è sicuramente quello __sequenziale__: i vari passaggi vengono posti in un ordine prestabilito e vengono attraversati uno alla volta uno dopo l'altro. Da questa idea nascono i cosiddetti _modelli sequenziali_, di cui il più famoso è certamente il _modello a cascata_.
+
+## Modello a cascata
+
+### Caratteristiche e punti di forza
+
+![Modello a cascata](/assets/02_waterfall-model.png)
+
+Nato negli anni '50 ma diventato famoso solo negli anni '70 grazie allo sviluppo di un grosso software per la difesa area chiamato SAGE (_Semi-Automated Ground Environment_), il modello a cascata organizza le fasi in una serie di step sequenziali: fatto uno si passa al successivo fino ad arrivare alla fine, come in una sorta di _catena di montaggio_. Viene infatti forzata una __progressione lineare__ da una fase alla successiva; non è previsto in questo modello tornare indietro a uno step precedente.
+
+Sebbene varino molto da processo a processo, la maggior parte dei processi che segue il modello a cascata include almeno le seguenti fasi organizzate in quest'ordine:
+
+1. Requisiti
+2. Progetto
+3. Codifica
+4. Testing
+5. Prodotto
+
+Ognuno di tali step produce un output, detto __semilavorato__, che è dato come input allo step successivo. In virtù dell'affidamento su tali semilavorati intermedi il modello a cascata si dice __document-based__: tra una fase e l'altra si crea infatti un documento che è il mezzo di trasmissione dell'informazione. Questo aspetto permette una __buona separazione dei compiti__ tra i vari dipendenti che lavorano al progetto: ognuno è infatti specializzato in una singola fase e una volta prodotto il documento utile ad avviare la fase successiva il suo coinvolgimento nel progetto non è più necessario ed esso può essere assegnato ad altri lavori.
+
+La linearità del modello rende inoltre possibile __pianificare i tempi__ accuratamente e monitorare semplicemente lo stato di avanzamento in ogni fase: è infatti sufficiente stimare la durata di ogni fase per ottenere una stima del tempo di completamento dell'intero progetto. Si tratta però di una stima a senso unico: una volta finita una fase non è possibile ridurre il tempo speso, e in caso di inconvenienti l'unica opzione è cercare di assorbire il ritardo.
+
+### Criticità
+
+Sebbene il modello a cascata abbia il grande pregio di aver posto l'attenzione sulla comunicazione tra gli elementi del progetto in un momento storico in cui il modello di sviluppo più diffuso era di tipo _code-and-fix_, esso soffre di numerose criticità.
+
+In primo luogo il modello __non prevede una fase di manutenzione__ del software prodotto: esso assume di non dover apportare modifiche al progetto dopo averlo consegnato, e impedisce dunque di _"tornare indietro"_ in alcun modo. Ovviamente questa assunzione è un'illusione smentita nella quasi totalità nei casi: qualunque software è destinato ad evolvere, e più un software viene usato più cambia. Una volta finito lo sviluppo ciò che si può fare è rilasciare al più piccole patch, che tuttavia non fanno altro che disallineare la documentazione prodotta precedentemente con il software reale.
+
+Il modello soffre inoltre di una generale __rigidità__, che mal si sposa con la flessibilità naturalmente richiesta dall'ambiente di sviluppo software. In particolare, l'impossibilità di tornare indietro implica un __congelamento dei sottoprodotti__: una volta prodotto un semilavorato esso è fisso e non può essere modificato; questo è particolarmente critico per le stime e specifiche fatte durante le prime fasi, che sono fisiologicamente le più incerte.
+
+Infine, il modello a cascata adotta un approccio volto alla __monoliticità__: tutta la pianificazione è orientata ad un singolo rilascio, e l'eventuale manutenzione può essere fatta solo sul codice. Inutile dire che si tratta di una visione fallace, in quanto come già detto più volte il software è destinato ad essere modificato e ad evolvere.
+
+### _Who's Afraid of The Big Bad Waterfall?_
+
+> LIBRO: __The Leprechauns of Software Engineeering__ di Laurent Bossavit.
+
+In realtà, il modello a cascata non è mai stato veramente elogiato, ma è sempre stato utilizzato come paragone negativo per proporre altri modelli o variazioni.
+Nel corso del tempo la sua presentazione è stata erroneamente attribuita al paper [_"Managing the development of large software systems: concepts and techniques"_](https://dl.acm.org/doi/10.5555/41765.41801) di W.W. Royce, di cui veniva citata solo la prima figura: Royce stava a dire il vero presentando quel modello per descrivere la sua esperienza nello sviluppo software, per poi proporre altri concetti più moderni (come lo sviluppo incrementale) che non sono però mai stati colti dalla comunità scientifica.
+
+Anche noi utilizziamo il modello a cascata solo come paragone negativo, e in generale nell'ambiente di sviluppo software esso non è più applicato alla lettera. Alcuni suoi aspetti si sono però mantenuti come linee guida generali (es. l'ordine delle fasi); è infatti bene chiarire subito che esistono due tipi di modelli:
+
+- __prescrittivi__: forniscono delle indicazioni precise da seguire per svolgere un processo;
+- __descrittivi__: colgono certi aspetti e caratteristiche di particolari processi esistenti, ma non obbligano a seguirli in modo rigoroso.
+
+Tutti i modelli visti per ora ricadono perlopiù nell'ambito descrittivo, mentre i modelli AGILE che vedremo più avanti tendono ad essere più di tipo prescrittivo.
+
+### Riassunto pro e contro
+
+<table style="margin-bottom: 20px">
+    <thead>
+        <tr>
+            <th style="text-align:center">Pro</th>
+            <th style="text-align:center">Contro</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td>
+                <ul style="margin-top: 15px">
+                    <li>Document-based</li>
+                    <li>Buona suddivisione dei compiti</li>
+                    <li>Semplice pianificazione dei tempi</li>
+                </ul>
+            </td>
+            <td>
+                <ul style="margin-top: 15px">
+                    <li>Rigidità</li>
+                    <li>Congelamento dei sottoprodotti</li>
+                    <li>Monoliticità</li>
+                </ul>
+            </td>
+        </tr>
+    </tbody>
+</table>
+
+## Modello a V (denti di pesce cane)
+
+![Modello a V](/assets/02_v-model.png)
+
+Dal modello a cascata nascono poi numerose varianti che cercano di risolverne i vari problemi: tra queste spicca per rilevanza il __modello a V__, che introduce fondamentalmente una __più estesa fase di testing__.
+
+Nonostante sia ancora un modello sequenziale come il modello a cascata, nel modello a V vengono infatti evidenziati nuovi legami tra le fasi di sviluppo, che corrispondono alle attività di __verifica__ e __convalida__: alla fine di ogni fase si _verifica_ che il semilavorato ottenuto rispetti la specifica espressa dalla fase precedente, e inoltre si richiede la _convalida_ del fatto che esso sia in linea con i veri vincoli e necessità del cliente. Come si vede, questo modello pone l'accento sul rapporto con il cliente, che viene continuamente coinvolto con la richiesta di feedback su ciascun sottoprodotto generato.
+
+Volendo formalizzare, le due nuove attività introdotte sono dunque:
+
+- __verifica__ (freccie grigie): controlla la correttezza rispetto alla descrizione formale delle specifiche;
+- __validazione__ (freccie bianche): controlla la compatabilità del sistema con le esigenze del cliente tramite feedback continuo.
\ No newline at end of file
diff --git a/src/02_ciclo-vita/02_modelli-iterativi.md b/src/02_ciclo-vita/02_modelli-iterativi.md
new file mode 100644
index 0000000000000000000000000000000000000000..d43bfb3838ec427dd43afa286ed147e1d81f2144
--- /dev/null
+++ b/src/02_ciclo-vita/02_modelli-iterativi.md
@@ -0,0 +1,21 @@
+# Modelli iterativi
+
+Osservando il modello a cascata e le sue varianti ci si è ben presto resi conto che la stringente sequenzialità delle fasi costituiva un grosso limite non conciliabile con la flessibilità richiesta dallo sviluppo software e con la naturale mutevolezza dei requisiti imposti dal cliente. Si inizia dunque a pensare di permettere agli sviluppatori di _ripetere_ alcune fasi più di una volta, ciclando su di esse fino a ottenere un prodotto soddisfacente: nascono così i primi __modelli interativi__.
+
+## Modello a cascata con singola retroazione
+
+![Waterfall con retroazione](/assets/02_waterfall-retroazione.png)
+
+Uno dei primi modelli iterativi è in realtà una variante del modello a cascata, in cui si permette di fare un'unico salto indietro; a parire da una fase si può cioè __ritornare alla fase precedente__: così, per esempio, si può _iterare_ tra _Codifica_ e _Testing_ fino a consegnare il prodotto.
+
+Anche in questo modello non si può però tornare indietro dalla consegna per eseguire attività di manutenzione; inoltre, l'introduzione di un'iterazione rende molto __più difficile pianificare__ il lavoro e monitorarne l'avanzamento: si tratta questa di una caratteristica condivisa da molti modelli iterativi.
+
+## Modello a fontana
+
+![Modello a fontana](/assets/02_fountain-model.png)
+
+Nel 1993 nasce, in contrapposizione al modello a cascata, il cosiddetto __modello a fontana__, che amplia il concetto di iterazione permettendo in qualunque momento di __tornare alla fase iniziale__: se ci si accorge della presenza di errori si torna indietro all'inizio (_software pool_) e si ricontrollano tutte le fasi precedenti. Ovviamente questo non implica buttare tutto il lavoro già fatto, quanto piuttosto risolvere l'errore con un approccio che parta innanzitutto dalla modifica dei requisiti (se possibile), delle specifiche e solo dopo del codice, evitando di rattoppare solo quest'ultimo alla bell'e meglio come nel modello _code-and-fix_.
+
+Il modello a fontana è inoltre il primo in cui sono previste delle azioni dopo la consegna; dopo l'ultima fase (_programma in uso_), infatti, si aprono ancora due strade: __manutenzione ed evoluzione__. La consegna del prodotto non è quindi più l'atto finale, ma solo un altro step del processo: ecco quindi che si aprono le porte ad una __visione incrementale__ dello sviluppo software, che approfondiremo nel prossimo paragrafo.
+
+Anche qui si perdono purtroppo le garanzie sui tempi di sviluppo: una volta ritornato all'inizio per sistemare un errore non è infatti affatto detto che riuscirò a ritornare alla fase da cui sono partito, ma potrei imbattermi in altri errori durante le fasi precedenti costringendomi a iterare su di esse più di una volta.
diff --git a/src/02_ciclo-vita/03_modelli-incrementali.md b/src/02_ciclo-vita/03_modelli-incrementali.md
new file mode 100644
index 0000000000000000000000000000000000000000..8a969aa7459a408e0a65d6aa4f1e41a51d7b2a67
--- /dev/null
+++ b/src/02_ciclo-vita/03_modelli-incrementali.md
@@ -0,0 +1,100 @@
+# Modelli incrementali
+
+Un modello incrementale è un particolare modello iterativo in cui nelle iterazioni è inclusa anche la consegna: questo permette di sviluppare il software a poco a poco, rilasciandone di volta in volta parti e componenti che costruiscano _incrementalmente_ il programma finito.
+
+Si noti la differenza tra incrementale e iterativo; si può parlare infatti di:
+
+- __implementazione iterativa__: dopo aver raccolto le specifiche e aver progettato il sistema, _iterativamente_ sviluppo i componenti, li integro nel prodotto finale, quindi consegno.
+- __sviluppo incrementale__: l'iteratività interessa tutte le fasi, comprese quelle di specifiche e realizzazione.  
+
+Lo sviluppo incrementale riconosce la criticità della variabilità delle richieste e la integra nel processo. 
+La manutenzione non è quindi più una particolarità ma è vista come normale e perfettamente integrata nel modello: in tal senso, la richiesta di una nuova feature o la correzione di un errore generano gli stessi step di sviluppo.
+
+## Modello prototipale
+
+Un particolare modello incrementale è quello protitipale: in questo modello viene introdotto il concetto di __protitipi usa e getta__ (_throw away_), interi programmi che vengono costruiti e poi vengono buttati via.
+
+Lo scopo del prototipo __non è consegnare__ un prodotto finito, ma __ricevere feedback__ dal cliente per essere sicuri di aver compreso a pieno i suoi requisiti, oppure testare internamente un'idea o uno strumento. Per questo motivo tali prototipi vengono costruiti fregandosene di correttezza, pulizia del codice, leggibilità eccetera.
+I protitipi possono dunque essere:
+
+- __pubblici__: per capire meglio i requisiti del cliente (vd. <a href="#b3">L3</a>);
+- __privati__: per esplorare nuovi strumenti, linguaggi, scelte per problemi difficili; inoltre, molto spesso una volta programmata una soluzione si capisce anche meglio il problema (_"do it twice"_).
+
+La tentazione coi prototipi pubblici può essere quella di consegnarli come prodotto finito, ma c'è il __rischio__ enorme di dover mantenere poi in futuro software non mantenibile, illeggibile e con altissimo debito tecnico.
+
+<a id="b3"></a>
+__Legge di Bohem (L3)__
+> La propotipizzazione riduce gli errori di analisi dei requisiti e di design, specialmente per le interfacce utente.
+
+## I problemi dei modelli incrementali
+
+Come già detto nessun modello è perfetto, e anche i modelli incrementali soffrono di alcuni problemi.
+
+Viene innanzitutto __complicato il lavoro di planning__: bisogna pianificare tutte le iterazioni e lo stato di avanzamento è meno visibile; inoltre, la ripetizione di alcune fasi richiede di avere sempre sul posto gli esperti in grado di eseguirle.
+Ad ogni iterazione, poi, dobbiamo rimettere mano a ciò che è stato fatto, in un processo che potrebbe non convergere mai a una versione finale.
+
+Ma cosa è un'iterazione, e quanto dura? Tagliare verticalmente sulle funzionalità non è infatti facile, soprattutto considerando che quando si consegna il prodotto esso dev'essere funzionante con tutti i layer necessari ed essere al contempo pensato per poter crescere con successivi attaccamenti. Ci sono dunque diversi rischi:
+
+- voler aggiungere troppe funzionalità nella prima iterazione;
+- overhead dovuto a troppe iterazioni;
+- avere un eccessivo overlapping tra le iterazioni: non si ha tempo di recepire il feedback dell'utente (es. Microsoft Office 2020 e 2019 vengono sviluppati contemporaneamente).
+
+### Pinball Life-Cycle
+
+![Pinball Life-Cycle](/assets/02_pinball-life-cycle.png)
+
+Il _"modello meme"_ del Pinball Life-Cycle, creato da Ambler come critica ai modelli incrementali, estremizza queste problematiche: l'ordine in cui faccio le attività è casuale, incoltrollabile. Qualunque passo è possibile dopo qualunque altro, e non si possono imporre vincoli temporal: il processo è __non misurabile__.
+
+Si tratta ovviamente di una visione eccessivamente pessimistica, ma spesso nelle aziende non specializzate l'iter di sviluppo assomiglia effettivamente a questo.
+
+## Modelli trasformazionali
+
+![Modelli trasformazionali](/assets/02_transformational-models.png)
+
+Diametralmente opposti all'incubo del Pinball Life-Cycle troviamo i __modelli trasformazionali__: tali modelli pretendono infatti di controllare tutti i passi e i procedimenti in __modo formale__.
+
+Partendo dai requisiti scritti in linguaggio informale, tali modelli procedono tramite una sequenza di __passi di trasformazione__ dimostrabili tutti formalmente fino ad arrivare alla versione finale.
+Essi si basano infatti sull'idea che se le specifiche sono corrette e i passi di trasformazione sono dimostrati allora ottengo un programma corretto, ovvero di sicuro aderente alle specifiche di cui sopra. Inoltre, la presenza di una storia delle trasformazioni applicate permette un rudimentale versioning, con la possibilità di tornare indietro a uno stato precedente del progetto semplicemente annullando le ultime trasformazioni fatte.
+
+![Trasformazioni formali](/assets/02_formal-transformations.jpg)
+
+Ad ogni passo si ottiene quindi un __protitipo__ che differisce dal prodotto finale per efficienza e completezza, ma che è possibile trasformare in un altro più efficiente e corretto. Non si tratta tuttavia di un processo totalmente automatico, anzi: ad ogni passo di "ottimizzazione", ovvero applicazione di una trasformazione, è richiesto l'intervento di un decisore umano che scelga che cosa ottimizzare.
+
+Viene quindi introdotto il concetto di __prova formale di correttezza__ delle trasformazioni applicate; per via di questo approccio molto matematico questo tipo di modelli è nella realtà applicato quasi solo negli ambienti di ricerca e produzione hardware.
+
+## <i>Meta</i>modello a spirale
+
+![Modello a spirale](/assets/02_spiral-model.png)
+
+Introduciamo ora un metamodello, ovvero un modello che ci permette di rappresentare e discutere di altri modelli (una sorta di framework).
+
+Nel metamodello a spirale l'attenzione è posto sui __rischi__, ovvero sulla possibilità che qualcosa vada male (decisamente probabile nell'ambiente di sviluppo software).
+Per questo motivo il modello è di tipo incrementale e pone l'accento sul fatto che non abbia senso fare lo studio di fattibilità una sola volta, ma ad ogni iterazione serva una decisione. Le fasi generali sono dunque:
+
+- Determinazione di obiettivi, alternative e vincoli
+- Valutazione alternative, identificazione rischi (decido se ha senso andare avanti)
+- Sviluppo e verifica
+- Pianificazione della prossima iterazione
+
+Nella figura il raggio della spirale indica i __costi__, che ad ogni iterazione aumentano fisiologicamente.
+
+### Variante _"win-win"_
+
+Esiste una variante al modello a spirale che fa notare come i rischi ad ogni fase non sono solo rischi tecnologici ma anche __contrattuali__ con il cliente. Ad ogni iterazione bisogna dunque trovare con esso un punto di equilibrio _win-win_ in entrambi le parti "vincono" (o hanno l'illusione di aver vinto), così da far convergere tutti su un obiettivo comune.
+
+## Modello COTS (Component Off The Shelf)
+
+![Modello COTS](/assets/02_cots.png)
+
+Vediamo infine un modello che si concentra molto sulla __riusabilità__: si parte dalla disponibilità interna o sul mercato di moduli preesistenti sui quali basare il sistema, e che è dunque necessario solo integrare tra di loro.
+
+Non si creda che si tratti di un approccio facile: questo modello di design necessita di far dialogare componenti che non necessariamente comunicano già nel modo voluto.
+
+Si tratta tuttavia di un modello di sviluppo diverso perché richiede attività diverse. In particolare:
+
+- _Analisi dei requisiti_
+- ___Analisi dei componenti___: prima di progettare considero la disponibilità di componenti che implementano una parte o tutte le funzionalità richieste;
+- ___Modifica dei requisiti___: stabilisco se il cliente è disposto ad accettare un cambiamento nei requisiti necessario per utilizzare un componente particolare;
+- ___Progetto del sistema col riuso di componenti___: occorre progettare il sistema per far interagire componenti che non necessariamente sono stati originariamente progettati per interagire;
+- _Sviluppo e integrazione_;
+- _Verifica del sistema_.
diff --git a/src/02_ciclo-vita/04_metodologie-agile.md b/src/02_ciclo-vita/04_metodologie-agile.md
new file mode 100644
index 0000000000000000000000000000000000000000..09363e258c624decf84f19cc8e470a41929eee63
--- /dev/null
+++ b/src/02_ciclo-vita/04_metodologie-agile.md
@@ -0,0 +1,60 @@
+# Metodologie Agili
+
+Finora i modelli visti erano di tipo prettamente descrittivo; vediamo ora dei modelli più prescrittivi, che dicano cioè che cosa fare effettivamente durante lo sviluppo.
+
+Le metodologie agili _"nascono dal basso"_, ovvero solitamente da chi sviluppa, per colmare un disagio prevalente nell'usare i metodi tradizionali. Per tale motivo, di tali metodologie esiste un...
+
+## [Manifesto](https://agilemanifesto.org/iso/it/manifesto.html)
+
+Nelle parole di Fowler e i suoi collaboratori, per migliorare il modo in cui sviluppiamo il software dobbiamo dare più importanza ad alcuni valori rispetto agli altri:
+
+- Gli __individui__ e la __collaborazione tra individui__ è più importante di processi e strumenti.
+- Il __software che funziona__ è più importante della documentazione ben fatta.
+- La __collaborazione con il cliente__ è più importante del contratto.
+- __Rispondere al cambiamento__ è più importante che seguire un piano.
+
+> LIBRO: __Agile!__ di Bertrand Meyer
+
+Come si vede, si tratta di un drastico cambio di rotta rispetto allo sviluppo tradizionale, che si evolve anche in un business model diverso: piuttosto che farsi pagare a programma finito, adesso gli sviluppatori vogliono farsi pagare a tempo di sviluppo, dando però la garanzia al cliente di lavorare durante tale periodo esclusivamente per lui e al massimo delle proprie capacità. Al rapporto confluttuale con il cliente, in cui ciascuno cerca di fregare l'altro, si sostituisce dunque una collaborazione più estesa in cui, come vedremo, anche il cliente diventa parte del team di sviluppo.
+
+Vediamo dunque adesso alcune delle più famose metodologie agili, mettendone in evidenza gli aspetti peculiari.
+
+## Lean Software
+
+Nato dal progetto di _Lean Manufactioring_ della Toyota, ha l'obiettivo di __ridurre gli sprechi__, ovvero quei prodotti e sottoprodotti che non vengono consegnati al cliente  (es. testing, prototipi...) e dunque non generano valore: essi possono essere ignorati.
+
+Un'altra idea interessante è quella di posticipare il più possibile le scelte vincolanti per aiutare a risparmiare risorse: più possibilità mi lascio aperte, più mi sarà facile adattarmi (a patto però che l'adattamento sia veloce).
+
+## Kanban
+
+![Kanban](/assets/02_kanban.jpg)
+
+L'obiettivo è qui invece di __minimizzare il lavoro in corso__ (work in progress), ovvero concentrarsi in ogni momento su una sola cosa in modo da evitare i continui _context switch_ che costituiscono una perdita di tempo.
+Le attività possono per esempio essere organizzate in una tabella con 5 colonne:
+
+- __backlog__: richieste dal cliente
+- __da fare__: attività da fare in questa iterazione
+- __in esecuzione__
+- __in testing__
+- __fatto__
+
+La tabella dà a colpo d'occhio informazioni sullo stato del progetto per tutti. Ogni __card__ (storia) è assegnata a uno sviluppatore (o coppia nel _pair programming_), in modo che nella colonna in esecuzione vi sia una sola card per sviluppatore (o coppia); qualora il lavoro di un altro blocchi il mio lavoro in qualche modo è poi mia responsabilità aiutarlo per rimuovere il blocco.
+
+## _Scrum_
+
+L'obiettivo è __fissare i requisiti__ durante le iterazioni (__brevi__, da 2 a 4 settimane), in modo da permettere agli sviluppatori di lavorare in pace senza doversi adattare continuamente a nuove richieste. Solo al termine di ogni iterazione, infatti, si permette al cliente di rimettere in discussione i requisiti.
+
+## Crystal
+
+Sebbene non sia molto apprezzata o usata, questa tecnica introduce l'interessante concetto di __comunicazione osmotica__. Nel modello a cascata la comunicazione è fatta tramite documenti rigidi, ed è settorializzata; in Crystal la conoscenza viene condivisa nel team tramite _"osmosi"_, in modo che tutti sappiano un po' di tutto.
+
+Questo rende il processo più robusto, perché l'assenza di una persona esperta in un campo non è più in grado di bloccare completamente i lavori. Il pair programming è in quest'ottica: tra i due componenti la conoscenza è condivisa; Crystal estende questo concetto all'intero team.
+
+Si capisce però facilmente che questa tecnica funziona solo con team piccoli (max 8-10 persone), sebbene altre metodologie agili (_SAFE_) tentino di scalarla anche a team più massicci.  
+
+## eXtreme Programming (XP)
+
+Si tratta di una tecnica a cui dedicheremo una trattazione più approfondita nella prossima lezione. Per il momento accontentiamoci di enunciarne i due motti:
+
+- __incrementa quindi semplifica__;
+- __sviluppo guidato dal test__ (_test-first_: prima testa poi sviluppa).
diff --git a/src/03_extreme-programming/00_index.md b/src/03_extreme-programming/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..9d59505c864df0d586ed60fc52b8203139a135f3
--- /dev/null
+++ b/src/03_extreme-programming/00_index.md
@@ -0,0 +1,10 @@
+# eXtreme Programming
+
+In questa lezione verranno trattati i seguenti argomenti.
+
+- [**Test Driven Development**](./01_tdd.md): _test-first + baby steps_
+- [**I fondamenti dell'XP**](./02_fondamenti.md): variabili, principi, figure e responsabilità
+- [**Tecniche**](./03_tecniche/): tutte e 13 le tecniche dell'XP
+- [**Relazione con il modello a cascata**](./04_cascata.md)
+- [**Documentazione**](./05_documentazione.md): carte CRC
+- [**Criticità**](./06_criticita.md): quando non utilizzare XP, critiche di Meyer e discussione sui _mesi uomo_.
diff --git a/src/03_extreme-programming/01_tdd.md b/src/03_extreme-programming/01_tdd.md
new file mode 100644
index 0000000000000000000000000000000000000000..23e7a8c22327270a2754943ebe2cdc2c4e2b77e0
--- /dev/null
+++ b/src/03_extreme-programming/01_tdd.md
@@ -0,0 +1,29 @@
+# Test Driven Development
+
+Il _test driven development_ (TDD) è una __tecnica di progettazione__ del software che mira a far emergere "dal basso" il design più semplice in grado di risolvere un dato problema. Non si tratta ne un'attività di verifica ne di scrittura del codice, quanto piuttosto un approccio alla scrittura di questi ultimi.
+
+Il TDD si fonda su due concetti fondamentali, esplicitati nella seguente citazione:
+
+> TDD = __test-first__ + __baby steps__
+
+Il significato di questa espressione è che per scrivere del codice che esalti la semplicità della soluzione è necessario __scrivere prima il test rispetto al codice__ (_test-first_) e procedere a __piccoli passi__ (_baby steps_), realizzando cioè piccole porzioni di codice, testandole e solo allora andando avanti. Questa tecnica mira infatti a stabilire un ciclo di _feedback istantaneo_: facendo piccoli passi e testando ogni volta ciò che si appena scritto è meno probabile buttare molto tempo su una soluzione che non funziona, e anche in caso di errore è più facile individuare cosa lo genera e come risolverlo.
+
+Per applicare questo approccio _test-driven_ allo sviluppo effettivo di software, il TDD ha sviluppato il seguente "mantra": __rosso__, __verde__, __refactoring__. Quando si scrive codice bisogna infatti seguire le seguenti tre fasi:
+
+- Ogni volta che si deve aggiungere una feature __si scrive prima il test__ che la provi; non essendo ancora stata sviluppata, tale test dovrà fallire (<span style="Color: red">rosso</span>).
+
+- Si cerca poi di __soddisfare il test il più velocemente possibile__, facendolo diventare <span style="color: green">verde</span>. Si ottiene così del codice corretto ma probabilmente molto brutto, quasi come fosse una bozza: tale codice serve però come feedback del fatto che l'algoritmo scelto funziona.
+
+- Si compie infine un'azione di __refactoring__ (_fattorizzazione_), ovvero si riorganizza e si riscrive il codice in modo da renderlo migliore assicurandosi però che il test continui ad essere soddisfatto (in questa fase dobbiamo rimanere in uno stato di <span style="color: green">verde</span>).
+
+Questa ciclo in tre fasi va ripetuto con una cadenza frequente, ogni 2-10 minuti: ciò obbliga a concentrarsi su compiti semplici evitando così di perdersi in costruzioni software complicate che magari non funzionano neanche. Si preferisce invece prima fare qualche piccolo progresso (_increment_) e poi semplificare per migliorare il codice (_simplify_).
+
+È importante inoltre capire perché quel passaggio intermedio, la "bozza" menzionata al secondo punto dell'elenco precedente, è tanto importante: concentrarsi in primo luogo sulla creazione di una base funzionante permette subito di capire se si è scelta la strategia giusta per risolvere il problema corrente. Scrivere direttamente il codice "in bella" impiegherebbe molto più tempo e potrebbe non produrrebbe neanche un codice funzionante, siccome maggiore è la complessità del codice che si scrive più è probabile commettere errori.
+
+In virtù di quanto appena detto, l'uso del TDD come tecnica di progettazione garantisce inoltre due importanti vantaggi:
+
+- Spesso capita di scrivere codice difficilmente testabile: scrivere il test prima e il codice dopo aiuta invece a progettare prodotti la cui correttezza può essere provata.
+
+- Scrivere prima i test aiuta a definire chiaramente le interfacce del programma e come queste comunicano tra di loro, mentre se non dovessimo farlo potremmo avere delle dipendenze complicate da rimuovere.
+
+Durante il testing ci si pone dal __punto di vista del cliente__: la tecnica TDD ci permette dunque di osservare il codice da molteplici prospettive (sviluppatore e cliente), cosa che contribuisce ovviamente alla creazione di un prodotto migliore.
diff --git a/src/03_extreme-programming/02_fondamenti.md b/src/03_extreme-programming/02_fondamenti.md
new file mode 100644
index 0000000000000000000000000000000000000000..1e29304fc9838889defd1c8fdfeb320b2a6accd7
--- /dev/null
+++ b/src/03_extreme-programming/02_fondamenti.md
@@ -0,0 +1,116 @@
+# Fondamenti
+
+Ora possiamo iniziare a parlare di Extreme Programming (XP), una tecnica di sviluppo agile nata tra la fine degli anni '90 e l'inizio degli anni 2000 dalla mente di Kent Beck, che la ideò nell'ambito di un progetto Chrysler.
+
+## Variabili
+
+Secondo Beck, durante lo sviluppo di software le principali variabili sono:
+
+- __portata__: la quantità di funzionalità da implementare, una variabile delicata dal valore _mutevole_ poiché il numero di funzionalità richieste può cambiare nel corso dello sviluppo;
+- __tempo__: il tempo che si può dedicare al progetto;
+- __qualità__: la qualità del progetto che si vuole ottenere, principalmente relativa a correttezza e affidabilità;
+- __costo__: le risorse finanziare che si possono impegnare per il progetto.
+
+Queste 4 variabili __non sono indipendenti__ tra di loro, in quanto cambiare una influenza automaticamente le altre, in positivo o in negativo. Ponendo quindi che la qualità non sia negoziabile (il software deve funzionare) bisognerà lavorare sulle altre, specialmente bilanciando costo e tempo.
+
+Nel panorama classico di sviluppo la portata era definita in modo rigido dal cliente, che richiedeva certe funzionalità non negoziabili e pagava lo sviluppatore a progetto completo.
+Con l'XP si stravolge invece la prospettiva: __il costo è orario__, il tempo disponibile non è fisso ma pari al tempo richiesto per lo sviluppo e la portata viene ricalcolata durante il progetto, essendo così l'unica variabile a variare effettivamente. Si tratta di un approccio _incrementale_ che mira ad avere sempre un prodotto consegnabile se il cliente decide di essere soddisfatto dello sviluppo: non si fa aspettare il cliente per dargli tutto il lavoro in un colpo solo, ma questo viene consegnato una parte alla volta. Oltre ad alleggerire la pressione sullo sviluppatore, questo approccio è utile per due motivi:
+
+- Il cliente è certo che lo sviluppatore si sia dedicando al progetto siccome vede il prodotto crescere a poco a poco.
+- Dà la possibilità al cliente di avere comunque qualcosa in mano se ad un certo punto vuole interrompere la collaborazione.
+- Permette al cliente di cambiare idea sulla portata e sulle funzionalità richieste in corso d'opera, bandendo la rigidità dei documenti di specifica.
+
+Tutti questi aspetti permettono di creare un rapporto molto meno conflittuale tra cliente e sviluppatore, cosa che crea le basi per una maggiore collaborazione tra le due parti.
+
+## Principi
+
+Parliamo ora un po' dei fondamenti della filosofia XP, confrontandoli con quanto veniva prescritto nell'ambiente di sviluppo classico. I principi dell'ingegneria del software classica erano infatti i seguenti:
+
+- __Separazione degli interessi__ (_aspects_ o _concerns_): separare tempi, responsabilità e moduli, ovvero tutte le varie viste o le varie dimensioni su cui si deve affrontare il problema.
+- __Astrazione e modularità__: bisogna usare le giuste astrazioni che ci permettono di dominare i problemi complessi (possono essere i diversi linguaggi di programmazione, linguaggi di descrizione o vari altri costrutti).
+- __Anticipazione del cambiamento__ (_design for change_): in fase di progettazione il programmatore deve pensare a come potrebbe cambiare il prodotto, accomodando la possibile  aggiunta di requisiti che il cliente magari non aveva neanche pensato; bisogna stare attenti però, perché spesso questo concetto complica arbitrariamente la progettazione e lo sviluppo, rischiando di far perdere molto tempo su cose che al cliente potrebbero non servire: può essere un'idea migliore partire da qualcosa di semplice ed incrementare man mano.
+- __Generalità__: per rendere più semplice la modifica e l'espansione futura è necessario scrivere interfacce molto generali ai sistemi che costruiamo.
+- __Incrementalità__: lo sviluppo avviene incrementalmente, un pezzetto alla volta.
+- __Rigore e formalità__: è importante essere rigidi e specifici sia nella comunicazione che nella descrizione dei requisiti.
+
+Sebbene non butti via tutti questi principi ma ne erediti invece alcuni per adattarli alle proprie esigenze (specialmente la _separazione degli interessi_, che viene data per scontata), l'XP pone l'accento su altri aspetti, ovvero:
+
+- __Feedback rapido__: bisogna mantenere un costante flusso di feedback; questo viene dato dai test, dai colleghi ma anche dal cliente, che dev'essere continuamente consultato sullo stato dei lavori. Tra le iniziative che favoriscono un veloce ciclo di feedback c'è lo _standup meeting_, una riunione mattutina fatta in piedi in cui ciascuno descrive in poche parole cosa ha fatto il giorno precedente e cosa intende fare oggi.
+- __Presumere la semplicità__: non bisogna complicare senza motivo né il codice, che dev'essere scritto con in mente ciò che serve a breve termine e non in un futuro remoto, né le relazioni tra colleghi, che non devono essere eccessivamente gerarchiche (tutti dovrebbero avere compiti molto simili); in generale si dovrebbe semplificare il più possibile in tutti gli ambiti del progetto.
+- __Accettare il cambiamento__: non ci si deve aspettare che il software sia immutabile; al contrario, deve essere dato per scontato il concetto di _flessibilità_ e _malleabilità_, ovvero che il cliente vorrà fare cambiamenti sia dopo che durante lo sviluppo del prodotto.
+- __Modifica incrementale__: ritornando al concetto di baby steps, ogni iterazione di sviluppo dovrebbe essere breve e le funzionalità introdotte piuttosto piccole; questa regola si applica tuttavia a tutti gli ambiti del progetto, tra cui la gestione del team: ovvero non bisognerebbe mai aggiungere più di una persona alla volta al gruppo di lavoro, in quanto aggiungerne di più potrebbe portare a passare più tempo ad istruirle che a sviluppare.
+- __Lavoro di qualità__: bisogna ovviamente ottenere un buon prodotto, ma per fare ciò la prospettiva cambia in favore dello sviluppatore, al quale si deve garantire un ambiente di lavoro salutare e un certo benessere; la fidelizzazione dei programmatori è importante perché più si trovano bene e meglio lavorano.
+
+I due punti più in contrasto sono il presumere la semplicità e l'anticipazione del cambiamento: ci sembra infatti più previdente pianificare per il futuro e anticipare eventuali cambiamenti, ma come vedremo nel prossimo paragrafo talvolta questo può essere controproducente.
+
+### Presumere la semplicità vs anticipazione del cambiamento
+
+XP mette davanti la semplicità all'anticipazione del cambiamento: non si scrive in anticipo codice che si pensa servirà in futuro. Questo non significa che non si stia progettando per il futuro, ma solo che questo non è il primo aspetto da guardare: il primo aspetto è la semplicità, ovvero fare le cose nella maniera più chiara possibile.
+
+Non pianificare per il futuro sembra rischioso: secondo uno studio condotto da Bohem nel 1976 viene ipotizzata una curva esponenziale per il corso delle modifiche all'aumento dell'avanzamento del progetto; più il progetto avanza più è costoso modificarlo, motivo per cui sembra necessario accomodare il cambiamento futuro in modo da ridurre tale costo. \
+Al contrario, XP presuppone una curva di tipo logaritmico che tenda ad un asintoto: passato un certo punto nello sviluppo il costo per le modifiche non subisce più cambiamenti sensibili, per cui non ha senso fasciarsi la testa in anticipo in quanto un codice semplice è relativamente facile da modificare.
+
+![Curve di costo: XP vs tradizionale](/assets/03_cost-curves.png)
+
+Va inoltre considerato che Bohem parlava in realtà di cost-to-fix, non del costo per la modifica in sé; inoltre la sua statistica era poco affidabile poiché era stata costruita a partire da pochi dati. La curva esponenziale da lui descritta è stata poi successivamente ritrattata per accomodare il fatto che se un errore avviene in una fase affligge solo le successive, e non le precedenti.
+
+## Figure e responsabilità
+
+Al fine di organizzare il lavoro, XP individua diverse figure che partecipano allo sviluppo:
+
+- Cliente: colui che richiede funzionalità e conosce il dominio applicativo.
+- Sviluppatore: colui che sviluppa concretamente scrivendo codice.
+- Manager: colui che amministra lo sviluppo con uno sguardo generale.
+
+È interessante l'inclusione del cliente nel contesto dello sviluppo: esso non è più soltanto il committente ma ha un ruolo attivo nel lavoro, potendo cioè contribuire alla riuscita del progetto anche e soprattutto in virtù della già citata conoscenza del dominio applicativo.
+
+Ciascuna di tali figure ha responsabilità e diritti riassunti nella seguente tabella (_manager e cliente sono accorpati perché hanno grossomodo gli stessi compiti_):
+
+<table style="margin: 20px">
+    <tr style="text-align: center; background-color: #DEDEDE">
+        <th>Soggetto</th>
+        <th>Ha responsabilità di decidere...</th>
+        <th>Ha diritto di...</th>
+    </tr>
+    <tr>
+        <td style="padding-left: 15px; padding-right: 15px; text-align: center;">
+            <b>Manager/Cliente</b>
+        </td>
+        <td style="padding-left: 5px; padding-right: 15px; padding-top: 10px">
+            <ul>
+                <li>Portata del progetto, ovvero le funzionalità da realizzare</li>
+                <li>Priorità tra funzionalità e loro <i>business value</i></li>
+                <li>Date dei rilasci, anche nel caso di release incrementali</li>
+            </ul>
+        </td>
+        <td style="padding-left: 5px; padding-right: 15px; padding-top: 10px">
+            <ul>
+                <li>Sapere cosa può essere fatto, con quali tempi e quali costi</li>
+                <li>Vedere progressi nel sistema, provati dai test da lui definiti (<i>trasparenza</i>)</li>
+                <li>Cambiare idea, sostituendo funzionalità o cambiandone le priorità a intervalli di tempo fissi (<i>fine del ciclo di sviluppo incrementale</i>)</li>
+            </ul>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding-left: 5px; padding-right: 15px; text-align: center;">
+            <b>Sviluppatore</b>
+        </td>
+        <td style="padding-left: 5px; padding-right: 15px; padding-top: 10px">
+            <ul>
+                <li>Stime dei tempi per le singole funzionalità (<i>no deadline imposte dall'alto</i>)</li>
+                <li>Scelte tecnologiche e loro conseguenze, ovvero <i>come</i> si realizzano le funzionalità richieste</li>
+                <li>Pianificazione dettagliata delle iterazioni</li>
+            </ul>
+        </td>
+        <td style="padding-left: 5px; padding-right: 15px; padding-top: 10px">
+            <ul>
+                <li>Ricevere dei requisiti chiari (<i>casi d'uso</i>) con priorità per le varie funzionalità</li>
+                <li>Cambiare le stime dei tempi man mano che il progetto procede e il contesto di sviluppo cambia</li>
+                <li>Identificare funzionalità pericolose o troppo difficili da realizzare</li>
+                <li>Produrre software di qualità, per il quale deve godere di un certo benessere</li>
+            </ul>
+        </td>
+    </tr>
+</table>
+
+Come si vede, per migliorare la fiducia tra sviluppatore e cliente sono necessari due requisiti: un certo grado di _trasparenza_ da parte di chi sviluppa, ottenuta dall'uso delle contiene release incrementali per mostrare come sta evolvendo il sistema, e una certa dose di _pazienza_ da parte del cliente, che deve accettare di lasciare allo sviluppatore la facoltà di decidere come si realizzano le funzionalità e di cambiare le prospettive temporali di sviluppo qualora fosse necessario.
diff --git a/src/03_extreme-programming/03_tecniche/00_index.md b/src/03_extreme-programming/03_tecniche/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..8f67da098a69223bdfe4cdae31d6199e9c02f421
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/00_index.md
@@ -0,0 +1,21 @@
+# Tecniche
+
+L'eXtreme Programming fornisce una serie di metodologie pratiche per poter garantire tutto ciò che è stato descritto fino ad ora. Lo schema sottostante le descrive mettendole in relazione tra loro in modo che i vari aspetti negativi delle diverse pratiche siano compensati dagli aspetti positivi di quelle in relazione con loro; in sostanza abbiamo un mix perfetto di attività organizzate in modo da garantire i buoni principi di cui sopra.
+
+![Relazione delle tecniche XP tra loro](/assets/03_approccio.png)
+
+## Elenco
+
+1. [**Planning game**](./01_planning-game.md)
+2. [**Brevi cicli di rilascio**](./02_brevi-cicli.md)
+3. [**Uso di una metafora**](./03_metafora.md)
+4. [**Presumere la semplicità**](./04_semplicita.md)
+5. [**Testing**](./05_testing.md)
+6. [**Refactoring**](./06_refactoring.md)
+7. [**Pair programming**](./07_pair-programming.md)
+8. [**Proprietà collettiva**](./08_proprieta-collettiva.md)
+9. [**Integrazione continua**](./09_integrazione-continua.md)
+10. [**Settimana da 40 ore**](./10_settimana.md)
+11. [**Cliente sul posto**](./11_cliente-posto.md)
+12. [**Standard di codifica**](./12_standard-codifica.md)
+13. [***They're just rules***](./13_just-rules.md)
diff --git a/src/03_extreme-programming/03_tecniche/01_planning-game.md b/src/03_extreme-programming/03_tecniche/01_planning-game.md
new file mode 100644
index 0000000000000000000000000000000000000000..ba2701aab708883027cd27470ff08f222cb153a2
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/01_planning-game.md
@@ -0,0 +1,136 @@
+# Planning game
+
+È l'attività di pianificazione che viene fatta all'inizio di ogni iterazione e serve per "congelare" il sottoinsieme di requisiti sul quale il team lavorerà per le prossime ~2 settimane.
+
+Si parte dalle richieste del cliente espresse tramite _user stories_, una versione semplificata degli _use case_ degli UML; esse hanno come soggetto sempre un ruolo specifico nell'azienda del cliente e descrivono una funzionalità. Ogni _user story_ è dunque composta da tre parti:
+
+- il __soggetto__, ovvero il ruolo dell'utente nell'azienda (può anche essere esterno);
+- l'__azione__ che vuole eseguire il soggetto;
+- la __motivazione__ che spinge il soggetto a portare avanti l'azione descritta.
+
+Esempi di _user stories_ potrebbero essere:
+
+- > _Da bibliotecario, voglio poter visualizzare dove si trova un particolare libro in modo da poterlo reperire per i clienti._
+- > _Da utente della biblioteca, voglio poter visualizzare lo stato di un libro per poterlo prendere in prestito._
+
+Lo scopo del planning game è dunque quello di determinare quali funzionalità saranno presenti nel prossimo rilascio combinando priorità commerciali e valutazioni tecniche: questo richiede una collaborazione da parte del cliente, che come vedremo sarà presente in loco al momento della decisione.
+
+## Procedura
+
+Quest'attività di pianificazione si divide fondamentalmente in tre fasi:
+
+1. All'inizio il cliente compila le __carte__, nient'altro che pezzetti di carta volutamente piccoli per impedire di scriverci troppo. Su ogni carta è presente:
+    - un identificativo numerico;
+    - una breve frase che descrive uno scenario d'uso;
+    - un caso di test che funge da test d'accettazione della funzionalità: si tratta in sostanza di un paio di esempi, di solito uno positivo e uno negativo, che devono essere soddisfatti per ritenere completa la feature;
+    - il valore di business che la funzionalità ha per il cliente.
+
+2. Per ogni carta il team di sviluppatori fa dunque una __stima__ del tempo necessario a realizzarla: raggiunta una stima comune questa viene scritta sulla carta e servirà per confrontare tale previsione con il tempo effettivamente impiegato, di cui si tiene conto sul suo retro. Per ciascuna carta uno sviluppatore assume infatti il ruolo di _tracker_, impegnandosi cioè a tracciare lo stato di avanzamento della relativa funzionalità durante le due settimane (_es. quante feature fatte, quanti bug segnalati, etc._).
+
+3. Il manager decide quindi sulla base di queste informazioni __quali carte verranno implementate__ durante prossima iterazione: per questa operazione prende in considerazione il valore delle feature, le dipendenze tra l'una e l'altra e una serie di altri fattori. Se, come dovrebbe essere, le varie funzionalità rappresentate nelle carte sono indipendenti, il manager può compiere questa scelta calcolando il rapporto tra il valore e il tempo stimato e usarlo per ordinare le carte: tuttavia l'operazione richiede una certa dose di ragionamento e non è mai così meccanica.
+
+![Card user story](/assets/03_user-story-card.png)
+
+## Le stime
+
+Abbiamo detto che le stime dei tempi vengono fatte dall'intero team in accordo; tuttavia il team è composto da persone diverse che quindi faranno stime diverse in funzione dell'esperienza e delle proprie capacità. È tuttavia importante raggiungere una stima accettata da tutti in quanto il team si impegna a rispettarla: se viene deciso che il tempo per una data scheda è di qualche ora e questa viene assegnata a uno sviluppatore che aveva fatto una stima di qualche giorno allora quest'ultimo si troverà in difficoltà nel portare a termine il compito; per questo motivo è importante il contributo anche degli sviluppatori junior o inesperti.
+
+Al di là del problema del raggiungimento di una stima comune, per il quale vedremo delle tecniche specifiche, ci possono essere una serie di problemi di stima legati alla funzionalità in sé. Potremmo infatti avere stime:
+
+- __molto differenti__ (ore vs giorni): in questo caso, è possibile che la carta non sia descritta o compresa correttamente; se uno sviluppatore stima poche ore e un altro qualche giorno c'è qualche problema.  in conclusione è necessario trovare un punto di incontro.
+
+- quasi uniformi, ma __molto alte__: se la stima supera il tempo di iterazione potrebbe essere che la storia sia troppo ampia. Non si può neanche iniziarla in questo ciclo e continuarla nel prossimo: se alla fine dell'iterazione non ho portato a termine il lavoro prefissato è come se non l'avessi fatto (anche se magari era stato completato all'80%), perché il cliente non lo vede nella release e tale lavoro non è dunque dimostrabile. Per ovviare a questo problema si può fare lo __splitting__ delle carte, ovvero scomporre una carta in più carte in modo da dividere il problema in sotto-problemi.
+
+- non uguali ma __simili__: non bisogna prendere la più bassa, alta o la media. Come abbiamo già detto, secondo XP bisogna arrivare ad un accordo in modo tale che chiunque nel team si riconosca nella stima effettuata.
+
+Oltre a ciò, la fase di stima dei tempi si porta dietro diverse problematiche intrinseche, tra cui:
+
+- __perdita di tempo__: per accordarsi su una stima comune si spende molto tempo (troppa comunicazione);
+
+- __effetto àncora__ (anchoring effect): si tratta di un effetto che si verifica quando bisogna assegnare un valore ad una quantità ignota. Poiché il cervello umano è più bravo a ragionare per relazioni piuttosto che per assoluti, una volta che viene fatta la prima stima numerica questa definisce l'ordine di grandezza delle stime successive, facendo cioè da punto di riferimento da cui è molto difficile distanziarsi: nel nostro caso quando il team si riunisce per fare delle stime e il primo membro dà la sua opinione, tutte le stime successive orbiteranno intorno ad essa. Tale effetto impedisce di fare una stima che prenda obiettivamente in considerazione le sensazioni di tutti i membri del team, e va dunque assolutamente evitato.
+
+Per evitare questi problemi e semplificare il processo di stima si sono sviluppati diversi processi, che data la loro natura giocosa aumentano anche l'engagement degli sviluppatori in questa fase di pianificazione.
+
+## [Planning poker](https://en.wikipedia.org/wiki/Planning_poker)
+
+![Planning poker](/assets/03_planning-poker.jpg)
+
+Una per una vengono presentate brevemente le carte con le user stories facendo attenzione a non fare alcun riferimento alle tempistiche in modo da non creare subito un effetto àncora: in questa fase il team può fare domande, chiedere chiarimenti e discutere per chiarire assunzioni e rischi sulla user story, ma deve stare molto attento a non fare alcuna stima.
+
+Dopodiché ogni componente del team sceglie una carta dal proprio mazzo personale per rappresentare la propria stima e la pone coperta sul tavolo: su queste carte si trovano una serie di numeri senza unità di misura che vanno da 0 a 100 seguendo un andamento non uniforme; il loro scopo è quello di definire un'ordine di grandezza piuttosto che una stima precisa. Ci sono anche delle carte particolari, ovvero:
+
+- il punto di domanda indica che non si è in grado di dare una stima
+- la tazza di caffè indica che la riunione è andata troppo per le lunghe ed [è necessaria una pausa](https://www.youtube.com/watch?v=-gAlDOcXgyM).
+
+Fatta questa prima stima _blind_ le carte vengono girate contemporaneamente: idealmente vi dovrebbe essere l'unanimità sulla stima. Se così non è chi ha espresso le stime più basse e più alte ha ~1 minuto per motivare la propria scelta in modo da cercare di convincere gli altri; si noti che agli altri componenti del team non è concesso parlare per evitare di perdere troppo tempo! \
+Finito questo momento di consultazione tutti i membri del team fanno una nuova stima e si continua così finché non si raggiunge l'unanimità; solitamente le votazioni convergono dopo un paio di round.
+
+Ma qual'è l'unità di misura su cui si fanno le stime? Dipende: essa può essere scelta prima o dopo aver trovato un accordo; possono essere ore, giorni o pomodori (un pomodoro è formato da 25 minuti senza alcuna distrazioni,e dopo c'è una pausa). Ovviamente non si può pretendere di lavorare delle ore senza alcuna distrazione, per cui in queste stime si considera anche un certo __slack time__, ovvero un tempo cuscinetto per che comprende il "tempo perso" a causa di distrazioni.
+
+## [Team Estimation Game](https://agilelearninglabs.com/2012/05/how-to-play-the-team-estimation-game/)
+
+Si tratta di un metodo un po più complesso articolato in 3 fasi e basato sul confronto tra i diversi task piuttosto che sulla stima numerica: esso si basa infatti sull'idea che sia semplice stabilire se un task sia più facile o più difficile di un altro, mentre è molto più complicato capire di quanto sia più facile/difficile. L'idea è dunque quella di splittare in fasi questa cosa di dover dare un valore al task considerandone sempre di più difficili per arrivare a fare una buona stima.
+
+### __<big>PRIMA FASE</big>__
+
+![Prima fase del team estimation game](/assets/03_team-estimation-1.jpg)
+
+Si fa una pila con le storie e si mette la prima carta al centro del tavolo. I developer si mettono in fila e uno alla volta eseguono queste azioni:
+
+- il __primo della fila estrae una carta della pila__, la legge ad alta voce e la __posiziona__ a sinistra (più semplice), a destra (più complicata) o sotto (equivalente) la carta già presente sul tavolo.
+- il __prossimo developer__ può:
+  - __estrarre una nuova carta dalla pila__ e __posizionarla__ secondo le stesse regole, eventualmente inserendola in mezzo a due colonne già presenti;
+  - __spostare una carta precedentemente posizionata__ commentando la motivazione della sua scelta; può ovviamente succedere che tale carta venga rispostata nella sua posizione originale, ma dopo un po' si troverà un accordo sulla difficoltà del relativo task.
+
+Terminata la pila avremo le carte disposte sul tavolo in colonne di difficoltà comparabile, ordinate dalla meno difficile (sinistra) alla più difficile (destra).
+Oltre ad aver ridotto la comunicazione (molte carte non saranno contestate), usando questa tecnica abbiamo evitato anche l'effetto àncora rendendolo relativo: l'assenza di valori precisi evita il rischio di influenzare eccessivamente gli altri. Inoltre a differenza del planning poker si può tornare sulle proprie decisioni, cosa che favorisce un continuo adattamento e ripensamento delle stime.
+
+### __<big>SECONDA FASE</big>__
+
+Si cerca dunque di quantificare le _distanze_ tra le carte.
+
+![Seconda fase del team estimation game](/assets/03_team-estimation-2.jpg)
+
+Ci si mette di nuovo in coda davanti al tavolo con il mazzo di carte del planning poker (uno solo, non uno per persona) e __si cerca di etichettare le colonne in base alle difficoltà__.
+
+Si posiziona la prima carta (solitamente si parte da 2 perchè magari nella prossima iterazione può esserci qualcosa di ancora più facile) sopra la prima colonna.
+
+Quindi:
+- il __primo sviluppatore__ prende il valore successivo e lo posiziona sulla prima colonna che pensa abbia quel valore (rispetto al 2), oppure lo posiziona tra due colonne se pensa che sia un valore di difficoltà intermedio tra le due.
+- lo __sviluppatore successivo__ può invece:
+    - __estrarre una carta__ dal mazzo e __posizionarla__ secondo le regole di prima (la prima colonna che pensa abbia un particolare valore di difficoltà);
+    - __spostare una carta__ con un valore precedentemente posizionato, commentando la motivazione dello spostamento;
+    - __passare__ il turno, nel caso in cui non ci siano più carte nella pila e non si vogliono spostare altre carte.
+
+È possibile avere delle carte in cui sopra non c'è nessun numero, queste saranno assimilate alla colonna alla loro sinistra. 
+
+Al termine di questa fase, la situazione sarà simile alla seguente:
+
+![Fine seconda fase del team estimation game](/assets/03_fine-seconda-fase-estimation-game.jpg)
+
+### __<big>TERZA FASE</big>__
+
+Si stima il tempo in ore/uomo di una delle carte più semplici e successivamente si calcolano tutte le colonne in proporzione alla prima.
+Ma questa fase è davvero cosi utile? Nella pratica si è visto che __è inutile valutare il lavoro fatto in ore/uomo__, anche perchè con il passare del tempo la taratura può variare.
+
+Nella prossima sezione parliamo di come la nozione di __velocity__ risolve questo problema. 
+
+## Velocity
+È importante riuscire a stimare la _velocità_ con la quale stiamo avanzando. 
+In fisica la velocità è data dal rapporto tra la distanza percorsa e il tempo per percorrerla. 
+Questa proprietà può essere usata anche nella gestione dello sviluppo agile: il numeratore è il punteggio delle storie mentre il denominatore è la lunghezza dell'iterazione (assimilabile in un'unità di tempo).
+
+La ___velocity___ nel mondo agile è quindi il __numero di story point__ guadagnati nell'arco dell'iterazione corrente.
+
+Essa riesce quindi a dare un'idea di quanto si è riusciti a fare in termini di complessità astratta.
+Se per esempio il team è riuscito a fare 50 punti nella iterazione appena finita, è ragionevole prefissarsi di fare almento 50 punti nell'iterazione successiva.
+
+La velocity __non può essere usata__ per dare __premi__, per __confrontare__ team diversi o __punire__ in caso di diminuzione, però si adatta al modo diverso degli sviluppatori di gestire le stime e dal fatto che si tende a sottostimare o sovrastimare carte diverse.
+
+All'atto di aggiungere una persona questa metrica deve inizialmente rimanere invariata, per prevedere la sua formazione; se la rimuovo ci sarà una perdita di produttività.
+
+La velocity __non deve considerare le storie lasciate incompiute__, quindi anche se l'ho completata al 90% devo considerarla come se non l'avessi fatta.
+Inoltre, __non deve essere__ imposta: la velocity di un team è fissa e non può essere aumentata.
+
+Esiste un movimento chiamato ___no estimates___, che evita al team tutta la parte delle stime. 
+Dall'esperienza del prof. Bellettini, però, questa metodologia funziona in team molto maturi che sono in grado di guidare il ciente a formulare storie simili in termini di difficoltà, avendo tutti una misura standard per le storie.
diff --git a/src/03_extreme-programming/03_tecniche/02_brevi-cicli.md b/src/03_extreme-programming/03_tecniche/02_brevi-cicli.md
new file mode 100644
index 0000000000000000000000000000000000000000..ce3e001f56d18e573a20386a99de01de1e890eac
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/02_brevi-cicli.md
@@ -0,0 +1,5 @@
+# Brevi cicli di rilascio
+Per ridurre i rischi, la vita e lo sviluppo dell'applicazione sono scanditi dai rilasci di diversioni del prodotto funzionanti, di solito uno ogni due settimane (come abbiamo visto in scrum con il _freez_, ma con un tempo di rilascio minore). 
+È necessario avere abbastanza tempo per sviluppare qualcosa di concreto, e il cliente per poter pensare alle richieste che ha fatto e stabilire se ha bisogno di modifiche.
+
+Betrand Meyer, nel suo libro _"Agile! The Good, the Hype and the Ugly"_, definisce questa idea _"brillante"_, _"forse l'idea agile con l'influenza e impatto maggiore nell'industria"_. 
diff --git a/src/03_extreme-programming/03_tecniche/03_metafora.md b/src/03_extreme-programming/03_tecniche/03_metafora.md
new file mode 100644
index 0000000000000000000000000000000000000000..6103d5df0029d6615ad68d05882275df1ec58085
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/03_metafora.md
@@ -0,0 +1,5 @@
+# Uso di una metafora
+
+Definire un __nuovo vocabolario__ con cui parlare con l'utente (tecnica _non informatica_) ma anche ai nuovi sviluppatori.
+Serve per permettere una nominazione di classi e metodi omogenei e fornire una vista d'insieme.
+Siccome non c'è una vera documentazione in XP, possiamo usare queste metafore come una vista d'insieme, quindi sostituire in parte l'architettura del sistema, e far capire gli elementi fondamentali, il loro scopo e le relazioni fra loro. 
\ No newline at end of file
diff --git a/src/03_extreme-programming/03_tecniche/04_semplicita.md b/src/03_extreme-programming/03_tecniche/04_semplicita.md
new file mode 100644
index 0000000000000000000000000000000000000000..5b0133f8a1386414f6fe524cbb06bd7b76265e88
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/04_semplicita.md
@@ -0,0 +1,7 @@
+# Semplicità di progetto
+Ovvero l'___arte di massimizzare il lavoro non fatto___, o da non fare.
+Non è necessario riscrivere cose già esistenti e consolidate. 
+
+Uno slogan tipico è __KISS__: __Keep It Simple, Stupid__.
+
+Questo punto si contrappone al _design for change_ che viene invece visto come un appesantimento inutile, perchè una feature che aggiungiamo può essere scartata dal cliente.
diff --git a/src/03_extreme-programming/03_tecniche/05_testing.md b/src/03_extreme-programming/03_tecniche/05_testing.md
new file mode 100644
index 0000000000000000000000000000000000000000..70e613440e3b8d8b3268bb68b778c0df9a01dd32
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/05_testing.md
@@ -0,0 +1,13 @@
+# Testing
+
+È consolidato su due fronti:
+- i clienti scrivono i __test di accettazione__ (o funzionali) sulle schede per aumentare la loro fiducia nel programmi;
+- i programmatori scrivono i __test di unità__ perché la fiducia nel codice diventi parte del programma stesso.
+
+Nell'XP ogni aspetto viene massimizzato, ma in particolare il testing viene esasperato di più in quanto, oltre ad essere molto importante, molti altri aspetti si basano su di esso (vedi la figura all'inizio della sezione). 
+Ha il ruolo di __rete di protezione__ in tutte le fasi: ogni cambiamento è verificabile tramite i test.
+
+Il testing aiuta molto anche quando non si parte da zero con il programma, ma quando si deve modificare un programma proprietario precedentemente sviluppato anche in modalità non agile.
+Prima di apportare modifiche al codice scrivo i test e solo successivamente procedo, in modo da non causare problemi.
+
+Un altro concetto importante è che i test dovrebbero __coprire tutte le righe di codice__.
\ No newline at end of file
diff --git a/src/03_extreme-programming/03_tecniche/06_refactoring.md b/src/03_extreme-programming/03_tecniche/06_refactoring.md
new file mode 100644
index 0000000000000000000000000000000000000000..f5e97b057adee31b63f4eea64f154774638a0ff7
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/06_refactoring.md
@@ -0,0 +1,13 @@
+# Refactoring
+
+Anche da novizi, non bisogna avere paura di apportare modifiche che semplificano il progetto: bisogna avere coraggio.
+
+Il refactoring è l'operazione che __modifica solo le proprietà interne__ del software, __non le funzionalità__.
+L'obiettivo è eliminare l'entropia generata dalle continue modifiche e aggiunte.
+
+Il refactoring deve essere __graduale e continuo__ in modo da poter aggiungere funzionalità in maniera semplice. 
+Chiaramente, in caso di ristrutturazioni architetturali di grosse dimensioni di sistemi legacy non è sempre possibile procedere in questa maniera.
+
+Parti di codice non stimolate da test non sono utili ai fini della soluzione: o si aggiungono test per gestire i casi specifici, altrimenti si possono rimuovere _in toto_.
+
+Il refactoring è una delle tecniche più __importanti__ e __fondamentali__ dell'XP.
diff --git a/src/03_extreme-programming/03_tecniche/07_pair-programming.md b/src/03_extreme-programming/03_tecniche/07_pair-programming.md
new file mode 100644
index 0000000000000000000000000000000000000000..e2133dd229a295a522b625ebec488f1b9e727f7e
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/07_pair-programming.md
@@ -0,0 +1,42 @@
+# Pair programming
+
+La programmazione a coppie (__pair programming__) è una tecnica controintuitiva: dal punto di vista del manager si pagano due persone per fare il lavoro di una, ma non è così.
+
+Ci sono diversi __vantaggi__:
+- in coppia, __ci si controlla a vicenda__ su ogni aspetto (codice, rispetto delle regole XP, idee); 
+- mentre il _pilota_ attua le idee, il _navigatore_ pensa cosa fare subito dopo: forma di __refactoring__;
+- favorisce l'__inserimento di nuovo personale__: piuttosto che lasciare i novizi da soli a studiare libroni, vengono affiancati e incitati a osservare e interagire con persone esperte che stanno lavorando;
+- fa ottenere una __proprietà collettiva__ (conoscenza osmotica), come descritta da Crystal. 
+Un altro punto importante sono i commenti _naive_ (ovvero fatti da programmatori junior) per permettere di chiarire concetti basilari date spesso per scontato. 
+
+Raddoppiare il numero di persone raddoppia la produttività?
+__No__, è stimato invece che la produttività aumenti circa del 50% - quindi non abbastanza per giustificare il costo.
+
+Diversi studi si chiedono se la produttività calcolata puntualmente sia una metrica sensata. 
+Secondo molti no, perché al termine di un'iterazione ciò che sembra poco produttivo in realtà lo è di più: il tempo non successivamente speso in verifica, convalida e refactoring è largamente assorbito dall'__ispezione continua del codice__ svoltasi durante le sessioni di pair programming.
+
+## Critiche
+Betrand Meyer, nel suo libro _"Agile! The Good, the Hype and the Ugly"_, scrive:
+
+> __Applied judiciously, pair programming can unquestionably be useful__. Many developers enjoy the opportunity to program jointly with a peer, particularly to deal with a thorny part of an assignment. 
+> The basic techniques, in particular the idea of speaking your thoughts aloud for immediate feedback, are well understood and widely applied. (As a manager I regularly hear, from a developer, “On this problem I would like to engage in a round of pair programming with X ”, and invariably find it a good idea.)
+> 
+> What is puzzling is the insistence of XP advocates that this technique is the only way to develop software and has to be applied at all times. __Such insistence makes no sense__, for two reasons.
+> 
+> The first is the __inconclusiveness of empirical evidence__, noted above. Granted, lack of data is often used as a pretext to block the introduction of new techniques. 
+> When an idea is obviously productive, we should not wait for massive, incontrovertible proof. 
+> But here there is actually a fair amount of empirical evidence, and it does not show a significant
+advantage for pair programming. 
+> Pair programming may be good in some circumstances, but if it were always the solution the studies would show it. 
+> In the absence of scientific evidence, a universal move is based on ideology, not reason.
+> 
+> The second reason, which may also explain why studies’ results vary, is that __people are different__. 
+> Many excellent programmers love interacting with someone else when
+they write programs; and many excellent programmers do not. 
+> Those of the second kind want to think in depth, undisturbed. 
+> The general agile view is that communication should be encouraged and that the days of the solitary, silent genius are gone. 
+> Fine; but if your team has an outstanding programmer who during the critical steps needs peace, quiet and solitude, do you kick him out of the team, or force him to work in a way that for him may be torture?
+
+> It is one thing to require that people explain their work to others; it is another, quite dangerous, to __force a single work pattern__, especially in a highly creative and challenging intellectual endeavor. 
+> When Linus Torvalds was writing Linux, he was pretty much by
+himself; that did not prevent him from showing his code, and, later on, engaging thousands of people to collaborate on it.
diff --git a/src/03_extreme-programming/03_tecniche/08_proprieta-collettiva.md b/src/03_extreme-programming/03_tecniche/08_proprieta-collettiva.md
new file mode 100644
index 0000000000000000000000000000000000000000..c52dddbe19331db026aed3a17ec72a5d48e87df2
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/08_proprieta-collettiva.md
@@ -0,0 +1,8 @@
+# Proprietà collettiva
+
+Il codice non appartiene a una singola persona ma al _team_: non devono quindi esistere policy di _"code owners"_ a la Microsoft.
+Tutti i componenti del team sono quindi autorizzati a modificare e sistemare ogni parte del codice, anche se scritta da un altro. 
+
+Durante il giorno, più volte al giorno, è comune __cambiare coppia__ e saranno quindi possibili situazioni in cui nessuno dei due ha una profonda conoscenza della parte di codice che si sta trattando o che il task non si addica alle competenze della coppia.
+
+In tutti i casi, in XP ci si riferisce al team e non al singolo.
diff --git a/src/03_extreme-programming/03_tecniche/09_integrazione-continua.md b/src/03_extreme-programming/03_tecniche/09_integrazione-continua.md
new file mode 100644
index 0000000000000000000000000000000000000000..3799bc2b9c2ed748050bd76adc7d2312c0511753
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/09_integrazione-continua.md
@@ -0,0 +1,16 @@
+# Integrazione continua
+Nell'ottica di ricevere feedback rapidi dal cliente è necessario __integrare spesso__, anche __più volte al giorno__. 
+Questo non significa far passare i test d'unità per integrare tutto in un'unica operazione, ma essere graduali: è frequente scoprire che parti testate e funzionanti singolarmente una volta integrate nel prodotto finale non funzionano. 
+
+L'integrazione continua e graduale è una tecnica largamente utilizzata in tutti i campi, non solo nello sviluppo software.
+
+Al termine dello sviluppo di una _feature_, è compito della coppia integrarla nella __macchina di riferimento__.
+L'accesso a tale macchina deve essere regolato in maniera __esclusiva__: in situazioni di lavoro da remoto si può utilizzare un token.
+La macchina di riferimento si trova, per quanto riguarda le funzionalità, in una situazione __monotona crescente__.
+Ad ogni integrazione è necessario produrre sempre qualcosa di consegnabile al cliente. 
+
+Una _user story_ si definisce __completata__ solo dopo aver terminato l'integrazione, superato dei test di integrazione e aver mostrato al cliente il risultato della macchina complessiva dopo l'integrazione.
+
+Un'altro punto a favore della continua integrazione è che evita la situazione in cui una coppia modifichi la macchina __dopo molto tempo__ dalla propria ultima integrazione, aumentando di molto la probabilità di errori per le altre coppie. 
+
+Se una coppia __non riesce ad integrare__ blocca anche tutte le altre che non possono andare avanti con le use story, quindi sarà necessario che quella coppia rinunci, ritorni sulla sua macchina e cerchi di risolvere lì - tutte le coppie hanno una propria macchina su cui testano prima di farlo su quella comune.
diff --git a/src/03_extreme-programming/03_tecniche/10_settimana.md b/src/03_extreme-programming/03_tecniche/10_settimana.md
new file mode 100644
index 0000000000000000000000000000000000000000..3831dc72d83bd598949473f865b168212fea167a
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/10_settimana.md
@@ -0,0 +1,7 @@
+# Settimana di 40 ore
+
+Il mestiere di sviluppatore ha sempre avuto dei __ritmi dettati dalle consegne__: lavorare troppo a lungo porta a un abbassamento della produttività, oltre che a stress e frustrazione. 
+
+Nell'XP si cerca di evitare queste situazioni in modo da avere una resa migliore, avere maggior soddisfazione nel lavorare nel team e nell'azienda, avere meno problemi fuori dal lavoro (tante volte questo eccessivo lavoro può causare problemi familiari) e inoltre abbassare la probabilità per l'azienda di perdere dipendenti. 
+
+Purtroppo però il mestiere dello sviluppatore non è meccanico e molto spesso si vuole portare a termine quello che si sta facendo perchè magari si è quasi alla soluzione, inoltre si continua a pensare a come risolvere dei problemi anche fuori dall'orario lavorativo.
diff --git a/src/03_extreme-programming/03_tecniche/11_cliente-posto.md b/src/03_extreme-programming/03_tecniche/11_cliente-posto.md
new file mode 100644
index 0000000000000000000000000000000000000000..1436b748d18e1ac9e60b650b76ea80f887c05fe3
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/11_cliente-posto.md
@@ -0,0 +1,11 @@
+# Cliente sul posto
+
+Dal punto di vista del cliente, il suo inserimento nel luogo fisico di sviluppo è un vantaggio in quanto può essere sicuro che gli sviluppatori stiano lavorando per lui e __può verificare come procede il progetto__.
+
+Dal punto di vista degli sviluppatori, invece, è fondamentale avere il cliente sul posto per potergli __chiedere chiarimenti__ in caso di specifiche non chiare. 
+La possibilità di poter far domande è come avere una _documentazione vivente_; il cliente potrà continuare a lavorare per la sua azienda, ma dovrà dare priorità alle richieste degli sviluppatori. 
+
+Avere il cliente sul posto ha comunque dei __limiti__: quest'ultimo, infatti, deve essere scelto accuratamente per avere una persona rappresentativa di tutti gli stakeholder; il compito è forse impossibile.
+Se il cliente del posto non è disponibile, il team deve trovare dei modi per poter comunque avere un _punto di riferimento_: la tecnica Scrum introduce il concetto di __product owner__, un componente interno al team che si comporta come se fosse il cliente.
+
+Il cliente durante le iterazioni può __creare altre storie__ che a partire dall'iterazione successiva potrà inserire nel planning game; è inoltre disponibile per __test funzionali__.
diff --git a/src/03_extreme-programming/03_tecniche/12_standard-codifica.md b/src/03_extreme-programming/03_tecniche/12_standard-codifica.md
new file mode 100644
index 0000000000000000000000000000000000000000..f7e8ecbff23b4eadf179e65c48b9144b51e4f137
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/12_standard-codifica.md
@@ -0,0 +1,10 @@
+# Standard di codifica
+
+È necessario prevedere delle regole (__convenzioni comuni__) che specificano come scrivere il codice, per aumentare la leggibilità e quindi la comunicazione attraverso il codice.
+
+Spesso, si utilizzano degli __strumenti__ per garantire il rispetto delle convenzioni o autocorreggere il codice auutomaticamente.
+
+Avere uno standard di codifica aiuta inoltre:
+- il refactoring;
+- la programmazione a coppie;
+- la proprietà collettiva.
diff --git a/src/03_extreme-programming/03_tecniche/13_just-rules.md b/src/03_extreme-programming/03_tecniche/13_just-rules.md
new file mode 100644
index 0000000000000000000000000000000000000000..75f84e7e41b48a28c92119a690602a443a27dd47
--- /dev/null
+++ b/src/03_extreme-programming/03_tecniche/13_just-rules.md
@@ -0,0 +1,11 @@
+# _They're just rules_
+
+L'ultima regola _"non è canonica"_, in quanto è stata aggiunta successivamente da alcuni agilisti.
+
+Al termine di un'iterazione si fa un __resoconto__ e quindi decidere come comportarsi per l'iterazione successiva.
+Nel suddetto resoconto si può anche decidere di __sospendere regole__ se si pensa che non siano adatte per la situazione o per il team e successivamente possono essere reintrodotte. 
+La decisione di non seguire una regola deve essere sempre fatta a livello di __team__, non dal singolo o dalla coppia.
+
+In conclusione, l'XP non è una tecnica così rigida e rigorosa: ad ogni iterazione, si possono effettuare test per trovare il giusto equilibrio.
+
+Questo punto non però è condiviso da tutti e una motivazione la si può trovare nel fatto che tutti i punti sono interconnessi tra loro, e quindio non è possibile studiarli singolarmente senza considerate anche gli altri perchè non avrebbero senso in quanto hanno una forte dipendenza l'una dall'altra; non a caso nei punti sopra si può notare come si influenzino a vicenda.
diff --git a/src/03_extreme-programming/04_cascata.md b/src/03_extreme-programming/04_cascata.md
new file mode 100644
index 0000000000000000000000000000000000000000..7c1d4032c59c9b5cab2f35918a9e45777b43f737
--- /dev/null
+++ b/src/03_extreme-programming/04_cascata.md
@@ -0,0 +1,23 @@
+# Relazione con il modello a cascata
+
+È possibile tentare di  raggruppare le diverse tecniche dell'eXtreme Programming nelle macrofasi descritte dal modello a cascata.
+
+- __Requirements__:
+    - i _clienti fanno parte del team di sviluppo_: requirements viventi;
+    - _consegne incrementali_ e pianificazioni continue: evoluzione del progetto.
+- __Design__:
+    - _metafora_ come visione unificante del progetto;
+    - _refactoring_: è design puro, molto utile per rendere possibile l'evolvibilità del software;
+    - presumere la _semplicità_.
+- __Code__:
+    - _programmazione a coppie_;
+    - _proprietà collettiva_;
+    - _integrazione continua_;
+    - _standard di codifica_.
+- __Test__
+    - _test di unità_ continuo (da scriversi prima del codice);
+    - _test funzionale_ scritto dagli utenti nelle user stories.
+
+In XP è inoltre presente la nozione di _prototipo_ sotto il nome di ___spike___, ovvero programmi molto semplici creati per esplorare soluzioni potenziali.
+Sono utili per capire se ho compreso le specifiche, la tecnologia da utilizzare e l'approccio da avere con i componenti esterni con cui bisogna dialogare. 
+Questi prototipi vengono creati, mostrati al cliente e quindi scartati.
diff --git a/src/03_extreme-programming/05_documentazione.md b/src/03_extreme-programming/05_documentazione.md
new file mode 100644
index 0000000000000000000000000000000000000000..365fc1fb3f5217affe00789d80ac98b8d36e148f
--- /dev/null
+++ b/src/03_extreme-programming/05_documentazione.md
@@ -0,0 +1,24 @@
+# Documentazione
+La __documentazione__ cartacea __non è necessaria__: 
+il cliente, il compagno di peer programming e il codice _sono la documentazione_.
+
+La documentazione è sostituita dal codice in quanto:
+- i __test di unità__ che sono delle _specifiche eseguibili_, infatti li scrivo prima di fare il codice (prima dico cosa voglio tramite il test);
+- il __continuo refactoring__ consente di avere un codice estremamente leggibile e quindi elimina il bisogno dei commenti.
+Scrivere codice semplice tramite refactoring in modo che sia facilmente comprensibile è in realtà molto complesso.
+
+## CRC cards
+Le __Class Responsibility and Collaboration cards__ permettono di rappresentare classi e le relazioni tra di esse.
+Nate in ambiente didattico per spiegare l'OOP, sono ora utilizzati da alcuni team agile per discutere di design e il modo di utilizzo è simile a quello del planning game.
+
+> Le carte CRC sono realizzate su piccole schede di carta o cartoncino. Ciascuna carta descrive una classe (o un oggetto) in modo sommario, indicando:
+> 
+> - Il nome della classe
+> - Le sue superclassi e sottoclassi (dove applicabile)
+> - Le sue responsabilità
+> - Il nome di altre classi con cui questa classe collabora per svolgere i compiti di cui è responsabile
+> - L'autore
+> 
+> L'uso di schede di piccole dimensioni ha lo scopo di limitare la complessità della descrizione, evitando che vengano riportate troppe informazioni di dettaglio. Serve anche a impedire che a una classe vengano assegnate troppe responsabilità. Il supporto cartaceo consente una serie di attività gestuali utili in fase di brainstorming, come piazzare le carte su un tavolo e spostarle, riorganizzarle, o anche eliminarle e sostituirle facilmente con altre nel corso della discussione. Il posizionamento delle carte su un tavolo può essere usato intuitivamente per rappresentare informazioni aggiuntive; per esempio, due carte possono essere parzialmente sovrapposte per indicare una relazione di stretta collaborazione, o una carta può essere posta sopra un'altra per indicare una relazione di controllo/supervisione.
+> 
+> _Da [Wikipedia](https://it.wikipedia.org/wiki/Carte_di_Class_Responsibility_Collaboration), l'enciclopedia libera (licenza CC BY-SA 3.0)_.
diff --git a/src/03_extreme-programming/06_criticita.md b/src/03_extreme-programming/06_criticita.md
new file mode 100644
index 0000000000000000000000000000000000000000..620c2a65d63f592cbfc3be718921c5b92abd7384
--- /dev/null
+++ b/src/03_extreme-programming/06_criticita.md
@@ -0,0 +1,39 @@
+# Criticità
+
+## Quando non utilizzare XP
+Back non esclude mai la possibilità di utilizzare l'XP: secondo lui diceva può provare ad utilizzare questo approccio sempre (anche se in realtà non è sempre possibile "provare"), a patto che vengano rispettati i 12 punti elencati sopra.
+
+Da questo possiamo concludere che Agile non si può usare quando:
+- l'__ambiente__ non permette l'applicazione dei 12 punti, come per esempio succede con i team dislocati in luoghi diversi;
+- ci sono __barriere managieriali__, come team troppo numerosi;
+- ci sono __barriere tecnologiche__, come quando per esempio non è possibile utilizzare una macchina specifica condivisa da tutte le coppie per i test, ostacolando l'integrazione continua.
+- ci sono __troppi stakeholders__ diversi e in contrasto tra loro;
+- situazioni in cui __la consegna incrementale non ha senso__, come per una _centrale nucleare_ (vero [Dyatlov](https://en.wikipedia.org/wiki/Anatoly_Dyatlov)?).
+
+## Critiche da Meyer
+Di seguito sono elencate alcune critiche all'eXtreme Programming fatte da Meyer (già pluricitato in questo documento).
+
+- __Sottovalutazione dell'up-front__, ovvero la progettazione iniziale prima di partire. Per Meyer, a parte in casi eccezionali (sviluppatori o manager particolarmente bravi) la progettazione non può essere fatta in modo totalmente incrementale. Nell'esperienza dei tesisti e colleghi di dottorando del prof. Bellettini questo problema non è così presente, ma potrebbe trattarsi di _survivorship bias_.
+- __Sopravalutazione delle user stories__: secondo Meyer sono troppo specifiche per sostituire i requisiti.
+- __Mancata evidenziazione delle dipendenze tra user stories__. 
+Le _user stories_ dovrebbero essere indipendenti tra loro, ma questo non è quasi mai possibile; nel design classico si utilizzano i diagrammi di Gantt per chiarire tutte le dipendenze tra i diversi punti del sistema da realizzare.
+- Il __TTD può portare ad una visione troppo ristretta__.
+- __Cross functional team__: se i team sono troppo disomogenei, ovvero ci sono tante singole figure specializzate in un campo e queste devono collaborare in coppia, ci possono essere dei problemi.
+
+I punti di cui sopra cercano di evidenziare la mancanza di approfondimento e chiarezza dell'XP su alcuni aspetti dell'approccio ad un lavoro fornito da un cliente.
+
+È consigliata la lettura del libro di Meyer.
+
+## Mesi uomo
+È diffuso tra i manager pensare che la stima in tempo in mesi uomo sia graficata come un ramo di un iperbole, ovvero che il tempo diminuisca simil-esponenzialmente all'aumentare dei mesi uomo; tale stima non considera i tempi di _overhead_, ovvero il tempo impiegato per la comunicazione e tutto ciò che non è l'implementazione.
+I mesi uomo __non quindi sono una metrica valida__, ma sono utili solo a posteriori per valutare se un approccio ad un problema si è dimostrato valido.
+
+Nella realtà, __all'aumentare delle persone aumenta il bisogno di comunicare__.
+
+Quando il lavoro è strettamente sequenziale e non parallelizzabile (come la _gravidanza_) anche all'aumentare delle persone il tempo non cambia.
+
+Nel mondo dello sviluppatore software spesso c'è un __numero ideale di persone__ per un progetto; dopodiché, persone in più causano solo confusione e rallentano i tempi a causa della comunicazione.
+Il numero può anche essere grande, dipende dall'entità del progetto (esempio: space shuttle).
+
+In generale, le metodologie agili iniziano a __non funzionare più__ se il team è __più grande di 8-10 persone__.
+Quando il progetto non funziona più con un tale numero di persone, è necessario esplorare altre pratiche.
diff --git a/src/04_open-source/00_index.md b/src/04_open-source/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..4fe929d46e83a9506375021fe4430f7e166fea5b
--- /dev/null
+++ b/src/04_open-source/00_index.md
@@ -0,0 +1,6 @@
+# Open source
+
+Apriamo ora le porte a un nuovo modo di sviluppare software che si è molto diffuso negli ultimi decenni ed è ora alla base di progetti applicativi molto importanti e famosi: il processo __open-source__. In due parole, un software open-source è un software il cui codice è liberamente consultabile online e il cui sviluppo si basa non su un singolo team ma su una community di sviluppatori indipendenti.
+
+- [**Letteratura**](./01_letteratura/00_index.md): analisi di quattro brani sulla disciplina
+- [**Sfide**](./02_sfide.md) rispetto allo sviluppo tradizionale
diff --git a/src/04_open-source/01_letteratura/00_index.md b/src/04_open-source/01_letteratura/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..3a00328ea14bc2bf330bad514febb9a6eb7bcdcd
--- /dev/null
+++ b/src/04_open-source/01_letteratura/00_index.md
@@ -0,0 +1,8 @@
+# Letteratura
+
+In questa sezione verranno analizzati i seguenti testi visti a lezione.
+
+- [**_The Cathedral and the Bazaar_**](./01_cathedral-bazaar.md) di Eric Steven Raymond
+- [**_Care and Feeding of FOSS_**](./02_care-feeding.md) di Craig James
+- [**_The Emerging Economic Paradigm Of Open Source_**](./03_emerging-economic-paradigm.md) di Bruce Perens
+- [**_An empirical study of open-source and closed-source software products_**](./04_empirical-study.md) di James Paulson
diff --git a/src/04_open-source/01_letteratura/01_cathedral-bazaar.md b/src/04_open-source/01_letteratura/01_cathedral-bazaar.md
new file mode 100644
index 0000000000000000000000000000000000000000..88835ba2f08b51ff9e5f0819baaf2613cd143d10
--- /dev/null
+++ b/src/04_open-source/01_letteratura/01_cathedral-bazaar.md
@@ -0,0 +1,64 @@
+# _The Cathedral and the Bazaar_
+
+Raymond propone nel suo [articolo](http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/) due immagini per descrivere i due mondi contrapposti closed-source e open-source:
+
+- la __cattedrale__ (closed source): si tratta di un ambiente molto gerarchizzato, fortemente legato al progetto iniziale di un unico "architetto" responsabile dei lavori;
+- il __bazaar__ (open source): è l'ambiente più anarchico possibile, in cui ognuno lavora per sé e costruisce espandendo ciò che gli altri hanno già fatto.
+
+Entrambe le costruzioni risultano splendide e attraenti, ma sono chiaramente legate a modi di pensare la costruzione e la progettazione totalmente opposti.
+
+## Vita e vantaggi di un progetto open-source
+
+Nell'articolo Raymond prosegue a descrivere come nasce un progetto open-source, esordendo con la seguente citazione:
+
+> Ogni buon lavoro software inizia dalla frenesia personale di un singolo sviluppatore.
+
+Si delinea dunque la seguente timeline di un progetto open-source:
+
+1. Uno sviluppatore ha un problema e intende risolverlo sviluppando un'applicazione.
+2. Lo sviluppatore chiede ad amici o colleghi cosa sanno sull'argomento: alcuni hanno lo stesso problema o problemi simili, ma nessuno ha una soluzione.
+3. Le persone interessate cominciano a scambiarsi pareri e conoscenze sull'argomento.
+4. Coloro che intendono spendere delle risorse (di fatto il proprio tempo libero) per risolvere il problema danno il via ad un progetto informale.
+5. I membri del progetto lavorano sul problema finché non raggiungono dei risultati presentabili.
+
+    Fino a qui però il progetto non è ancora definibile open-source in quanto vi lavora solo un gruppo ristretto di amici e conoscenti: il vero progetto open nasce quando __viene messo online a disposizione di tutti il codice sorgente__. Continuando, dunque:
+
+6. Si rende noto il lavoro online e arrivano i primi suggerimenti esterni al gruppo: questi saranno tanto più frequenti quanto più il progetto pubblicato presenterà errori in quanto la comunità, principalmente composta da altri sviluppatori, sarà motivata a risolverli.
+7. Si crea interazione tra i vecchi e i nuovi membri del gruppo di sviluppo.
+8. Nuove informazioni e competenze vengono acquisite e si ritorna al punto 5.
+
+Raymond continua esponendo alcune delle caratteristiche e dei vantaggi dei progetti open-source, primo fra tutti il fatto che:
+
+> Se dai a tutti il codice sorgente, ognuno di essi diventa un tuo ingegnere.
+
+dove con questo si intende che la possibilità di vedere e commentare il codice sorgente permette a utenti esperti di suggerire modifiche e prendere parte attiva allo sviluppo. Talvolta si tende però a pensare che un progetto di questo tipo sia destinato unicamente ad altri informatici o sviluppatori, ma ciò non è affatto vero: tante attività utili a portare avanti un progetto open-source non richiedono necessariamente competenze informatiche, come per esempio la segnalazione di bug o la moderazione di contenuti nella comunità.
+
+A tal proposito, è importante il seguente concetto:
+
+> Se ci sono abbastanza occhi [che cercano errori], gli errori diventano di poco conto.
+
+Non c'è molto da spiegare: più sono le persone che controllano e leggono il codice più sarà probabile trovare gli errori in esso contenuti; inoltre, gli errori rilevati possono essere risolti più facilmente grazie al supporto della community di sviluppatori, che potrebbe già conoscere una soluzione. \
+L'accento posto sulla community viene ulteriormente rimarcato dal valore che viene attribuito ai beta-tester, che in un progetto open-source è chiunque utilizzi l'applicazione in qualunque suo stadio vista la sua estrema malleabilità:
+
+> Se tratti i tuoi beta-tester come se fossero la tua risorsa più importante, essi risponderanno diventando la tua risorsa più importante.
+
+Per mantenere attiva la community di sviluppatori è però necessario un costante monitoraggio e cura; per permettere al progetto open-source di sopravvivere anche quando l'interesse dei creatori originali si è spento è fondamentale passarne il controllo a qualcuno di fidato e competente, come ci ricorda Raymond nell'ultima citazione che riportiamo:
+
+> Quando hai perso interesse in un programma, l'ultimo tuo dovere è passarlo a un successore competente.
+
+Spesso questo passaggio di testimone non viene fatto e il progetto muore: occorre invece trovare qualcuno di interessato allo sviluppo, anche perché un programma in uso dovrà necessariamente cambiare ed evolvere in futuro.
+
+## Confronto tra modelli
+
+Per capire meglio i concetti fondanti del mondo open-source mostriamo in un modo sintetico un confronto tra lo stesso e i metodi di sviluppo tradizionale e agile nel riguardo di svariati concetti:
+
+| Cosa | Tradizionale | Agile | Open source |
+|------|--------------|-------|-------------|
+| __Documentazione__ | La documentazione è enfatizzata come strumento di controllo qualità e gestione. | La documentazione è de-enfatizzata. | Tutti i manufatti di sviluppo sono disponibili a chiunque, compresi il codice e la documentazione. |
+| __Requisiti__ | Gli analisti traducono le necessità dell'utente in specifiche software. | Gli utenti fanno parte del team. | Gli sviluppatori spesso sono anche gli utenti. |
+| __Assegnamento dello staff__ | Gli sviluppatori sono assegnati ad un unico progetto. | Gli sviluppatori sono assegnati ad un unico progetto. | Gli sviluppatori tipicamente lavorano su più progetti con diversi livelli di partecipazione (_impossibile pianificare lo sviluppo_). |
+| __Revisione del codice paritaria__ | La revisione del codice tra pari è ampiamente accettata ma raramente effettuata. | La _pair programming_ introduce una forma di revisione del codice tra pari. | La revisione del codice è una necessità ed è praticata quasi universalmente. |
+| __Tempi di rilascio__ | Tante feature in poche release massicce. | Tante piccole release incrementali. | Gerarchia dei tipi di release: _nightly_ (compilazione giornaliera dal branch master), _development_ e _stable_. |
+| __Organizzazione__ | I team sono gestiti dall'alto. | I team sono auto-organizzati. | I contributori individuali decidono per sé come organizzare la propria partecipazione. |
+| __Testing__ | Il testing è gestito dallo staff di _Quality Assurance_ (QA), che segue le attività di sviluppo. | Il testing è parte integrante dello  sviluppo (TDD). | Il testing e la QA possono essere svolti da tutti gli sviluppatori. |
+| __Distribuzione del lavoro__ | Parti differenti della codebase sono assegnate a persone differenti. | Chiunque può modificare qualsiasi parte della codebase. | Chiunque può modificare qualsiasi parte della codebase, ma solo i _committer_ possono rendere ufficiali le modifiche. |
\ No newline at end of file
diff --git a/src/04_open-source/01_letteratura/02_care-feeding.md b/src/04_open-source/01_letteratura/02_care-feeding.md
new file mode 100644
index 0000000000000000000000000000000000000000..e2dfd1013ff77ff101f339b175711d78ac26f611
--- /dev/null
+++ b/src/04_open-source/01_letteratura/02_care-feeding.md
@@ -0,0 +1,13 @@
+## _Care and Feeding of FOSS_
+
+Ma come si inserisce il modello di sviluppo open-source in un panorama tecnologico sempre più accentrato nelle mani di poche e potenti aziende? Il timore è infatti quello che rendendo il codice disponibile a tutti si potrebbe essere subito vittima di plagio da parte di giganti del settore in grado di realizzare in poco tempo ciò che prenderebbe a un team open-source svariati mesi, accaparrandosi così una larga fetta di mercato. Craig James smentisce tuttavia tale visione nel suo [articolo](https://web.archive.org/web/20081015010505/http://www.moonviewscientific.com/essays/software_lifecycle.htm), in cui descrive il ciclo di vita di un software FOSS (Free and Open Source Software) come la sequenza delle seguenti fasi:
+
+1. __Invenzione__: qualcuno ha un'idea e la implementa facendola funzionare.
+Dal momento in cui l'idea viene resa pubblica si assiste una grossa esplosione di progetti in tal senso finanziati da varie aziende che cercano di diventare leader nel nuovo mercato.
+2. __Espansione e innovazione__: il mondo si accorge dell'invenzione e la tecnologia inizia a espandersi in quanto le aziende si 'rincorrono' a vicenda cercando di aggiungere più funzionalità possibili. Questa fase non rappresenta un buon momento per far nascere un progetto open source: un piccolo gruppo non riuscirebbe a prevalere sulle grandi aziende; poiché inoltre le specifiche non sono ancora ben definite si rischierebbe di implementare funzioni inutili.
+3. __Consolidamento__: i prodotti di alcune aziende iniziano a dominare il mercato, mentre i competitor vengono assorbiti, falliscono o diventano di nicchia. Diminuisce complessivamente il numero di player e l'innovazione rallenta.
+4. __Maturità__: il problema e le specifiche sono ora ben chiare e consolidate. Per un prodotto commerciale è ormai difficile entrare nel mercato, ma per uno open source è paradossalmente il momento migliore: il piccolo team ha uno slancio innovativo che le grosse aziende non hanno più e il loro prodotto può brillare dei vantaggi del mondo open-source, tra cui sicurezza e flessibilità.
+5. __FOSS Domination__: lentamente, il prodotto open-source inizia a erodere il vantaggio tecnologico dei competitor commerciali, che d'altra parte non hanno alcun interesse ad innovare ulteriormente: ciascuna loro innovazione potrebbe infatti essere facilmente copiata ed essi sono inoltre già largamente rientrati del loro investimento iniziale. Il prodotto open-source inizia ad accaparrarsi fette di mercato.
+6. __The FOSS era__: alla fine il progetto open-source domina completamente il nuovo mercato, mentre le grandi aziende devono ripiegare sulla vendita di servizi più che di software.
+
+Il vantaggio di un progetto open-source non è dunque tanto la rapidità di espansione o l'abilità di intercettare il mercato, quanto il fatto che esso permetta una continua innovazione che _segue_ le necessità del mercato, cosa che le grandi aziende faticano a conseguire in quanto ancora legate a un paradigma di investimento in cui una volta fatto il lavoro e guadagnato un tot è più costoso fare manutenzione del software piuttosto che lasciarlo morire.
\ No newline at end of file
diff --git a/src/04_open-source/01_letteratura/03_emerging-economic-paradigm.md b/src/04_open-source/01_letteratura/03_emerging-economic-paradigm.md
new file mode 100644
index 0000000000000000000000000000000000000000..cc85d7b33040071cbe93626184706d062cd0670a
--- /dev/null
+++ b/src/04_open-source/01_letteratura/03_emerging-economic-paradigm.md
@@ -0,0 +1,25 @@
+## _The Emerging Economic Paradigm Of Open Source_
+
+Affrontiamo ora un fenomeno interessante: perché sempre più aziende investono in software open-source? A prima vista parrebbe un controsenso, perché da sempre i produttori di software proteggono il proprio prodotto tramite segreto aziendale: avere codice liberamente consultabile distrugge tale privatezza ed espone all'ascesa di competitor. Per rispondere a questa domanda ci serviamo di un [articolo](http://web.archive.org/web/20120724095330/http://perens.com/works/articles/Economic.html) di Bruce Perens sull'argomento.
+
+Perens fa in primo luogo notare che spesso per diverse aziende il software da sviluppare non è il prodotto, ma una __tecnologia abilitante essenziale__: per esempio, Amazon sviluppa molto software per il sito di e-commerce ma il suo prodotto non è il sito. In tali ambiti la scrittura di codice è un _costo_, non il prodotto su cui guadagnare.
+
+Dal punto di vista economico è poi importante stabilire se la tecnologia dà un vantaggio competitivo, ovvero se essa è __differenziante__ o __non differenziate__. Per fare ciò è sufficiente rispondere alle seguenti domande:
+
+- __Il cliente si accorge degli effetti del software?__ Per esempio, le persone si accorgono dell'esistenza del sistema di raccomandazione dei libri di Amazon?
+- __I competitor non hanno accesso allo stesso software?__ Se Amazon usasse il sistema di raccomandazione venduto anche a Feltrinelli allora non avrebbe senso mantenerlo privato.
+
+Se la risposta a una delle due domande è no, avere un software proprietario non apporta alcun vantaggio: un modello di sviluppo open-source sparpaglia invece i costi e genera valore, motivo per cui sempre più aziende lo scelgono.
+
+Perens conclude il suo articolo riportando una matrice in cui confronta 4 diversi modelli di sviluppo, ognuno con le sue caratteristiche che ne determinano l'applicabilità in diverse situazioni:
+
+- __Retail__: il software è sviluppato per poter essere venduto a chiunque lo desideri;
+- __In-House & Contract__: il software è sviluppato per un singolo committente;
+- __Consortium & Non-Open-Source Collaboration__: un modello secondo cui diverse aziende concorrenti si mettono insieme per sviluppare un software comune (molto poco diffuso);
+- __Open Source__: il software è disponibile gratuitamente e il suo codice è pubblico.
+
+Tali modelli vengono valutati in base all'efficienza (_quanti soldi vanno agli sviluppatori_), al tasso di fallimento del progetto, ai costi di distribuzione, al rischio di plagio, al fatto di proteggere o meno la differenziazione a livello commerciale del cliente e/o del venditore e alla dimensione richiesta del mercato perché il progetto sia un successo.
+
+![Confronto paradigmi](/assets/04_confronto.png)
+
+Come si può notare dalla tabella, non tutti i paradigmi proteggono il vantaggio competitivo, con differenze dal punto di vista del cliente e del produttore: è dunque importante scegliere il modello di sviluppo che più si confà alle proprie esigenze.
diff --git a/src/04_open-source/01_letteratura/04_empirical-study.md b/src/04_open-source/01_letteratura/04_empirical-study.md
new file mode 100644
index 0000000000000000000000000000000000000000..50c734c80c134b3de365e84fe5b9dd008e5d117c
--- /dev/null
+++ b/src/04_open-source/01_letteratura/04_empirical-study.md
@@ -0,0 +1,7 @@
+## _An empirical study of open-source and closed-source software products_
+
+Concludiamo il discorso sul mondo open-source riportando uno [studio](https://www.researchgate.net/publication/3188403_An_empirical_study_of_open-source_and_closed-source_software_products) portato avanti da J.W. Paulson e altri con lo scopo di verificare alcune affermazioni ritenute vere nei riguardi del software open-source.
+
+![Risultati studio](/assets/04_empirical-study.png)
+
+Per ognuna di tali affermazioni l'articolo definisce una metrica e la calcola nei riguardi di progetti open-source e closed-source, concludendo così che solo alcune delle dicerie sull'open-source risultano vere mentre molte altre (es. _è più sicuro_, _è più veloce_) si rivelano essere al più circostanziali.
diff --git a/src/04_open-source/02_sfide.md b/src/04_open-source/02_sfide.md
new file mode 100644
index 0000000000000000000000000000000000000000..529040f8424a59c854bdd51e2c8f045ee0689373
--- /dev/null
+++ b/src/04_open-source/02_sfide.md
@@ -0,0 +1,32 @@
+# Le sfide del modello open source
+
+Per sua natura il sistema di sviluppo open source pone delle sfide peculiari sconosciute all'ambiente di sviluppo tradizionale; prima di affrontare dunque gli strumenti di design e organizzazione del lavoro che sono nati per risolvere queste problematiche diamone una breve panoramica.
+
+## Difficile integrazione del software
+
+Quella dell'integrazione del software è in realtà una vecchia sfida che viene enormemente amplificata nell'ambito FOSS. Per integrare le nuove feature sviluppate in un software stabile diversi modelli avevano costruito le proprie pratiche:
+
+- Nel modello a cascata l'integrazione era una fase circoscritta e a sé stante.
+- A tale struttura molto rigida si contrappone lo schema innovativo _Stabilize & Synchronize_ nato in ambiente Microsoft: durante il giorno gli sviluppatori lavorano sul proprio pezzo di codice in cui sono responsabili, e di notte il software viene ricompilato da zero. La mattina dopo, si avevano dunque due possibilità:
+  - la compilazione falliva, il responsabile veniva trovato e _"punito"_;
+  - la compilazione aveva successo, il software integrato è quindi nella _"versione migliore possibile"_.
+- In XP l'integrazione veniva eseguita più volte al giorno in modo esclusivo: un solo sviluppatore alla volta poteva integrare il proprio lavoro sull'unica macchina di integrazione disponibile; questo permetteva di individuare facilmente eventuali problemi di integrazione e risolverli con rapidità.
+
+Ma come organizzare l'integrazione nel mondo open-source? Per sua natura, in questo ambito l'integrazione viene eseguita continuamente e senza coordinazione a priori: è anarchia totale, con lo svantaggio che da un giorno all'altro una enorme parte della codebase potrebbe cambiare in quanto un singolo sviluppatore potrebbe integrare mesi e mesi di lavoro in un'unica botta. Vedremo più avanti che strumenti si sono costruiti per contenere tale problematica.
+
+## Sfaldamento del team
+
+Nell'open source nascono inoltre problemi riguardanti la gestione del team. Occorre decidere:
+
+- come comunicare
+- come tenersi uniti
+- come coordinarsi
+- come ottenere nuove collaborazioni
+
+Per comunicare si utilizza di solito __internet__: si potrebbe dire che senza internet non potrebbe esistere il concetto stesso di open source. In particolare si utilizzano spesso dei __forum__ per organizzare il lavoro, in modo da tenere la community unita e rispondere dubbi ai nuovi utenti.
+
+Per quanto riguarda il coordinamento del lavoro approfondiremo nelle prossime lezioni vari strumenti per la sincronizzazione del lavoro e di versioning per codice e documentazione (come __git__).
+
+Deve poi essere facile, addirittura banale, poter compilare il codice e ricreare l'ambiente di sviluppo omogeneo per tutti; si utilizzano quindi strumenti di __automatizzazione delle build__ (come i __Makefile__) in modo che chiunque voglia partecipare possa farlo indipendentemente dalla propria configurazione software e hardware.
+
+È infine importante _educare_ i reporter dei bug e avere un sistema per organizzare per le __segnalazioni di errori__: il sistema dovrebbe essere accessibile a tutti in modo da evitare segnalazioni duplicate e consentire una facile organizzazione delle stesse. Vedremo più avanti come anche una segnalazione d'errore avrà il suo "ciclo di vita".
diff --git a/src/05_scm/00_index.md b/src/05_scm/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..1db1fb4bb5f38e5adefb585dae3c143c1fd5ce17
--- /dev/null
+++ b/src/05_scm/00_index.md
@@ -0,0 +1,12 @@
+# Sofware Configuration Management
+
+Le soluzioni di Software Configuration Management nascono da problemi complessi purtroppo molto comuni nel mondo dello sviluppo software, come:
+- pubblicare un _hotfix_ su una versione precedente a quella in cui si sta sviluppando. Può essere difficile localizzare le versioni vecchie, modificarle e rimappare le modifiche sulle versioni nuove;
+- condividere lavori con altri gestendo accessi contemporanei e conflitti;
+- stabilire la responsabilità di ciascuna linea di codice. 
+
+Il Software Configuration Management è l'insieme di pratiche che hanno l'obiettivo di rendere sistematico il processo di sviluppo, tenendo traccia dei cambiamenti in modo che il prodotto sia in ogni instante in uno stato (_configurazione_) ben definito.
+
+- [**Storia**](./01_storia.md)
+- [**Meccanismo di base**](./02_meccanismo.md)
+- [**git**](./03_git.md)
diff --git a/src/05_scm/01_storia.md b/src/05_scm/01_storia.md
new file mode 100644
index 0000000000000000000000000000000000000000..bef2cbfded380d632902c87c10c0e8a1bbaf8198
--- /dev/null
+++ b/src/05_scm/01_storia.md
@@ -0,0 +1,33 @@
+# Storia
+Il Configuration Management negli anni '50 nell'ambito dell'industria aerospaziale. 
+Alla fine degli anni '70 inizia ad essere applicato all'ingegneria del software.
+
+Gli oggetti di cui si controlla l'evoluzione sono detti _configuration item_ o _artifact_ (manufatti, solitamente file).
+Dunque l'SMC ci permette di controllare le revisioni degli _artifact_ e il risultato di tali revisioni,questo processo è molto utile per la generazione di un prodotto a partire da una configurazione ben determinata.
+
+## Manufatti
+Gli _"oggetti"_ di cui si controlla l'evoluzione sono detti _configuration item_ o manufatti; generalmente sono file.
+Se si cambia nome a un file è come eliminarne uno e partire da zero con uno nuovo. 
+Originariamente i tool tracciavano i file indipendentemente, senza un senso logico (una _configurazione_) comune. 
+
+![Definizione di configurazione](/assets/05_configuration-management.png)
+
+- anni '80: strumenti locali (SCCS, ...)
+- anni '90: strumenti client-server centralizzati (CVS, subversion, ...)
+- anni '00: strumenti distribuiti peer-to-peer (git, mercurial, bazaar, ...)
+
+git nasce da un'esigenza di Linus Torvalds con il kernel Linux.
+
+## Centralizzato vs decentralizzato
+<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/4XpnKHJAok8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+
+Il mondo open source preferisce un approccio decentralizzato al version control. Perché?
+- è possibile lavorare offline;
+- è molto più veloce, perché la rete non fa più da _bottleneck_;
+- supporta diversi modi di lavorare:
+    - simil centralizzato: un repository viene considerato "di riferimento";
+    - due peer che collaborano direttamente;
+    - gerarchico a più livelli (kernel Linux).
+
+Non c'è sincronizzazione automatica, ma ci sono comandi espliciti per fare _merge_ tra repository remote.
+In git, per via della sua struttura modulare, è possibile utilizzare il proprio algoritmo _merge_ rispetto a quelli già inclusi. 
diff --git a/src/05_scm/02_meccanismo.md b/src/05_scm/02_meccanismo.md
new file mode 100644
index 0000000000000000000000000000000000000000..891f0ef66f3ca32472fac691b9f1aaef7a139dea
--- /dev/null
+++ b/src/05_scm/02_meccanismo.md
@@ -0,0 +1,44 @@
+# Meccanismo di base
+Ogni _cambiamento_ è regolato da:
+- __check-out__: dichiara la volontà di lavorare partendo da una particolare revisione di un manufatto (o di una configurazione di diversi manufatti);
+- __check-in__ (o __commit__): dichiara la volonta di registrarne una nuova (spesso chiamata change-set).
+
+Queste operazioni vengono attivate rispetto a un _repository_.
+Scambio di dati tra il repository (che contiene tutte le configurazioni) e il workspace (l'ambiente in cui si trova nel filesystem).
+
+Solitamente ho un repository e \\( n \\) workspace, uno per ogni ambiente dove sto lavorando.
+
+## Repository
+La repository mantiene:
+- date
+- etichette
+- versioni
+- diramazioni (branches)
+- ecc...
+
+Per risparmiare spazio, le repository salvano solo le differenze tra una versione e l'altra. 
+In realtà, Git non fa così, perché usa link simbolici: fare il _checkout_ di una specifica versione è instantaneo.
+
+Le repository possono essere centralizzate o distribuite.
+
+Nei sistemi di versioning distribuiti c'è il concetto di __hashing__, in modo da identificare file uguali anche se in posizioni diverse.
+Per confrontare storie diverse si utilizzano gli hash dei file e delle directory.
+
+### Cosa tracciare?
+Qualunque sistema si usi, occorre prendere decisioni importanti che influenzano la replicabilità della produzione.
+- Si traccia l'evoluzione anche di componenti fuori dal nostro controllo (librerie, compilatori)?
+- Si archiviano i file che sostitusicono il prodotto (eseguibile, ecc...)?
+
+La risposta ad entrambe queste domande è __no__, perché è scomodo, anti economico, costoso...
+Questo porta però problemi, perché non c'è perfetta replicabilità.
+
+## Accesso concorrente
+
+Quando il repository è condiviso da un gruppo di lavoro, nasce il problema di gestirne l'accesso concorrente. 
+Esistono due modelli:
+- __modello _'pessimistico'___ (RCS): prevedo il possibile conflitto assicurandomi che chi lavora sia l'unico con l'accesso in scrittura. Funziona solo in ambienti centralizzati, nell'open source non può funzionare.
+- __modello _'ottimistico'___ (CVS): il sistema si disinteressa del problema e fornisce supporto per le attività di _merge_ di change-set paralleli potenzialmente conflittuali. 
+    
+Il modello ottimistico può essere regolato con i branch: l'attività di _merge_ è quindi fondamentale.
+CVS/Subversion scoraggiava i branch, Git li rende facili e li incoraggia.
+In Git, l'uso dei branch è talmente comune che a volte è necessario introdurre delle politiche (come GitFlow) sul loro utilizzo.
\ No newline at end of file
diff --git a/src/05_scm/03_git.md b/src/05_scm/03_git.md
new file mode 100644
index 0000000000000000000000000000000000000000..e1b1eddd2c9e4c8dced1732e7a707646cb4a1220
--- /dev/null
+++ b/src/05_scm/03_git.md
@@ -0,0 +1,74 @@
+# git 
+
+![Schema git](/assets/05_git-schema.png)
+
+In figura, possiamo osservare 4 _livelli_ ordinati:
+- __working directory__ (WD): rappresenta la _configurazione_ della directory di lavoro sul filesystem - esiste indipendentemente da git.
+Può essere vista come l'unione dei _tracked_ and _untracked_ files; 
+- __index__ (o __area di staging__): insieme dei _tracked_ files da git.
+- (\\(n\\)?) __local repository__: insieme delle modifiche committate e relativo storico. 
+- (\\(n\\)) __remote repository__: branch remoto; è possibile avere sia più branch per progetto remoto che più progetti remoti configurati.
+
+Il termine ___repository___ è abbastanza _misleading_, perché è comunemente associato ad un progetto mentre in questa astrazione a livelli corrisponde di fatto a un __branch__.
+
+Il passaggio tra un livello e l'altro non è mai automatico, ma è sempre esplicitato da un'operazione. 
+
+## Operazioni di base
+
+È consigliata la lettura di [git Cheatsheet](http://ndpsoftware.com/git-cheatsheet.html).
+
+![Puntatori commit git](/assets/05_git-commits.png)
+
+Per ogni branch c'è un puntatore all'ultimo commit di tale branch.
+L'HEAD punta all'ultimo commit in cui siamo: normalmente corrisponde al puntatore del branch corrente; quando non è così siamo in una situazione di _HEAD scollegato_. 
+È utile potersi spostare tra i commit per controllare revisioni precedenti, ma in caso di nuovi commit è importante creare un nuovo branch per poterci riferire ad esse.
+
+### `git commit` &mdash; _record changes to the repository_
+
+Il comando git commit ci permette di _salvare_ del contenuto dall'index al branch locale.
+
+Dopo aver creato il commit, l'`HEAD` e il puntatore al branch corrente puntano al nuovo commit.
+Anche il contenuto dell'index equivale al contenuto del commit.
+
+![Puntatori commit git 2](/assets/05_git-commit.png)
+
+#### `--amend`
+
+Con l'opzione -\-amend è possibile rimpiazziare facilmente l'ultimo commit con uno nuovo. 
+
+![git commit --amend](/assets/05_git-commit-amend.png)
+
+### `git switch` &mdash; _switch branches_
+
+Il comando git switch ha un sottoinsieme delle funzionalità del comando git checkout ed è più semplice da utilizzare.
+
+Permette di passare a un nuovo branch semplicemente modificando l'HEAD (e di conseguenza il contenuto dell'index e dei file). 
+
+![git checkout](/assets/05_git-checkout.png)
+
+### `git merge` &mdash; _join two or more development histories together_
+
+Il comando git merge è utile per unire branch (o più in generale alberi) insieme.
+
+Se i due branch non sono divergenti, il merge avviene in modo banale con un _fast-forward_: nessun ulteriore commit verrà cambiato, verrà solo modificato il puntatore del branch e l'HEAD. 
+Per forzare la creazione di un merge di commit (in gitFlow è apprezzato) occorre utilizzare l'opzione `--no-ff`.
+
+In tutti gli altri casi, il merge può concludersi con successo oppure possono avvenire conflitti. 
+Per risolverli, git ci proporrà un'interfaccia simile alla seguente.
+
+    <<<<<<< yours:sample.txt
+    Conflict resolution is hard;
+    let's go shopping.
+    =======
+    git makes conflict resolution easy.
+    >>>>>>> theirs:sample.txt
+
+Una volta risolti tutti i conflitti è sufficiente commitare le modifiche concludendo quindi il merge.
+
+![git merge](/assets/05_git-merge.png)
+
+### `git reset` &mdash; _reset current HEAD to the specified state_
+
+Il comando git reset reimposta il contenuto dei file nell'_index_ (e, opzionalmente con l'opzione `--hard` nella WD) all'ultimo commit puntato da HEAD o ad un altro commit.
+
+![git reset](/assets/05_git-reset.png)
diff --git a/src/06_git-workflow/00_index.md b/src/06_git-workflow/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..21d6575431b4d428775825038603d5476c557680
--- /dev/null
+++ b/src/06_git-workflow/00_index.md
@@ -0,0 +1,11 @@
+# Git workflow e strumenti
+
+In git, l'utilizzo dei branch è fortemente incentivato dal suo design e dalle sue funzionalità, rendendo praticamente impossibile lavorare senza utilizzarli. I branch consentono di creare versioni separate del codice, permettendo di lavorare su diverse funzionalità o correzioni di bug in modo indipendente e senza interferire con il codice principale. 
+C'è __libertà completa__ sul loro utilizzo: tutti i branch hanno pari importanza, chiunque può crearli e nominarli in qualunque modo.
+
+Lavorando in un team, è quindi necessario stabilire delle politiche sui tipi e i nomi di branch, in modo da organizzare il lavoro al meglio. 
+
+- [**GitFlow**](./01_gitflow.md): organizzazione branch
+- [**Hosting centralizzato**](./02_hosting-centralizzato.md): repository centrali git
+- [**Gerrit**](./03_gerrit.md): meccanismo di review in team
+- [**Strumenti dell'opensource**](./04_strumenti-opensource/00_index.md): strumenti opensource per build automation e bug tracking
diff --git a/src/06_git-workflow/01_gitflow.md b/src/06_git-workflow/01_gitflow.md
new file mode 100644
index 0000000000000000000000000000000000000000..cb408d25a4ab6a19215766d6086df98f4d4ce24a
--- /dev/null
+++ b/src/06_git-workflow/01_gitflow.md
@@ -0,0 +1,216 @@
+# GitFlow
+
+GitFlow è una tecnica di organizzazione dei branch e delle repository che prevede la creazione sia di diversi tipi di branch a vita limitata che il loro _merge_ guidato.
+
+È disponibile online una [cheatsheet](https://danielkummer.github.io/git-flow-cheatsheet/) che fornisce una panoramica veloce delle principali operazioni e dei comandi di GitFlow. 
+Si tratta di uno strumento utile per chi è alle prime armi con questa tecnica di organizzazione dei branch, ma non esaustivo: per una comprensione più approfondita del metodo, è meglio integrarlo con altre risorse.
+
+I branch e i tipi di branch previsti da GitFlow sono:
+- branch master;
+- branch develop;
+- feature branches;
+- release branches;
+- hotfix branches.
+
+## `develop` e `master`
+
+![GitFlow develop e master](/assets/06_gitflow-develop-master.png)
+
+In GitFlow, ci sono due branch che hanno una durata più lunga rispetto ai branch temporanei utilizzati per lavorare su specifiche funzionalità o correzioni di bug:
+- __`master`__: contiene le versioni stabili del codice sorgente, pronte per essere consegnate o rilasciate al cliente o al pubblico. Queste versioni sono considerate _affidabili_ e _testate_, quindi possono essere utilizzate in produzione;
+- __`develop`__: è il ramo di integrazione in cui vengono integrati i contribuiti di tutti i gruppi; è anche il punto di partenza degli altri tipi di branch.
+
+Al termine di ogni rilascio, il contenuto del branch `develop` viene integrato nel branch `master`, che rappresenta la versione stabile del codice. Le _versioni notturne_, invece, sono versioni di sviluppo che vengono rilasciate periodicamente e contengono le ultime modifiche apportate al codice. Esse vengono create partendo dal branch `develop`, che rappresenta il punto di integrazione di tutti i contributi dei gruppi di lavoro.
+
+## Feature
+
+![GitFlow feature](/assets/06_gitflow-feature.png)
+
+I __*feature branch*__ sono branch temporanei utilizzati per sviluppare nuove funzionalità o per correggere bug. __Possono essere creati solo a partire dal branch `develop`__ e vengono utilizzati per isolare il lavoro su una specifica funzionalità o problema dal resto del codice. 
+Quando il lavoro è completato, il feature branch viene integrato di nuovo nel `develop` tramite un merge. 
+In questo modo, è possibile lavorare in modo organizzato su diverse funzionalità o problemi senza interferire tra loro.
+Per integrare il lavoro svolto in un feature branch nel branch `develop`, è necessario eseguire un merge del feature branch nel `develop`. 
+Ci sono diversi modi di fare ciò, a seconda delle preferenze e delle esigenze specifiche.
+Un modo semplice di fare il merge è utilizzare il comando `git merge` dalla riga di comando. 
+Se il merge non è possibile a causa di conflitti, sarà necessario risolverli manualmente prima di poter completare l'operazione. 
+Una volta risolti i conflitti, sarà necessario creare un nuovo commit per finalizzare il merge.
+
+Per iniziare una feature...
+```bash
+$ git checkout develop                  # entra nel branch develop
+$ git branch feature/myFeature          # crea un branch di feature
+$ git checkout feature/myFeature        # entra nel branch appena creato
+```
+
+Al termine della feature, integro:
+
+```bash
+$ git checkout develop                  # entra nel branch develop
+$ git merge --no-ff feature/myFeature   # mergia il branch di feature
+$ git branch -d feature/myFeature       # elimina il branch di feature
+```
+### `--no-fast-forward`
+
+![GitFlow fast forward](/assets/06_gitflow-feature-ff.png)
+
+Di default, git risolve il merge di due branch con la stessa storia banalmente eseguendo il _fast forward_, ovvero spostando il puntatore del branch di destinazione all'ultimo commit del branch entrante.
+
+In GitFlow è preferibile esplicitamente __disabilitare il fast forward__ (con l'opzione `--no-ff`) durante il merge in `develop` in quanto è più facile distinguere il punto di inizio e il punto di fine di una feature.
+
+## Squashing
+
+Usando git è anche possibile eseguire in fase di merge lo _squashing_ dei commit, ovvero la fusione di tutti i commit del branch entrante in uno solo. 
+Questa operazione è utile per migliorare la leggibilità della history dei commit su branch grossi (come `develop` o `master`) ma il suo uso in GitFlow è opinabile: il prof. Bellettini consiglia di non utilizzarlo, in modo da mantenere i commit originali.
+
+## Release
+
+![GitFlow release](/assets/06_gitflow-release.png)
+
+Lo scopo di creare una release è __cristalizzare l'insieme delle funzionalità__ presente sul branch `develop` all'inizio di essa dedicandosi solo alla sistemazione degli errori o alle attività necessarie per il deploy (modifica del numero di versione, ...). 
+L'insieme delle funzionalità rilasciate è quello presente sul branch `develop` al momento di inizio di una release. 
+
+I bug fix possono essere ri-mergiati in `develop`, anche utilizzando la funzionalità __cherry-pick__ di git; essa permette di selezionare un commit specifico da un ramo e applicarlo in un altro ramo. 
+Ad esempio, se si ha un ramo di sviluppo ("`develop`") e un ramo di release ("`release`"), è possibile utilizzare il cherry-pick per selezionare solo i commit che contengono bugfix e applicarli al ramo di release, senza dover fare un merge di tutto il ramo di sviluppo. 
+Ciò può essere utile in casi in cui si vuole mantenere la stabilità del ramo di release, includendo solo i bugfix considerati essenziali per la release.
+
+Per iniziare una nuova release è sufficiente creare un nuovo branch da `develop`:
+```bash
+$ git checkout -b release/v1.2.3 develop
+```
+
+Al termine della creazione della release, è necessario fare il merge della release nel branch `master` e nel branch `develop`. 
+Il merge in `master` rappresenta il rilascio della nuova versione del codice, che diventa disponibile per il pubblico o per il cliente. 
+Il merge in `develop`, invece, integra le modifiche apportate durante la creazione della release nel branch di sviluppo, in modo che siano disponibili per le release future. 
+In questo modo, è possibile gestire in modo organizzato il ciclo di vita del codice e il flusso di lavoro.
+```bash
+$ git checkout master               # entra nel branch master
+$ git merge --no-ff release/v1.2.3  # mergia la feature
+$ git tag -a v1.2.3                 # tagga sul branch master il rilascio
+$ git checkout develop              # entra nel branch develop
+$ git merge --no-ff release/v1.2.3  # mergia la feature
+$ git branch -d release/v1.2.3      # elimina il branch della feature
+```
+
+In git, i tag sono etichette che possono essere applicate a un commit per segnalarne l'importanza o per marcare un punto specifico dello storico del repository. Un __tag è un puntatore costante al commit__ a cui è stato applicato, quindi non cambia mai e permette di fare riferimento in modo stabile a una versione specifica del codice. Al contrario, i branch sono puntatori dinamici che vanno avanti nel tempo, seguendo l'evoluzione del codice attraverso i nuovi commit
+
+In GitFlow, le release sono versioni stabili del codice che vengono rilasciate al pubblico o al cliente. 
+Ogni release viene creata partendo dal branch `develop` e viene gestita come un branch a sé stante, che viene chiuso una volta che tutte le modifiche previste sono state integrate. 
+Al contrario, le feature sono branch temporanei utilizzati per sviluppare nuove funzionalità o per correggere bug. 
+È possibile avere più feature aperte contemporaneamente, ma solo una release rimanere aperta in un dato istante.
+
+## Hotfix
+
+Un _hotfix_ è una riparazione veloce di difetti urgenti senza dover aspettare la prossima release.
+È l'unico caso per cui non si parte da `develop`, ma dall'ultima - o una particolare - versione rilasciata su `master`.
+
+```bash
+$ git checkout -b hotfix/CVE-123 master # crea un branch di hotfix
+```
+
+Quando lo chiudo:
+```bash
+$ git checkout master                   # entra nel branch master
+$ git merge --no-ff hotfix/CVE-123      # mergia l'hotfix
+$ git tag -a hotfix/CVE-123             # tagga sul branch master il rilascio
+$ git checkout develop                  # entra nel branch develop
+$ git merge --no-ff hotfix/CVE-123      # mergia l'hotfix
+$ git branch -d hotfix/CVE-123          # elimina il branch di hotfix
+```
+
+# Limiti
+
+Quali sono i limiti di git presentato così?
+
+git e GitFlow come sono stati esposti presentano numerosi vincoli, tra cui:
+- la __mancanza di un sistema di autorizzazione granulare__, ovvero la possibilità di assegnare permessi in modo specifico e mirato a diverse funzionalità o risorse. Inoltre, non esiste una distinzione tra diversi livelli di accesso, quindi o si ha accesso completo a tutte le funzionalità o non si ha accesso a niente;
+- l'__assenza di code review__, ovvero il processo di revisione del codice sorgente da parte di altri sviluppatori prima che venga integrato nel codice base.
+
+## `git request-pull` &mdash; _generates a summary of pending changes_
+
+Il tool `git request-pull` è un comando di git che serve per formattare e inviare una proposta di modifiche a un repository tramite una mailing list. 
+Il comando crea un messaggio di posta elettronica che chiede al maintainer del repository di "pullare" le modifiche, ovvero di integrarle nel codice base. 
+Oggi, questa pratica è stata in molti progetti sostituita dalle pull request, che sono richieste di integrazione delle modifiche presentate attraverso un'interfaccia web. 
+Le pull request offrono una serie di vantaggi rispetto alle richieste via email, come una maggiore trasparenza del processo di integrazione, una maggiore efficienza e una maggiore facilità di utilizzo.
+
+La sintassi del comando è la seguente:
+
+    git request-pull [-p] <start> <URL> [<end>]
+
+Per esempio, i contributori Linux usano questo strumento per chiedere a Linus Torvalds di unire le modifiche nella sua versione.
+L'output generato mostra file per file le modifiche fatte e i commenti dei commit creati, raggruppati per autore.
+
+```
+$ git request-pull master git@gitlab.di.unimi.it:silab-gang/sweng.git lezioni/09
+The following changes since commit a16f3a0488c062d7b61dc4af15c2ba8081166040:
+
+  Handle '/sweng' path (2022-11-25 19:14:40 +0100)
+
+are available in the git repository at:
+
+  git@gitlab.di.unimi.it:silab-gang/sweng.git lezioni/09
+
+for you to fetch changes up to 4ac534bcd31c8c0353070c3f42eea09737b497b5:
+
+  Refactor notes on Factory method pattern (2022-12-19 09:58:32 +0100)
+
+----------------------------------------------------------------
+Daniele Ceribelli (6):
+      Add pattern Adapter
+      Add notes until decorator
+      Finish notes
+      Add notes until factory method pattern
+      Add notes until Abstract Factory pattern
+      Finish notes with all patterns
+
+Marco Aceti (14):
+      Add lesson notes
+      Add flyweight pattern
+      Add observer pattern
+      Add more patterns
+      Fix typo
+      Merge branch 'master' into lezioni/09
+      Replace hook UMLs images with PlantUML
+      Merge branch 'master' into lezioni/09
+      Fix gitLab CI (take 2)
+      Refactor diagrams until 'Strategy'
+      Refactor all remaining diagrams
+      Merge remote-tracking branch 'gitlab/master' into lezioni/09
+      Merge remote-tracking branch 'gitlab/master' into lezioni/09
+      Make 'Decorator' code samples *readable*
+
+Matteo Mangioni (18):
+      Add introduction
+      Integrate Singleton notes
+      Add images for first patterns
+      Integreate Iterator notes
+      Integrate Chain of Responsibility notes
+      Integrates FlyWeight notes
+      Add NullObject pattern
+      Merge branch 'lezioni/09' of gitlab.com:silab-gang/sweng into lezioni/09
+      Merge branch 'mangio/patterns-appunti' into lezioni/09
+      Integrate Strategy section
+      Integrate Observer notes
+      Refactor notes on pattern Adapter
+      Refactor notes on pattern Facade
+      Make already reviewed notes follow new style guide
+      Refactor notes on pattern Composite
+      Refactor notes on pattern Decorator
+      Refactor notes on pattern State
+      Refactor notes on Factory method pattern
+
+ _posts/2022-10-26-Patterns.md               | 1301 ++++++++++++++++++++++++++
+ assets/09_esempio-abstract-factory.png      |  Bin 0 -> 2362937 bytes
+ assets/09_facade.png                        |  Bin 0 -> 73981 bytes
+ assets/09_model-view-controller.png         |  Bin 0 -> 2751267 bytes
+ assets/09_model-view-presenter.png          |  Bin 0 -> 2118448 bytes
+ assets/09_nullObject-valori-non-assenti.png |  Bin 0 -> 67792 bytes
+ 6 files changed, 1301 insertions(+)
+ create mode 100644 _posts/2022-10-26-Patterns.md
+ create mode 100644 assets/09_esempio-abstract-factory.png
+ create mode 100644 assets/09_facade.png
+ create mode 100644 assets/09_model-view-controller.png
+ create mode 100644 assets/09_model-view-presenter.png
+ create mode 100644 assets/09_nullObject-valori-non-assenti.png
+```
+
+Questo modello è molto più _peer to peer_ delle pull request proposte dai sistemi semi-centralizzati spiegati in seguito.
diff --git a/src/06_git-workflow/02_hosting-centralizzato.md b/src/06_git-workflow/02_hosting-centralizzato.md
new file mode 100644
index 0000000000000000000000000000000000000000..267ca16b0df7b3da30315b76e670776fe0794529
--- /dev/null
+++ b/src/06_git-workflow/02_hosting-centralizzato.md
@@ -0,0 +1,32 @@
+# Hosting centralizzato
+
+Un hosting centralizzato Git è un servizio che fornisce una repository centrale per i progetti Git dove i contributi vengono integrati e gestiti, garantendo una maggiore trasparenza e controllo del processo di sviluppo e mantenendo molti vantaggi della decentralizzazione, come la possibilità di lavorare in modo asincrono e autonomo.
+
+Gli hosting centralizzati come GitHub e GitLab, nella loro costante evoluzione, spesso inventano nuovi meccanismi e provano a imporre nuovi workflow, come il GitHub Flow o il GitLab Flow, per semplificare e ottimizzare il processo di sviluppo. Tuttavia, è importante valutare attentamente questi nuovi approcci e verificare se si adattano alle esigenze specifiche del progetto e della squadra di sviluppo.
+Inoltre, molti servizi di hosting centralizzati offrono funzionalità aggiuntive, come la possibilità di eseguire il "fork" di un repository, inviare _pull request_ per le modifiche e di utilizzare strumenti di continuous integration (CI) per testare automaticamente le modifiche apportate al codice.
+
+## Fork
+
+Il "fork" di un repository Git è una __copia del repository originale__ che viene creata su un account di hosting diverso dal proprietario originale. 
+Questo permette a un altro sviluppatore di creare una copia del repository e di lavorare su di essa senza influire sul lavoro del proprietario originale e __senza la sua autorizzazione__. 
+È possibile quindi mantenere una _connessione_ tra i due repository e condividere facilmente le modifiche apportate.
+
+La maggioranza delle piattaforme di hosting centralizzato __ottimizza la condivisione dello spazio degli oggetti__, utilizzando un'unica _repository fisica_ per tutti i fork.
+Tuttavia, questo può comportare alcune problematiche di sicurezza, come ad esempio la difficoltà per la piattaforma di stabilire in quale fork si trova un determinato oggetto in caso di conflitto o la possibilità che un utente malintenzionato possa modificare o eliminare accidentalmente oggetti di altri fork. 
+Per questo motivo, è importante che le piattaforme implementino __misure di sicurezza adeguate__ per proteggere i dati dei fork e garantire la tracciabilità delle modifiche ([esempio sul kernel Linux](https://github.com/torvalds/linux/commit/b4061a10fc29010a610ff2b5b20160d7335e69bf)). 
+
+## Review / Pull request
+
+Tra la creazione di una pull request e il suo _merge_, specialmente nei progetti open source (dove chiunque può proporre qualsiasi patch) è fondamentale prevedere un processo di __review__.
+
+![Pull request](/assets/06_pull-request.png)
+
+La funzionalità di _review/pull request_ permette di facilitare le interazioni tra gli sviluppatori utilizzando il sito di hosting come luogo comune per la discussione informale e la revisione delle modifiche.
+
+## Continous integration (CI)
+
+Come accennato in precedenza, molti servizi di hosting centralizzati offrono strumenti di __continuous integration__ (CI) che possono essere utilizzati per testare automaticamente le modifiche proposte nella pull request. 
+Questi strumenti consentono di verificare che le modifiche non introducano errori o vulnerabilità e di garantire che il codice sia pronto per essere integrato nel repository principale.
+Possono essere utilizzati anche per eseguire automaticamente la _suite di test_ o automatizzare il deployment.
+
+![CI/CD](/assets/06_ci-cd.png)
diff --git a/src/06_git-workflow/03_gerrit.md b/src/06_git-workflow/03_gerrit.md
new file mode 100644
index 0000000000000000000000000000000000000000..a164067d8d4ccb5feeec8bfe719c891315134537
--- /dev/null
+++ b/src/06_git-workflow/03_gerrit.md
@@ -0,0 +1,33 @@
+# Gerrit
+
+Gerrit è un __sistema di review__ del codice sviluppato internamente da Google per gestire i progetti interni; si basa sul concetto di "peer review": tutti gli sviluppatori sono autorizzati a fare review delle proposte di modifica di qualsiasi zona di codice.
+
+Nel processo di review di Gerrit, i __developer__ possono sottoporre proposte di cambiamento utilizzando un sistema di "patch" che descrive le modifiche apportate al codice. 
+I __reviewer__, ovvero gli altri sviluppatori del progetto, possono quindi esaminare le patch e decidere se accettarle o rifiutarle. 
+Una volta che una patch ha ricevuto un numero sufficiente di review positivi, viene automaticamente integrata nel __repository principale autoritativo__   in cui tutti hanno accesso in sola lettura.
+
+Gerrit obbliga a strutturare le modifiche (_changeset_) in un unico commit (tecnica _squash_) al momento dell'accettazione. 
+Ciò significa che tutte le modifiche apportate devono essere fuse in un unico commit, in modo da rendere più facile la gestione del repository. 
+Al momento della review, invece, le modifiche rimangono separate in versioni singole, ovvero ogni modifica viene presentata come un commit separato, in modo che i reviewer possano esaminarle più facilmente.
+
+## Verifier
+
+Il verifier è uno strumento o un processo che viene utilizzato in Gerrit per verificare che le modifiche proposte siano corrette e funzionino come dovrebbero. 
+In particolare, il verifier scarica la patch, la compila, esegue i test e controlla che ci siano tutte le funzioni necessarie. 
+Se il verifier rileva dei problemi, può segnalarli al team di sviluppo perché vengano corretti prima che la patch venga accettata.
+
+Una volta terminato il proprio processo, approva le modifiche votandole positivamente.
+Solitamente sono necessari 1 o 2 voti per procedere.
+
+## Approver
+
+Una volta verificata, una proposta di modifiche deve essere anche approvata. 
+L'approvatore deve determinare la risposta alle seguenti domande riguardo la proposta di modifiche:
+- _è valida per lo scopo del progetto?_
+- _è valida per l'architettura del progetto?_
+- _introduce nuove falle nel design che potrebbero causare problemi in futuro?_
+- _segue le best practices stabilite dal progetto?_
+- _è un buon modo per implementare la propria funzione?_
+- _introduce rischi per la sicurezza o la stabilità?_
+
+Se l'approver ritiene che la proposta di modifiche sia valida, può approvarla scrivendo "LGTM" (acronimo di _"Looks Good To Me"_) nei commenti della pull request.
diff --git a/src/06_git-workflow/04_strumenti-opensource/00_index.md b/src/06_git-workflow/04_strumenti-opensource/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..56991094e758ebe4f6783410c49ee8cfddfe24dd
--- /dev/null
+++ b/src/06_git-workflow/04_strumenti-opensource/00_index.md
@@ -0,0 +1,7 @@
+# Strumenti dell'open source
+
+Gli strumenti dell'open source sono una serie di programmi, librerie e servizi che vengono utilizzati per sviluppare progetti open source. 
+Questi strumenti sono pensati per semplificare il processo di sviluppo e gestione di progetti open source, rendendoli accessibili a una comunità di sviluppatori e contribuenti.
+
+- [**Build automation**](./01_build-automation.md): `make`, Ant e Gradle
+- [**Bug tracking**](./02_bug-tracking.md): tecniche di bug workflow
diff --git a/src/06_git-workflow/04_strumenti-opensource/01_build-automation.md b/src/06_git-workflow/04_strumenti-opensource/01_build-automation.md
new file mode 100644
index 0000000000000000000000000000000000000000..ffed93c01568f8d96c8b3918e218b0978d18cee4
--- /dev/null
+++ b/src/06_git-workflow/04_strumenti-opensource/01_build-automation.md
@@ -0,0 +1,127 @@
+# Build automation
+
+La build automation è un processo fondamentale nello sviluppo di software open source, che consiste nel creare un sistema automatizzato per compilare il codice sorgente in un eseguibile. 
+Questo processo è importante perché consente di risparmiare tempo e risorse, evitando di dover compilare manualmente il codice ogni volta che si apportano modifiche. 
+Inoltre, la build automation garantisce una maggiore qualità e coerenza del software, poiché il processo di compilazione viene eseguito in modo uniforme ogni volta.
+
+## `make`
+
+`make` è uno strumento di build automation che viene utilizzato per automatizzare il processo di compilazione di un progetto. 
+In particolare, `make` viene utilizzato per specificare come ottenere determinati _targets_ (obiettivi), ovvero file o azioni che devono essere eseguite, partendo dal codice sorgente. 
+Ad esempio, in un progetto di sviluppo software, un _target_ potrebbe essere il file eseguibile del programma, che viene ottenuto compilando il codice sorgente. 
+`make` segue la filosofia _pipeline_, ovvero prevede l'utilizzo di singoli comandi semplici concatenati per svolgere compiti più complessi.
+
+È supportata la _compilazione incrementale_, ovvero il fatto di compilare solo le parti del progetto che sono state modificate dall'ultima volta, al fine di velocizzare il processo. 
+Inoltre, vengono gestite le _dipendenze_ tra file, ovvero le relazioni tra i diversi file che compongono il progetto: se un file sorgente dipende da un altro file, make assicura che il file dipendente venga compilato solo dopo che il file da cui dipende è stato compilato. 
+Ciò garantisce che il progetto venga compilato in modo coerente e che le modifiche apportate a un file siano considerate correttamente nella compilazione dei file dipendenti.
+
+```make
+CC=gcc
+CFLAGS=-I.
+
+%.o: %.c $(DEPS)
+  $(CC) -c -o $@ $< $(CFLAGS)
+
+hellomake: hellomake.c hellofunc.o
+  $(CC) -o hellomake hellomake.o hellofunc.o $< $(CFLAGS)
+```
+
+Nell'esempio, se il _target_ hellomake (definito dai file `hellomake.c` e `hellofunc.o`) è stato aggiornato, occorre ricompilarlo utilizzando i comandi sotto.
+
+Tuttavia, make lavora a un livello molto basso, il che può rendere facile commettere errori durante la sua configurazione e utilizzo.
+
+Non c'è portabilità tra macchine (ambienti) diverse.
+
+### `Makefile`
+
+Un _Makefile_ è un file di testo che contiene le istruzioni per il programma make su come compilare e linkare i file sorgente di un progetto. 
+Ogni riga del Makefile definisce un obiettivo o una dipendenza, insieme ai comandi che devono essere eseguiti per raggiungerlo. 
+L'utilizzo del Makefile permette di automatizzare la compilazione e il linkaggio dei file sorgente, semplificando il processo di sviluppo di un progetto. 
+Nell'esempio menzionato, il Makefile definisce il target `hellomake`, che dipende dai file `hellomake.c` e `hellofunc.o`, e fornisce i comandi per compilarli e linkarli insieme.
+
+### Generazione automatica
+
+Sono stati creati dei tool (`automake`, `autoconf`, `imake`, ...) che _generano_ `Makefile` ad-hoc per l'ambiente attuale.
+
+Il _mantra_:
+```bash
+$ ./configure
+$ make all
+$ sudo make install
+```
+era largamente utilizzato per generare un Makefile ad-hoc per l'ambiente attuale e installare il software sulla macchina in modo automatico. 
+`automake`, `autoconf`, e `imake` sono strumenti che aiutano a questo scopo, generando Makefile che possono essere utilizzati per compilare e installare il software in modo automatico.
+
+## Ant 
+Ant nasce in Apache per supportare il progetto Tomcat.
+Data una __definizione in XML__ della struttura del progetto e delle dipendenze invocava comandi programmati tramite classi Java per compilare il progetto.
+
+Il vantaggio è che Java offre un livello d'astrazione sufficiente a rendere il sistema di build portabile su tutte le piattaforme.
+
+Nella versione base supporta integrazioni con altri tool come CVS, Junit, FTP, JavaDOCS, JAR, ecc...
+Non solo compila, ma fa anche deployment.
+Il deployment consiste nell'installare e configurare un'applicazione o un sistema su uno o più server o ambienti di esecuzione. 
+Nel contesto di Ant, il deployment può includere l'invocazione di comandi per copiare i file del progetto sui server di destinazione, configurare le impostazioni di sistema o dell'applicazione, avviare o fermare servizi o processi, e così via. 
+In questo modo, Ant può essere utilizzato non solo per compilare il progetto, ma anche per distribuirlo e rendere disponibile l'applicazione o il sistema ai suoi utenti.
+
+I target possono avere dipendenze da altri target.
+I target contengono task che fanno effettivamente il lavoro; si possono aggiungere nuovi tipi di task definendo nuove classi Java.
+
+Esempio di un build file:
+
+```xml
+<?xml version="1.0"?>
+<project name="Hello" default="compile">
+  <target name="clean" description="remove intermediate files">
+    <delete dir="classes" />
+  </target>
+  <target name="clobber" depends="clean" description="remove all artifact files">
+    <delete file="hello.jar">
+  </target>
+  <target name="compile" description="compile the Java source code to class files">
+    <mkdir dir="classes" />
+    <javac srcdir="." destdir="classes" />
+  </target>
+  <target name="jar" depends="compile" description="create a Jar file for the application">
+    <jar destfile="hello.jar">
+      <fileset dir="classes" includes="**/*.class" />
+      <manifest>
+        <attribute name="Main-Class" value="HelloProgram" />
+      </manifest>
+    </jar>
+  </target>
+</project>
+```
+
+## Gradle
+
+Gradle è uno strumento di build automation che utilizza le repository Maven come punto di accesso alle librerie di terze parti. 
+Maven è una piattaforma di gestione delle dipendenze e della build automation per il linguaggio di programmazione Java. 
+Le repository Maven sono archivi online che contengono librerie Java, plugin e altri componenti utilizzati nella build di progetti Java. 
+Gradle utilizza queste repository per cercare e scaricare le librerie di cui ha bisogno per eseguire la build del progetto.
+
+Gradle, che supporta Groovy o Kotlin come linguaggi di scripting, adotta un approccio dichiarativo e fortemente basato su convenzioni. 
+Ciò significa che tutto ciò che è già stato definito come standard non deve essere ridichiarato. 
+Inoltre, Gradle definisce un linguaggio specifico per la gestione delle dipendenze e permette di creare build multi-progetto.
+
+Gradle scala bene in complessità: permette di fare cose semplici senza usare le funzioni complesse. 
+È estendibile tramite plugin che servono per trattare tool, situazioni, linguaggi legati solitamente al mondo Java.
+
+### Plugin
+
+I plugin servono per trattare tool, situazioni, linguaggi definendo task e regole per lavorare più facilmente.
+
+Il plugin _Java_ definisce:
+- una serie di __sourceSet__, ovvero dove è presente il codice e le risorse. Principalmente sono:
+  - `src/main/java`: sorgenti Java di produzione;
+  - `src/main/resources`: risorse di produzione;
+  - `src/test/java`: sorgenti Java di test;
+  - `src/test/resources`: risorse di test.
+- dei __task__, anche con dipendenze tra loro.
+
+![Task gradle](/assets/06_gradle-tasks.png)
+
+### Altri plugin
+- application, per l'esecuzione;
+- FindBugs, jacoco: per la verifica e la convalida;
+- eclipse, idea: per integrazione con gli IDE;
\ No newline at end of file
diff --git a/src/06_git-workflow/04_strumenti-opensource/02_bug-tracking.md b/src/06_git-workflow/04_strumenti-opensource/02_bug-tracking.md
new file mode 100644
index 0000000000000000000000000000000000000000..a866cfb09ee9735370c31fa425d907782cd76df9
--- /dev/null
+++ b/src/06_git-workflow/04_strumenti-opensource/02_bug-tracking.md
@@ -0,0 +1,24 @@
+# Bug tracking
+
+Il bug tracking è stato reso necessario nel mondo open source per via della numerosità dei contributi e della alta probabilità di avere segnalazioni duplicate.
+
+Inoltre, per gestire le segnalazioni di bug nell'ambito dello sviluppo open source, esistono diversi strumenti come git-bug, BugZilla, Scarab, GNATS, BugManager e Mantis.
+
+## Bug workflow
+
+![Bug workflow](/assets/06_bug-workflow.png)
+
+L'obiettivo del bug tracking è avere più informazioni possibili su ogni bug per saperli riprodurre e quindi arrivare a una soluzione.
+
+È importante verificare i bug una volta che l'_issue_ è stato aperto, in modo da poter confermare la sua esistenza e la completezza delle informazioni fornite.
+
+Un _issue_ è un problema o una richiesta di funzionalità segnalata all'interno di un progetto di software. 
+Gli issue vengono solitamente utilizzati per tenere traccia dei problemi noti o delle richieste di nuove funzionalità all'interno di un progetto, e possono essere gestiti attraverso un sistema di bug tracking o gestione delle richieste. 
+Gli issue possono essere aperti da qualsiasi membro del team o dalla comunità, e possono essere risolti o chiusi da un membro del team responsabile.
+
+Ci sono diversi modi per cui può essere chiuso un bug:
+- __duplicate__: quando è stato già segnalato in precedenza e quindi non rappresenta un problema nuovo. In questo caso, viene solitamente fatto riferimento al numero del bug originale che ha già ricevuto una risoluzione;
+- __wontfix__: il bug viene chiuso come "non risolvibile" perché o rappresenta una funzionalità voluta dal progetto o è troppo complesso da risolvere per essere considerato conveniente farlo dal punto di vista dei progettisti;
+- __can't reproduce__: non è stato possibile riprodurre il bug, ovvero che non è stato possibile ottenere lo stesso risultato o il comportamento segnalato dal bug. Ciò può essere dovuto a una mancanza di dettagli o a un errore nella segnalazione del bug stesso;
+- __fixed__: il bug è stato fixato;
+vs __fix verified__: il fix è stato integrato in una release passando tutti gli step di verifica.
diff --git a/src/07_progettazione/00_index.md b/src/07_progettazione/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..23dc9522f6de2c6c7e52b0cf9b83d7a46f502ebe
--- /dev/null
+++ b/src/07_progettazione/00_index.md
@@ -0,0 +1,68 @@
+# Progettazione
+
+Durante le lezioni, per discutere di progettazione siamo partiti da un esempio di programma in C che stampa una canzone.
+Il codice considerato è completamente illegibile:
+
+```c
+#include <stdio.h>
+main(t,_,a)
+char *a;
+{
+return!0<t?t<3?main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a)):
+1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13?
+main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72?main(_,t,
+"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#\
+;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l \
+q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# \
+){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' \
+iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c \
+;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# \
+}'+}##(!!/")
+:t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1)
+:0<t?main(2,2,"%s"):*a=='/'||main(0,main(-61,*a,
+"!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry"),a+1);
+} 
+```
+
+Successivamente abbiamo scomposto il codice per renderlo logicamente più sensato e facilmente modificabile, sono state __estratte le parti comuni__ e spostate in una funzione apposita, mentre le __parti mutabili sono state salvate in alcune strutture dati__; la canzone viene così stampata tramite un ciclo. 
+In questo modo scrivendo un codice più semplice siamo stati in grado di creare una soluzione più generale e più aperta ai cambiamenti.
+
+```java
+public class TwelveDaysOfChristmas {
+    static String[] days = {"first", "second", ..., "twelfth"};
+    static String[] gifts = { "a partdrige in a pear tree", "two turtle doves", ... };
+
+    static String firstLine(int day) {
+        return "On the " + days[day] +
+               " day of Christmas my true love gave to me:\n";
+    }
+
+    static String allGifts(int day) {
+        if (day == 0) {
+            return "and " + gifts[0];
+        } else {
+            return gifts[day] + "\n" + allGifts(day-1);
+        }
+    }
+
+    public static void main(String[] args) {
+        System.out.println(firstLine(0));
+        System.out.println(gifts[0]);
+        for (int day == 1; day < 12; day++) {
+            System.out.println(firstLine(day));
+            System.out.println(allGifts(day));
+        }
+    }
+}
+```
+
+È importante quindi __adottare la soluzione più semplice__ (che __non è quella più stupida__!) e una misura convenzionale per dire quanto una cosa è semplice - almeno in Università - si esprime in termini del tempo dedicato dal programmatore all'implementazione.
+Tale misura si sposa bene con il __TDD__, che richiede __brevi iterazioni__ di circa 10 minuti: se la feature attuale richiede più tempo è opportuno ridurre la portata scomponendo il problema.
+
+- [**Refactoring**](./01_refactoring.md): modifiche al codice senza nuove funzionalità;
+- [**Design knowledge**](./02_design-knowledge.md): dove mantenere la conoscenza del design?
+- [**Conoscenze preliminari**](./03_conoscenze-preliminari.md): object orientation, SOLID principles, reference escaping, encapsulation e information hiding, immutabilità, code smell
+- [**Principio Tell-Don't-Ask**](./04_tell-dont-ask.md)
+- [**Interface segregation**](./05_interface-segregation.md)
+- [**Esempio**](./06_esempio/00_index.md) applicando i principi precedenti
+- [**Analisi del testo naturale**](./07_analisi-testo-naturale.md) con _noun extraction_
diff --git a/src/07_progettazione/01_refactoring.md b/src/07_progettazione/01_refactoring.md
new file mode 100644
index 0000000000000000000000000000000000000000..300e41bdd44a37b41ee9428b1671ac895bf7d582
--- /dev/null
+++ b/src/07_progettazione/01_refactoring.md
@@ -0,0 +1,16 @@
+# Refactoring
+Durante il refactoring è opportuno rispettare le seguenti regole:
+- le __modifiche al codice non devono modificare le funzionalità__:
+il refactoring DEVE essere invisibile al cliente;
+- __non possono essere aggiunti test aggiuntivi__ rispetto alla fase verde appena raggiunta.
+
+Se la fase di refactoring sta richiedendo troppo tempo allora è possibile fare rollback all'ultima versione verde e __pianificare meglio__ l'attività di refactoring, per esempio scomponendolo in più step.
+Vale la regola del _"do it twice"_: il secondo approccio a un problema è solitamente più veloce e migliore.
+
+## Motivazioni
+
+Spesso le motivazioni dietro un refactoring sono:
+- precedente __design molto complesso e poco leggibile__, a causa della velocità del passare ad uno _scenario verde_;
+- __preparare il design di una funzionalità__ che non si integra bene in quello esistente; dopo aver raggiunto uno _scenario verde_ in una feature, è possibile che la feature successiva sia difficile da integrare. 
+In questo caso, se il _refactoring_ non è banale è bene fermarsi, tornare indietro e evolvere il codice per facilitare l'iterazione successiva (__design for change__). 
+- presenza di __debito tecnico__ su lavoro fatto in precendenza, ovvero debolezze e "scorciatoie" che ostacolano notevolmente evoluzioni future: _"ogni debito tecnico lo si ripaga con gli interessi"_.
diff --git a/src/07_progettazione/02_design-knowledge.md b/src/07_progettazione/02_design-knowledge.md
new file mode 100644
index 0000000000000000000000000000000000000000..8710c09c115a3014a33c4874b7145820f81103ac
--- /dev/null
+++ b/src/07_progettazione/02_design-knowledge.md
@@ -0,0 +1,21 @@
+# Design knowledge
+
+La design knowledge è la __conoscenza del design__ architetturale di un progetto. 
+È possibile utilizzare:
+- la __memoria__: non è efficace perché nel tempo si erode, specialmente in coppia;
+- i __documenti di design__ (linguaggio naturale o diagrammi): se non viene aggiornato di pari passo con il codice rimane disallineato, risultando più dannoso che d'aiuto.
+- le __piattaforme di discussione__ (version control, issue management): possono aiutare ma le informazioni sono sparse in luoghi diversi e di conseguenza difficili da reperire e rimane il problema di mantenere aggiornate queste informazioni.
+- gli __UML__: tramite diagrammi UML si è cercato di sfruttare l'approccio ___generative programming___, ovvero la generazione automatica del codice a partire da specificazioni di diagrammi.
+Con l'esperienza si è visto che non funziona.
+- il __codice__ stesso: tramite la lettura del codice è possibile capire il design ma è difficile rappresentare le ragioni della scelta.
+
+È bene sfruttare tutte le tecniche sopra proposte __combinandole__, partendo dal codice. \
+È inoltre importante scrivere documentazione per spiegare le ragioni dietro le scelte effettuate e non le scelte in sé, che si possono dedurre dal codice.
+
+## Condivisione
+
+Per condividere tali scelte di design (il _know how_) è possibile sfruttare:
+- __metodi__: con pratiche (come Agile) o addirittura l'object orientation stessa, che può essere un metodo astratto per condividere scelte di design.
+- __design pattern__: fondamentali per condividere scelte di design, sono utili anche per generare un vocabolario comune (sfruttiamo dei nomi riconosciuti da tutti per descrivere i ruoli dei componenti) e aiutano l'implementazione (i pattern hanno delle metodologie note per essere implementati). 
+I pattern non si concentrano sulle prestazioni di un particolare sistema ma sulla generalità e la riusabilità di soluzioni a problemi comuni;
+- __principi__: per esempio i principi SOLID.
diff --git a/src/07_progettazione/03_conoscenze-preliminari.md b/src/07_progettazione/03_conoscenze-preliminari.md
new file mode 100644
index 0000000000000000000000000000000000000000..4820c07e2ab7b73008f63884649749efdcd93689
--- /dev/null
+++ b/src/07_progettazione/03_conoscenze-preliminari.md
@@ -0,0 +1,110 @@
+# Conoscenze preliminari
+
+Prima di proseguire è bene richiamare concetti e termini fondamentali presumibilmente visti durante il corso di Programmazione II.
+
+## Object orientation
+
+Per essere definito _object oriented_, un linguaggio di programmazione deve soddisfare tre proprietà:
+- __ereditarietà__: ovvero la possibilità di poter definire una classe ereditando proprietà e comportamenti di un'altra classe.
+- __polimorfismo__: quando una classe può assumere diverse forme in base alle interfacce che implementa. 
+Il prof fa l'esempio del _tennista scacchista_: in un torneo di tennis è poco utile sostituire una persona che gioca a tennis ed è brava con gli scacchi (quindi una classe che implementa entrambe le interfacce) con una che gioca a scacchi.
+Il collegamento tra capacità e oggetto è fatto __a tempo di compilazione__: non è importante quindi se la capacità non è ancora definita;
+- __collegamento dinamico__: in Java il tipo concreto degli oggetti e quindi il problema di stabilire _quale metodo chiamare_ viene risolto durante l'esecuzione. 
+In C++ occorre esplicitare questo comportamento utilizzando la keyword `virtual`.
+
+## <a style="color: darkgreen">SOLID</a> principles
+
+Ci sono 5 parti che compongono questo principio:
+1. __<span style="color: darkgreen"><big>S</big></span>INGLE RESPONSIBILITY__: una classe, un solo scopo.
+Così facendo, le classi rimangono semplici e si agevola la riusabilità.
+2. __<span style="color: darkgreen"><big>O</big></span>PEN-CLOSE PRINCIPLE__:
+le classi devono essere aperte ai cambiamenti (_opened_) ma senza modificare le parti già consegnate e in produzione (_closed_).
+Il refactoring è comunque possibile, ma deve essere preferibile estendere la classe attuale.
+3. __<span style="color: darkgreen"><big>L</big></span>ISKOV SUBSTITUTION PRINCIPLE__:
+c'è la garanzia che le caratteristiche eredidate dalla classe padre continuinino ad esistere nelle classi figlie.
+Questo concetto si collega all'aspetto __contract-based__ del metodo Agile: le _precondizioni_ di un metodo di una classe figlia devono essere ugualmente o meno restrittive del metodo della classe padre.
+Al contrario, le _postcondizioni_ di un metodo della classe figlia non possono garantire più di quello che garantiva il metodo nella classe padre.
+Fare _casting_ bypassa queste regole.
+4. __<span style="color: darkgreen"><big>I</big></span>NTERFACE SEGREGATION__:
+più le capacità e competenze di una classe sono frammentate in tante interfacce più è facile utilizzarla in contesti differenti.
+In questo modo un client non dipende da metodi che non usa. 
+Meglio quindi avere __tante interfacce specifiche__ e piccole (composte da pochi metodi), piuttosto che poche, grandi e generali. 
+5. __<span style="color: darkgreen"><big>D</big></span>EPENDENCY INVERSION__:
+il codice dal quale una classe dipende non deve essere più __concreto__ di tale classe.
+Per esempio, se il _telaio della FIAT 500_ dipende da uno specifico motore, è possibile utilizzarlo solo per quel specifico motore.
+Se invece il telaio dipende da _un_ concetto di motore, non c'è questa limitazione.
+In conlusione, le classi concrete devono tendenzialmente dipendere da classi astratte e non da altre classi concrete.
+
+## Reference escaping
+
+Il _reference escaping_ è una violazione dell'incapsulamento. 
+
+Può capitare, per esempio: 
+- quando un getter ritorna un riferimento a un segreto;
+```java
+public Deck {
+    private List<Card> cards;
+        
+    public List<Card> getCards() {
+        return this.cards;
+    }
+}
+```
+- quando un setter assegna a un segreto un riferimento che gli viene passato;
+```java
+public Deck {
+    private List<Card> cards;
+
+    public setCards(List<Card> cards) {
+        this.cards = cards;
+    }
+}
+```
+
+- quando il costruttore assegna al segreto un riferimento che gli viene passato;
+```java
+public Deck {
+    private List<Card> cards;
+
+    public Deck(List<Card> cards) {
+        this.cards = cards;
+    }
+}
+```
+
+## Encapsulation e information hiding
+
+__Legge di Parnas (L8)__.
+> _Solo ciò che è nascosto può essere cambiato liberamente e senza pericoli._
+
+Lo stato mostrato all'esterno non può essere modificato, mentre quello nascosto sì.
+
+Questo principio serve per __facilitare la comprensione del codice__ e renderne più facile la modifica parziale senza fare danni.
+
+## Immutabilità
+
+Una classe è immutabile quando non c'è modo di modificare lo stato di ogni suo oggetto dopo la creazione.
+
+Per assicurare tale proprietà è necessario:
+- __non fornire metodi di modifica__ allo stato;
+- avere tutti gli __attributi privati__ per i tipi potenzialmente mutabili (come `List<T>`);
+- avere tutti gli __attributi final__ se non già privati;
+- assicurare l'__accesso esclusivo__ a tutte le parti non mutabili, ovvero non avere reference escaping.
+
+## Code smell
+
+I _code smell_ sono dei segnali che suggeriscono problemi nella progettazione del codice. 
+Di seguito ne sono elencati alcuni:
+- __codice duplicato__: si può fare per arrivare velocemente al verde ma è da togliere con il refactoring. 
+Le parti di codice in comune possono quindi essere fattorizzate.
+- __metodi troppo lunghi__: sono poco leggibili e poco riusabili;
+- __troppi livelli di indentazione__: scarsa leggibilità e riusabilità, è bene fattorizzare il codice;
+- __troppi attributi__: suggerisce che la classe non rispetta la single responsability, ovvero fa troppe cose;
+- __lunghe sequenze di _if-else_ o _switch___;
+- __classe troppo grande__;
+- __lista parametri troppo lunga__;
+- __numeri _magici___: è importante assegnare alle costanti numeriche all'interno del codice un nome per comprendere meglio il loro scopo;
+- __commenti che spiegano cosa fa il codice__: indicazione che il codice non è abbastanza chiaro;
+- __nomi oscuri o inconsistenti__;
+- __codice morto__: nel programma non deve essere presente del codice irraggiungibile o commentato. Utilizzando strumenti di versioning è possibile riaccedere a codice precedentemente scritto con facilità.
+- __getter e setter__: vedi principio di __tell don't ask__.
diff --git a/src/07_progettazione/04_tell-dont-ask.md b/src/07_progettazione/04_tell-dont-ask.md
new file mode 100644
index 0000000000000000000000000000000000000000..b7688cdba6a20b0072a6e541ceb98d1c502319b3
--- /dev/null
+++ b/src/07_progettazione/04_tell-dont-ask.md
@@ -0,0 +1,61 @@
+# <a href="https://martinfowler.com/bliki/TellDontAsk.html">Principio Tell-Don't-Ask</a>
+
+![Tell-Don't-Ask](/assets/07_tell-dont-ask.png)
+
+> Non chiedere i dati, ma dì cosa vuoi che si faccia sui dati
+
+Il responsabile di un'informazione è anche responsabile di tutte le operazioni su quell'informazione.
+
+Il principio _Tell-Don't-Ask_ sancisce che piuttosto di __chiedere__ ad un oggetto dei dati e fare delle operazioni con quei dati è meglio __dire__ a questo oggetto cosa fare con i dati che contiene. 
+
+## Esempio
+
+Se desideriamo stampare il contenuto di tutte le carte in un mazzo possiamo partire da questo codice.
+
+```java
+class Main {
+    public static void main(String[] args) {
+        Deck deck = new Deck();
+        Card card = new Card();
+
+        card.setSuit(Suit.DIAMONDS);
+        card.setRank(Rank.THREE);
+        deck.getCards().add(card);
+        deck.getCards().add(new Card());    // <-- !!!
+
+        System.out.println("There are " + deck.getCards().size() + " cards:");
+        for (Card currentCard : deck.getCards()) {
+            System.out.println(
+                currentCard.getRank() + 
+                " of " + 
+                currentCard.getSuit()
+            );
+        }
+    }
+}
+```
+
+All'interno del ciclo reperiamo gli attributi della carta e li utilizziamo per stampare le sue informazioni.
+Inoltre, nella riga evidenziata viene aggiunta una carta senza settare i suoi attributi. 
+La responsabilità della gestione dell'informazione della carta è quindi __erroneamente delegata__ alla classe chiamante.
+
+Per risolvere, è possibile trasformare la classe `Card` nel seguente modo:
+
+```java
+class Card {
+    private Suit suit;
+    private Rank rank;
+
+    public Card(@NotNull Suit s, @NotNull Rank r) {
+        suit = s;
+        rank = r;
+    }
+
+    @Override
+    public String toString() {
+        return rank + " of " + suit;
+    }
+}
+```
+
+l'informazione viene ora interamente gestita dalla classe `Card`, che la gestisce nel metodo `toString()` per ritornare la sua rappresentazione testuale.
diff --git a/src/07_progettazione/05_interface-segregation.md b/src/07_progettazione/05_interface-segregation.md
new file mode 100644
index 0000000000000000000000000000000000000000..22dcacce38c285b6a41c6da97b24b52e91fc6611
--- /dev/null
+++ b/src/07_progettazione/05_interface-segregation.md
@@ -0,0 +1,7 @@
+# Interface segregation
+
+Le interfacce possono _"nascere"_ tramite due approcci: 
+- __up front__: scrivere direttamente l'interfaccia;
+- __down front__: scrivere il codice e quindi tentare di estrarne un'interfaccia.
+
+L'approccio down-front si adatta meglio al TDD ed è illustrato nel seguente esempio.
diff --git a/src/07_progettazione/06_esempio/00_index.md b/src/07_progettazione/06_esempio/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..cfa990e9aa5fe07e04838ed37ecab1320dc8e514
--- /dev/null
+++ b/src/07_progettazione/06_esempio/00_index.md
@@ -0,0 +1,10 @@
+# Esempio con gerarchia Card / Deck
+
+In questo esempio sono trattati numerosi principi, come l'_interface segreagation_, _linking dinamico/statico_, _implementazione di interfacce multiple_ e il _contract based design_ vs la _programmazione difensiva_.
+
+- [**Interface segregation**](./01_interface-segregation.md)
+- [**Collegamento statico e dinamico**](./02_collegamento-statico-dinamico.md)
+- [**Loose coupling**](./03_loose-coupling.md)
+- [**Interfacce multiple**](./04_interfacce-multiple.md)
+- [**_Contract-based_ design vs programmazione difensiva**](./05_contract-based.md)
+- [**Classi astratte**](./06_classi-astratte.md)
diff --git a/src/07_progettazione/06_esempio/01_interface-segregation.md b/src/07_progettazione/06_esempio/01_interface-segregation.md
new file mode 100644
index 0000000000000000000000000000000000000000..0675e34edebde6df709590d550d07211c34b97e2
--- /dev/null
+++ b/src/07_progettazione/06_esempio/01_interface-segregation.md
@@ -0,0 +1,48 @@
+# Interface segregation all'opera
+
+```java
+public static List<Card> drawCards(Deck deck, int number) {
+    List<Card> result = new ArrayList<>();
+    for (int i = 0; i < number && !deck.isEmpty(); i++) {
+        result.add(deck.draw());
+    }
+    return result;
+}
+```
+
+Consideriamo il metodo `drawCards` che prende come parametri un `Deck` e un intero. \
+Le __uniche competenze__ riconosciute a `Deck` sono l'indicazione _se è vuoto_ (`isEmpty()`) e il _pescare una carta_ dal mazzo (`draw()`).
+`Deck` può quindi __implementare un'interfaccia__ che mette a disposizione queste capacità.
+
+È possibile modificare il metodo in modo da accettare un qualunque oggetto in grado di eseguire le operazioni sopra elencate, ovvero che implementi l'interfaccia __`CardSource`__. 
+
+<a id="cardsource" style="display:none;"></a>
+
+```java
+public interface CardSource {
+    /**
+     * @return The next available card.
+     * @pre !isEmpty()
+     */
+    Card draw();
+
+    /**
+     * @return True if there is no card in the source
+     */
+    boolean isEmpty();
+}
+```
+
+```java
+public class Deck implements CardSource { ... }
+```
+
+```java
+public static List<Card> drawCards(CardSource deck, int number) {
+    List<Card> result = new ArrayList<>();
+    for (int i = 0; i < number && !deck.isEmpty(); i++) {
+        result.add(deck.draw());
+    }
+    return result;
+}
+```
diff --git a/src/07_progettazione/06_esempio/02_collegamento-statico-dinamico.md b/src/07_progettazione/06_esempio/02_collegamento-statico-dinamico.md
new file mode 100644
index 0000000000000000000000000000000000000000..61782d566c5ca5aeba7ec6ce9750333446dcc2ea
--- /dev/null
+++ b/src/07_progettazione/06_esempio/02_collegamento-statico-dinamico.md
@@ -0,0 +1,8 @@
+# Collegamento statico e dinamico
+
+Notare come è necessario specificare __staticamente__ che `Deck` implementi `CardSource`, ovvero occorre forzare la dichiarazione del fatto che `Deck` sia un _sottotipo_ di `CardSource` (Java è strong typed) e quindi sia possibile mettere un oggetto `Deck` ovunque sia richiesto un oggetto `CardSource`. \
+In altri linguaggi come __Go__ c'è una __maggiore dinamicità__ perché non c'è bisogno di specificare nel codice che un oggetto è sottotipo di qualcos'altro, è sufficiente solo che implementi un metodo con la stessa signature.
+Il controllo che l'oggetto passato ad una funzione abbia le capacità necessarie avviene a runtime e non prima.
+
+Un problema della troppa dinamicità (__duck typing__) è che se i metodi di un oggetto non hanno dei nomi abbastanza specifici si possono avere dei problemi. 
+Per esempio, in un programma per il gioco del tennis se una funzione richiede un oggetto che implementa il metodo `play()`, e riceve in input un oggetto che non c'entra nulla con il tennis (per esempio un oggetto di tipo `GiocatoreDiScacchi`) che ha il metodo `play()`, si possono avere degli effetti indesiderati.
diff --git a/src/07_progettazione/06_esempio/03_loose-coupling.md b/src/07_progettazione/06_esempio/03_loose-coupling.md
new file mode 100644
index 0000000000000000000000000000000000000000..0ba63ea584e25acca485c9890c4f45ab5bd1f10f
--- /dev/null
+++ b/src/07_progettazione/06_esempio/03_loose-coupling.md
@@ -0,0 +1,8 @@
+# _Loose coupling_
+Il _loose coupling_ è la capacità di una variabile o un parametro di accettare l'assegnamento di oggetti aventi tipo diverso da quello della variabile o parametro, a patto che sia un sottotipo.
+
+```java
+Deck deck = new Deck();
+CardSource source = deck;
+List<Card> cards = drawCards(deck, 5);
+```
diff --git a/src/07_progettazione/06_esempio/04_interfacce-multiple.md b/src/07_progettazione/06_esempio/04_interfacce-multiple.md
new file mode 100644
index 0000000000000000000000000000000000000000..831f1eabdb27703e49c716ce2d6d823f7510b37c
--- /dev/null
+++ b/src/07_progettazione/06_esempio/04_interfacce-multiple.md
@@ -0,0 +1,42 @@
+# Interfacce multiple
+
+Tornando all'esempio, la classe `Deck` (che implementa `CardSource`) __può implementare anche altre interfacce__, come `Shuffable` o `Iterable<Card>`. 
+Al metodo precedente interessa solo che Deck abbia le capacità specificate in `CardSource`, se poi implementa anche altre interfaccie è ininfluente.
+
+```plantuml
+@startuml
+scale 1024 width
+
+class Client1
+class Client2
+class Client3
+
+interface Iterable<Card> << interface >> {
+    + {abstract} Iterator<Card> iterator()
+}
+
+class Deck implements Iterable, Shuffable, CardSource {
+    + void shuffle()
+    + Card draw()
+    + boolean isEmpty()
+    + Iterator<Card> iterator()
+}
+
+interface Shuffable << interface >> {
+    + {abstract} void shuffle()
+}
+
+interface CardSource << interface >> {
+    + {abstract} Card draw()
+    + {abstract} boolean isEmpty()
+}
+
+Client2 ..> Iterable
+Client1 ..> Shuffable
+Client3 ..> Iterable
+Client3 ..> CardSource
+
+hide empty fields
+hide empty methods
+@enduml
+```
\ No newline at end of file
diff --git a/src/07_progettazione/06_esempio/05_contract-based.md b/src/07_progettazione/06_esempio/05_contract-based.md
new file mode 100644
index 0000000000000000000000000000000000000000..d04decba42dc5dfbbf95e060b68c56f7ee9fd5a3
--- /dev/null
+++ b/src/07_progettazione/06_esempio/05_contract-based.md
@@ -0,0 +1,11 @@
+# _Contract-based_ design vs programmazione difensiva
+
+Tornando alla [specificazione](./01_interface-segregation.md#cardsource) dell'interfaccia di `CardSource`, è possibile notare dei commenti in formato Javadoc che specificano le __precondizioni__ e le __postcondizioni__ (il valore di ritorno) del metodo. Secondo il ___contract-based_ design__, esiste un _"contratto"_ tra chi implementa un metodo e chi lo chiama.
+
+Per esempio, considerando il metodo `draw()`, __è responsabilità del chiamante__ verificare il soddisfacimento delle precondizioni (_"il mazzo non è vuoto"_) prima di invocare il metodo.
+Se `draw()` viene chiamato quando il mazzo è vuoto ci troviamo in una situazione di __violazione di contratto__ e può anche esplodere la centrale nucleare.
+
+Per specificare il contratto si possono utilizzare delle __asserzioni__ o il `@pre` nei __commenti__. 
+Le prime sono particolarmenti utili in fase di sviluppo perché interrompono l'esecuzione del programma in caso di violazione, ma vengono solitamente rimosse in favore delle seconde nella fase di deployment.
+
+Un'altro approccio è la __programmazione difensiva__ che al contrario delega la responsabilità del soddisfacimento delle precondizioni al _chiamato_, e non al chiamante.
diff --git a/src/07_progettazione/06_esempio/06_classi-astratte.md b/src/07_progettazione/06_esempio/06_classi-astratte.md
new file mode 100644
index 0000000000000000000000000000000000000000..22795cfb6a67597fb3d533743b76c05757e8970f
--- /dev/null
+++ b/src/07_progettazione/06_esempio/06_classi-astratte.md
@@ -0,0 +1,29 @@
+# Classi astratte
+
+Una classe astratta che implementa un'interfaccia __non deve necessariamente implementarne__ tutti i metodi, ma può delegarne l'implementazione alle sottoclassi impedendo l'istanziamento di oggetti del suo tipo.
+
+Le interfacce diminuiscono leggermente le performance, però migliorano estremamente la generalità (che aiutano l'espandibilità ed evolvibilità del programma), quindi vale la pena di utilizzarle.
+
+È possibile utilizzare le __classi astratte__ anche per classi complete, ma che __non ha senso che siano istanziate__.
+Un buon esempio sono le classi _utility_ della libreria standard di Java.
+
+#### Classe utility della libreria standard di Java
+
+Un esempio è __`Collections.shuffle(List<?> list)`__ che accetta una lista omogenea di elementi e la mischia.
+Il _tipo_ degli elementi è volutamente ignorato in quanto non è necessario conoscerlo per mischiarli.
+
+Per l'__ordinamento__, invece, è necessario conoscere il tipo degli oggetti in quanto bisogna confrontarli tra loro per poterli ordinare.
+La responsabilità della comparazione è però delegata all'oggetto, che deve aderire all'interfaccia `Comparable<T>`.
+
+__`Collections.sort(...)`__ ha, infatti, la seguente signature:
+```java
+public static <T extends Comparable<? super T>> void sort(List<T> list)
+```
+
+La notazione di generico __aggiunge dei vincoli__ su `T`, ovvero il tipo degli elementi contenuti nella lista:
+- `T extends Comparable<...>` significa che `T` deve estendere - e quindi implementare - l'interfaccia `Comparable<...>`;
+- `Comparable<? super T>` significa che tale interfaccia può essere implementata su un antenato di `T` (o anche `T` stesso).
+
+`Comparable` è un altro esempio di _interface segregation_: serve per specificare che un oggetto ha bisogno della caratteristica di essere comparabile.
+
+__Digressione__: la classe Collections era l'unico modo per definire dei metodi sulle interfacce (es: dare la possibilità di avere dei metodi sulle collezioni, ovvero liste, mappe, ecc), ma ora si possono utilizzare i metodi di default.
diff --git a/src/07_progettazione/07_analisi-testo-naturale.md b/src/07_progettazione/07_analisi-testo-naturale.md
new file mode 100644
index 0000000000000000000000000000000000000000..205d29a5367c21e109a1dcbfddf19f25644d6ce3
--- /dev/null
+++ b/src/07_progettazione/07_analisi-testo-naturale.md
@@ -0,0 +1,148 @@
+# Analisi del testo naturale
+
+Come organizzare la partenza del design suddividendo in classi e responsabilità?
+
+I due approcci principali sono:
+- __pattern__: riconoscere una situazione comune da una data;
+- __TDD__: partendo dalla soluzione più semplice si definiscono classi solo all'occorrenza.
+
+Un'altra tecnica che vedremo è l'__estrazione dei nomi__ (noun extraction), per un certo senso _naive_ ma adatta in caso di storie complesse.
+
+## Noun extraction
+
+Basandosi sulle specifiche &mdash; come i commenti esplicativi delle User Stories &mdash; si parte dai sostantivi (o frasi sostantivizzate), si _sfolsticono_ con dei criteri, si cercano le relazioni tra loro e quindi si produce la gerarchia delle classi.
+
+Per spiegare il procedimento considereremo il seguente esempio:
+
+> - _The __library__ contains __books__ and __journals__.
+> It may have several __copies__ of a given book. 
+> Some of the books are for __short term loans__ only.
+> All other books may be borrowed by any __library member__ for three __weeks__._
+> - ___Members of the library__ can normally borrow up to six __items__ at a time, but __members of staff__ may borrow up to 12 items at one time.
+> Only member of staff may borrow journals._
+> - _The __system__ must keep track of when books and journals are borrowed and returned, enforcing the __rules__ described above._
+
+Nell'esempio sopra sono stati evidenziati i sostantivi e le frasi sostantivizzate.
+
+### Criteri di _sfoltimento_
+
+I criteri di _sfoltimento_ servono per diminuire il numero di sostantivi considerando solo quelli rilevanti per risolvere il problema.
+In questa fase, in caso di dubbi è possibile rimandare la decisione a un momento successivo.
+
+Di seguito ne sono riportati alcuni:
+- __Ridondanza__: sinonimi, termini diversi per indicare lo stesso concetto. Anche se è stata utilizzata una locuzione diversa potrebbe essere comunque ridondante, sopratutto in lingue diverse dall'inglese in cui ci sono molti sinonimi. \
+Nell'esempio: _library member_ e _member of the library_, _loan_ e _short term loan_.
+- __Vaghezza__: nomi generici, comuni a qualunque specifica; potrebbero essere sintomo di una _classe comune astratta_. \
+Nell'esempio: _items_.
+- __Nomi di eventi e operazioni__: nomi che indicano azioni e non hanno un concetto di _stato_. \
+Nell'esempio: _loan_.
+- __Metalinguaggio__: parti _statiche_ che fanno parte della logica del programma e che quindi non necessitano di essere rappresentati come classi. \
+Nell'esempio: _system_, _rules_.
+- __Esterne al sistema__: concetti esterni o verità _"assolute"_ al di fuori del controllo del programma. \
+Esempio: _library_, _week_ (una settimana ha 7 giorni).
+- __Attributi__: informazioni atomiche e primitive (stringhe, interi, ...) relative a una classe, che quindi non necessitano la creazione di una classe di per sé. \
+Esempio: _name of the member_ (se ci fosse stato).
+
+Al termine di questa fase, si avrà una lista di classi _"certe"_ e _"incerte"_.
+In questo esempio, sono soppravvisuti i termini _journal_, _book_, _copy_ (of _book_), _library member_ e _member of staff_. 
+
+### Relazioni tra classi
+
+Il prossimo passo è definire le relazioni tra le classi.
+
+Inizialmente, si collegano con delle _linee_ (non frecce) senza specificare la direzione dell'associazione.
+Parliamo di __associazioni__ e non __attributi__ perché non è necessariamente vero che tutte le associazioni si trasformino in attributi.
+
+```plantuml
+@startuml
+scale 1024 width
+
+class Book
+class CopyOfBook
+class LibraryMember
+class StaffMember
+class Journal
+
+Journal -- StaffMember : borrows/returns
+Book -- CopyOfBook : is a copy
+CopyOfBook -- LibraryMember : borrows/returns
+CopyOfBook -- StaffMember : borrows/returns
+
+hide empty fields
+hide empty methods
+@enduml
+```
+
+Il prossimo passo è specificare le __cardinalità__ delle relazioni, come specificato dal linguaggio UML (opposto in questo aspetto al diagramma ER).
+La precisione richiesta in questo punto è soggettiva: da una parte, specificare puntualmente il numero massimo di elementi di una associazione può aiutare ad ottimizzare successivamente, dall'altra porta confusione.
+
+```plantuml
+@startuml
+scale 1024 width
+
+class Book
+class CopyOfBook
+class LibraryMember
+class StaffMember
+class Journal
+
+Journal "0..*" -- "0..1" StaffMember : borrows/returns
+Book "1" -- "1..*" CopyOfBook : is a copy
+CopyOfBook "0..*" -- "0..1" LibraryMember : borrows/returns
+CopyOfBook "0..*" -- "0..1" StaffMember : borrows/returns
+
+hide empty fields
+hide empty methods
+@enduml
+```
+
+Dopo aver ragionato sulle cardinalità, si iniziano a cercare __generalizzazioni__ e fattorizzazioni. 
+In questo caso, notiamo che:
+- `StaffMember` _è un_ `LibraryMember` con in più la possibilità di prendere `Journal`. 
+Inoltre, un altro indicatore è che hanno la stesso tipo di relazioni con gli altri oggetti.
+- `Items` è un termine generico per indicare `CopyOfBook` e `Journal`.
+
+```plantuml
+@startuml
+scale 1024 width
+
+class BorrowableItem
+class LibraryMember
+class Book
+class CopyOfBook extends BorrowableItem
+class StaffMember extends LibraryMember
+class Journal extends BorrowableItem
+
+Book "1" -- "1..*" CopyOfBook : is a copy
+BorrowableItem "0..*" -- "0..1" LibraryMember : borrows/returns
+
+hide empty fields
+hide empty methods
+@enduml
+```
+
+Distinguere `CopyOfBook` e `Journal` è inutile, perché di fatto un `Journal` _è una copia di_ un giornale.
+Si può quindi fattorizzare __rimuovendo la generalizzazione__, come mostrato di seguito.
+
+```plantuml
+@startuml
+scale 1024 width
+
+class Book
+class BorrowableItem
+class LibraryMember
+class StaffMember extends LibraryMember
+class Journal
+
+Book "0..1" - "1..*" BorrowableItem : is a copy
+BorrowableItem "0..*" -- "0..1" LibraryMember : borrows/returns
+BorrowableItem "1..*" - "0..1" Journal : is a copy
+
+hide empty fields
+hide empty methods
+@enduml
+```
+
+È imporante però preoccuparsi delle __cardinalità__ delle relazioni: è sì vero che un `BorrowableItem` può non _essere una copia di_ un `Book` e di un `Journal`, ma deve essere copia di _esattamente_ una delle due opzioni.
+UML prevede un __linguaggio OCL__ ([Object Constraint Language](https://en.wikipedia.org/wiki/Object_Constraint_Language)) per esprimere vincoli divesamente impossibili da esprimere in un diagramma.
+È anche possibile scrivere il _constraint_ in linguaggio naturale come __nota__.
diff --git a/src/08_patterns/00_index.md b/src/08_patterns/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..bb9a813d0b5d4b5ada2562831ba266cf4f8e788e
--- /dev/null
+++ b/src/08_patterns/00_index.md
@@ -0,0 +1,27 @@
+# [Patterns](https://refactoring.guru/)
+
+Parlando di progettazione del software e di buone pratiche è impossibile non parlare di __design patterns__, soluzioni universalmente riconosciute valide a problemi di design ricorrenti: si tratta cioè di strumenti concettuali di progettazione che esprimono un'architettura vincente del software catturando la soluzione ad una famiglia di problemi.
+
+Ad ogni pattern sono associati una serie di __idiomi__, implementazioni del pattern specifiche per un certo linguaggio di programmazione che sfruttano i costrutti del linguaggio per realizzare l'architettura dettata dal pattern.
+Durante questa discussione vedremo alcuni idiomi per Java, che talvolta si discosteranno fortemente dalla struttura descritta dai diagrammi UML dei pattern.
+
+Ma attenzione, esistono anche degli __anti-pattern__, soluzioni che _sembrano_ buone ma sono dimostratamente problematiche: dovremo assicurarci di tenerci lontani da questi design truffaldini!
+
+- [**Discutere di pattern: i meta-patterns**](./01_meta-patterns.md)
+- [**Singleton**](./02_singleton.md)
+- [**Iterator**](./03_iterator.md)
+- [**Chain of responsibility**](./04_chain-of-responsibility.md)
+- [**Flyweight**](./05_flyweight.md)
+- [**NullObject**](./06_nullobject.md)
+- [**Strategy**](./07_strategy.md)
+- [**Observer**](./08_observer.md)
+- [**Adapter**](./09_adapter.md)
+- [**Facade**](./10_facade.md)
+- [**Composite**](./11_composite.md)
+- [**Decorator**](./12_decorator.md)
+- [**State**](./13_state.md)
+- [**Factory method**](./14_factory.md)
+- [**Abstract factory**](./15_abstract-factory.md)
+- [**Model view controller**](./16_mvc.md)
+- [**Model view presenter**](./17_mvp.md)
+- [**Builder**](./18_builder.md)
\ No newline at end of file
diff --git a/src/08_patterns/01_meta-patterns.md b/src/08_patterns/01_meta-patterns.md
new file mode 100644
index 0000000000000000000000000000000000000000..a5684fc01e9f5fb5474339605d7583b0fef5c88b
--- /dev/null
+++ b/src/08_patterns/01_meta-patterns.md
@@ -0,0 +1,59 @@
+# Discutere di pattern: i meta-patterns
+
+Prima di iniziare a parlare dei principali pattern che un informatico dovrebbe conoscere, possiamo chiederci come possiamo parlare di pattern: semplice, con dei _meta-patterns_, pattern con cui costruire altri pattern!
+
+Nello specifico, i meta-patterns identificano due elementi base su cui ragionare quando si trattano i pattern:
+
+- __HookMethod__: un "metodo astratto" che, implementato, determina il comportamento specifico nelle sottoclassi; è il _punto caldo_ su cui interveniamo per adattare lo schema alla situazione.
+
+- __TemplateMethod__: metodo che coordina generalmente più HookMethod per realizzare il design voluto; è l'_elemento freddo_ di invariabilità del pattern che ne realizza la rigida struttura.
+
+Ovviamente i metodi _template_ devono avere un modo per accedere ai metodi _hook_ se intendono utilizzarli per realizzare i pattern.
+Tale collegamento può essere fatto in tre modi differenti:
+
+- __Unification__: _hook_ e _template_ si trovano nella stessa classe astratta, classe da cui erediteranno le classi concrete per implementare i metodi _hook_ e, di conseguenza, il pattern; i metodi _template_ sono invece già implementati in quanto la loro struttura non si deve adattare alla specifica applicazione.
+
+```plantuml
+@startuml
+class TemplateHookClass {
+  {abstract} hookMethod()
+  templateMethod()
+}
+@enduml
+```
+
+- __Connection__: _hook_ e _template_ sono in classi separate, indicate rispettivamente come _hook class_ (astratta) e _template class_ (concreta), collegate tra di loro da un'aggregazione: la classe template contiene cioè un'istanza della classe hook, in realtà un'istanza della classe concreta che realizza i metodi hook usati per implementare il pattern.
+
+```plantuml
+@startuml
+class TemplateClass {
+  templateMethod()
+}
+class HookClass {
+  {abstract} hookMethod()
+}
+TemplateClass o--> HookClass
+@enduml
+```
+
+- __Recursive connection__: come nel caso precedente _hook_ e _template_ sono in classi separate, ma oltre all'aggregazione tali classi sono qui legate anche da una relazione di generalizzazione: la classe template dipende infatti dalla classe hook.
+
+```plantuml
+@startuml
+class TemplateClass {
+  templateHookMethod()
+}
+class HookClass {
+  {abstract} templateHookMethod()
+}
+TemplateClass --|> HookClass
+TemplateClass o--> HookClass
+@enduml
+```
+
+Vedremo a quale meta-pattern aderiranno i pattern che vediamo. 
+A tal proposito,i pattern che vedremo fanno parte dei cosiddetti "__Gang Of Four patterns__", una serie di 23 pattern definiti da Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides; oltre ad averli definiti, questi signori hanno diviso i pattern in tre categorie:
+
+- __Creazionali__: legati alla creazione di oggetti
+- __Comportamentali__: legati all'interazione tra oggetti
+- __Strutturali__: legati alla composizioni di classi e oggetti
diff --git a/src/08_patterns/02_singleton.md b/src/08_patterns/02_singleton.md
new file mode 100644
index 0000000000000000000000000000000000000000..815fabd5520ab9f36758f23677c23ced6dba7822
--- /dev/null
+++ b/src/08_patterns/02_singleton.md
@@ -0,0 +1,67 @@
+# <big>S</big>INGLETON
+
+Talvolta vorremmo che di un certo oggetto esistesse __una sola istanza__ perché logicamente di tale oggetto non ha senso esistano diverse copie all'interno dell'applicazione (es. diverse istanze della classe Gioco in un sistema che gestisce un solo gioco alla volta).
+Tuttavia i linguaggi Object-Oriented gestiscono solo classi con istanze multiple, per cui la realizzazione di questa unicità può rivelarsi più complessa del previsto.
+
+La soluzione consiste nel rendere la classe stessa responsabile del fatto che non può esistere più di una sua istanza: per fare ciò il primo passo è ovviamente quello di _rendere privato il costruttore_, o se non privato comunque non pubblico (conviene metterlo protected in modo da poter creare sottotipi). \
+Bisogna però garantire comunque un modo per recuperare l'unica istanza disponibile della classe: si crea dunque il _metodo statico_ `getInstance` che restituisce a chi lo chiama l'unica istanza della classe, creandola tramite il costruttore privato se questa non è già presente.
+Tale istanza è infatti memorizzata in un _attributo statico_ della classe stessa, in modo così da poterla restituire a chiunque ne abbia bisogno.
+
+```plantuml
+@startuml
+scale 300 width
+
+class Singleton {
+    + {static} instance : Singleton
+    # Singleton()
+    + {static} Singleton getInstance()
+    + sampleOp()
+}
+@enduml
+```
+
+Con queste accortezze è possibile creare una classe Singleton simile a questa:
+
+```java
+public class Singleton {
+    /* costruttore privato o comunque non pubblico */
+    protected Singleton() { ... }
+
+    /* salvo l'istanza per usarla dopo */
+    private static Singleton instance = null;
+
+    /* metodo statico */
+    public static Singleton getInstance() {
+        if (instance == null) {
+            instance = new Singleton();
+        }
+        return instance;
+    }
+
+    public void metodoIstanza() { ... }
+}
+```
+
+Tuttavia, per come lo abbiamo scritto questa classe non assicura di non creare più di un'istanza di sé stessa, in quanto non prende in considerazione la __concorrenza__.
+Se due processi accedono in modo concorrente al metodo `getInstance`, entrambi potrebbero eseguire il controllo sul valore nullo dell'istanza ed ottenere un successo in quanto l'istanza non è ancora stata assegnata al relativo attributo statico nell'altro processo: si ottiene dunque che uno dei due processi ha accesso ad una propria istanza privata, cosa che distrugge completamente il nostro pattern!
+
+Una prima soluzione sarebbe di mettere un lock sull'esecuzione del metodo anteponendovi la direttiva `@Synchronized`: tuttavia, tale approccio comporterebbe un notevole calo di prestazioni del sistema portando vantaggi unicamente alla prima chiamata. \
+Una soluzione molto più efficiente (non possibile però fino a Java 5) è invece quella che prevede di avere un _blocco sincronizzato_ di istruzioni posto all'interno del ramo in cui si pensa che l'istanza sia nulla in cui ci si chiede se effettivamente l'istanza è nulla e solo allora si esegue il costruttore; la presenza del doppio controllo assicura che non vi siano squilibri dovuti alla concorrenza, mentre sincronizzare solamente un blocco e non l'intero metodo fa sì che il calo di prestazioni sia sentito solamente durante le prime chiamate concorrenti.
+
+## Idioma Java
+
+Fortunatamente si è sviluppato per il linguaggio Java un idioma molto semplice per il Singleton, in cui al posto di usare una classe per definire l'oggetto si usa un __enumerativo__ con un unico valore, l'istanza.
+Ciascun valore di tali oggetti è infatti trattato nativamente da Java proprio come un Singleton: viene creato al momento del suo primo uso, non ne esiste più di una copia, e chiunque vi acceda accede sempre alla medesima istanza.
+La possibilità di creare attributi e metodi all'interno degli `enum` completa il quadro.
+
+```java
+public enum MySingleton {
+    INSTANCE;
+
+    public void metodoIstanza() { ... }
+}
+
+MySingleton.INSTANCE.sampleOp();
+```
+
+Si tratta inoltre di un approccio "thread safe", ovvero che lavora già bene con la concorrenza; l'unico svantaggio è che, se non si conosce l'idioma, a prima vista questa soluzione risulta molto meno chiara rispetto all'approccio precedente.
diff --git a/src/08_patterns/03_iterator.md b/src/08_patterns/03_iterator.md
new file mode 100644
index 0000000000000000000000000000000000000000..908a43e09af1fa1ed9aa68610aa311400758bf47
--- /dev/null
+++ b/src/08_patterns/03_iterator.md
@@ -0,0 +1,73 @@
+# <big>I</big>TERATOR
+
+Talvolta gli oggetti che definiamo fanno da __aggregatori__ di altri oggetti, contenendo cioè una collezione di questi su cui poi fare particolari operazioni: in questi casi è molto probabile che vorremo poter iterare sui singoli elementi aggregati, ma senza esporre la rappresentazione interna usata per contenerli.
+
+Proprio per risolvere questo tipo di problematiche nasce il pattern Iterator: esso consiste nella creazione di una classe `ConcreteIterator` che abbia accesso alla rappresentazione interna del nostro oggetto e esponga i suoi elementi in modo sequenziale tramite i metodi `next()` e `hasNext()`; dovendo accedere alla rappresentazione, molto spesso tale iteratore si realizza come una _classe interna anonima_.
+
+Java supporta largamente il pattern Iterator, a tal punto che nella libreria standard esiste un'interfaccia generica per gli iteratori, `Iterator<E>`: all'interno di tale interfaccia sono definiti, oltre ai metodi di cui sopra, il metodo `remove()`, normalmente non supportato in quanto permetterebbe di modificare la collezione contenuta dalla classe, e il metodo `forEachRemaining()`, che esegue una data azione su tutti gli elementi ancora non estratti dell'iteratore.
+
+```java
+public interface Iterator<E> {
+    boolean hasNext();
+    E next();
+
+    default void remove() {
+        throw new UnsupportedOperationException("remove");
+    }
+
+    /* aggiunta funzionale opzionale */
+    default void forEachRemaining(Consumer<? super E> action) {
+        Objects.requireNonNull(action);
+        while (hasNext())
+            action.accept(next());
+    }
+}
+```
+
+Esiste inoltre un'interfaccia che l'oggetto iterabile può implementare, `Iterable<E>`: essa richiede solamente la presenza di un metodo `iterator()` che restituisca l'iteratore concreto, e una volta implementata permette di utilizzare il proprio oggetto aggregatore all'interno di un costrutto foreach.
+
+```plantuml
+@startuml
+scale 1024 width
+
+interface Iterable<T> << interface >> {
+    + {abstract} Iterator<T> iterator()
+}
+interface Iterator<T> << interface >> {
+    + {abstract} boolean hasNext()
+    + {abstract} T next()
+    + void remove()
+    {method} ...
+}
+Iterable .> Iterator
+class ConcreteIterable implements Iterable {
+    + Iterator<T> iterator()
+}
+class ConcreteIterator implements Iterator {
+    + boolean hasNext()
+    + T next()
+}
+ConcreteIterable .> ConcreteIterator
+hide empty fields
+@enduml
+```
+
+Così, per esempio, possiamo passare dal seguente codice:
+
+```java
+Iterator<Card> cardIterator = deck.getCards();
+while (cardIterator.hasNext()) {
+    Card card = cardIterator.next();
+    System.out.println(card.getSuit());
+}
+```
+
+... a quest'altro:
+
+```java
+for (Card card : deck) {
+    System.out.println(card.getSuit());
+}
+```
+
+Oltre ad essere più stringato il codice è significativamente più chiaro, rendendo palese che la singola `card` sia read-only.
diff --git a/src/08_patterns/04_chain-of-responsibility.md b/src/08_patterns/04_chain-of-responsibility.md
new file mode 100644
index 0000000000000000000000000000000000000000..e7de667514cc8a8dbdf1301516e6d9058c3f12d5
--- /dev/null
+++ b/src/08_patterns/04_chain-of-responsibility.md
@@ -0,0 +1,58 @@
+# <big>C</big>HAIN OF <big>R</big>ESPONSIBILITY
+
+Talvolta nei nostri programmi vorremmo definire una gestione "a cascata" di una certa richiesta. Pensiamo per esempio a una serie di regole anti-spam: all'arrivo di una mail la prima regola la esamina e si chiede se sia applicabile o meno; in caso affermativo contrassegna la mail come spam, altrimenti _la passa alla prossima regola_, che a sua volta farà lo stesso test passando il controllo alla terza in caso negativo, e così via.
+Abbiamo cioè un _client_ in grado di fare una richiesta, e una __catena di potenziali gestori__ di cui non sappiamo a priori chi sarà in grado di gestirla effettivamente.
+
+Il pattern Chain of Responsibility risolve il disaccoppiamento tra client e gestore _concatenando i gestori_.
+Esso prescrive la creazione di un'interfaccia a cui tutti i gestori devono aderire, contenente solo la dichiarazione di un metodo `evaluate` che implementa la logica descritta prima: si stabilisce se si può gestire la richiesta, e se non si può si chiama lo stesso metodo su _un altro gestore_ ottenuto come parametro al momento della creazione.
+
+```plantuml
+@startuml
+scale 1024 width
+
+class PokerHand {
+    + HandRank getRank()
+}
+interface ChainedHandEvaluator << interface >> {
+    + {abstract} handEvaluator()
+}
+PokerHand -> ChainedHandEvaluator
+ChainedHandEvaluator "0..1" o-> "0..1" ChainedHandEvaluator : next
+class HighCardEvaluator implements ChainedHandEvaluator {
+    + HighCardEvaluator(ChainedHandEvaluator)
+    + handEvaluator()
+}
+note left of HighCardEvaluator::"handEvaluator()" 
+    if (can_handle) { 
+        return do_it(); 
+    } else { 
+        return next.handEvaluator(); 
+    }
+end note
+hide empty fields
+@enduml
+```
+
+In questo modo all'interno del client è sufficiente creare una vera e propria catena di gestori e chiamare il metodo `evaluate` del primo: si noti che l'ordine in cui vengono assemblati tali gestori conta, in quanto la valutazione procede sequenzialmente.
+
+```java
+public interface Gestore {
+
+    /* Il tipo di ritorno dipende dal campo applicativo */
+    public ??? evaluate(); 
+
+}
+
+public class Client {
+
+    private Gestore evaluator = 
+        new GestoreConcreto1(
+            new GestoreConcreto2(
+                new GestoreConcreto3(null)));
+
+    public void richiesta() {
+        evaluator.evaluate();
+    }
+
+}
+```
\ No newline at end of file
diff --git a/src/08_patterns/05_flyweight.md b/src/08_patterns/05_flyweight.md
new file mode 100644
index 0000000000000000000000000000000000000000..eb5f08659f63aa6ce2824d790a24b7a0195dfbaf
--- /dev/null
+++ b/src/08_patterns/05_flyweight.md
@@ -0,0 +1,30 @@
+# <big>F</big>LY<big>W</big>EIGHT
+
+Talvolta ci troviamo in una situazione simile a quella che aveva ispirato il pattern Singleton: abbiamo una __serie di oggetti immutabili fortemente condivisi__ all'interno del programma e per motivi di performance e risparmio di memoria vorremmo che _non esistano istanze diverse a parità di stato_.
+Se due client devono usare un'istanza con lo stesso stato vorremmo cioè non usino ciascuno un'istanza duplicata ma proprio la _stessa istanza_: essendo le istanze immutabili, tale condivisione non dovrebbe infatti creare alcun tipo di problema.
+
+Il pattern FlyWeight serve a gestire una collezione di oggetti immutabili assicurandone l'unicità: esso consiste nel rendere privato il costruttore e __costruire tutte le istanze a priori con un costruttore statico__, salvandole in una lista privata.
+I client possono dunque richiedere una certa istanza con un metodo `get` specificando lo stato dell'istanza desiderata: in questo modo, a parità di richiesta verranno restituite le stesse identiche istanze.
+
+Abbiamo visto un'applicazione di questo pattern durante i laboratori parlando di `Card`:
+
+```java
+public class Card {
+    private static final Card[][] CARDS = new Card[Suit.values().length][Rank.values().length];
+
+    static {
+        for (Suit suit : Suit.values()) {
+            for (Rank rank : Rank.values()) {
+                CARDS[suit.ordinal()][rank.ordinal()] = new Card(rank, suit);
+            }
+        }
+    }
+
+    public static Card get(Rank pRank, Suit pSuit) {
+        return CARDS[pSuit.ordinal()][pRank.ordinal()];
+    }
+}
+```
+
+A differenza del pattern Singleton è difficile definire a priori quante istanze ci sono: abbiamo un'istanza per ogni possibile combinazione dei valori degli attributi che compongono lo stato.
+Proprio per questo motivo il pattern può risultare un po' inefficiente per oggetti con rappresentazioni grandi: alla prima computazione vengono infatti inizializzati _tutti_ gli oggetti, perdendo un po' di tempo e sprecando potenzialmente spazio se non tutte le istanze saranno accedute.
\ No newline at end of file
diff --git a/src/08_patterns/06_nullobject.md b/src/08_patterns/06_nullobject.md
new file mode 100644
index 0000000000000000000000000000000000000000..430fc6096fc8f3b63557a0a0c1eacc066a8ddc3f
--- /dev/null
+++ b/src/08_patterns/06_nullobject.md
@@ -0,0 +1,42 @@
+# <big>N</big>ULL<big>O</big>BJECT
+
+Spesso nei nostri programmi avremo bisogno di utilizzare valori "nulli": pensiamo per esempio al termine di una Chain of Responsibilities, dove per fermare la catena di chiamate dobbiamo dare un valore nullo al `next` dell'ultimo gestore.
+In generale, a una variabile che indica un riferimento ad un oggetto possiamo assegnare il valore speciale `null` per indicare che essa _non punta a nulla_.
+
+Il problema sorge però quando a runtime si prova a dereferenziare tale valore e viene sollevata un'eccezione (`NullPointerException` in Java): questa possibilità ci costringe nel codice ad essere sempre molto titubanti sui valori che ci vengono passati, in quanto non possiamo mai assumere che essi puntino ad un valore reale e dunque dobbiamo sempre controllare che non siano nulli.
+
+C'è però da dire che anche con tali accortezze l'utilizzo di `null` è poco carino, in quanto un valore nullo può indicare cose anche molto diverse:
+
+- un errore a runtime;
+- uno stato temporaneamente inconsistente;
+- un valore assente o non valido.
+
+Ogni volta che si utilizza `null` il codice diventa un po' meno chiaro, e sarebbe necessario disambiguare con commenti o documentazione per spiegare con che accezione tale valore viene usato.
+Anche le strategie di gestione del `null` variano drasticamente a seconda del significato assegnato a tale valore: quando non ci sono valori "assenti" e dunque il `null` indica solo un errore è sufficiente controllare che i dati passati non siano nulli con condizioni, asserzioni o l'annotazione `@NotNull`.
+
+![null object valori non assenti](/assets/09_nullObject-valori-non-assenti.png)
+
+Quando invece ci sono __valori "assenti"__, ovvero che indicano situazioni particolari (es. il Joker in un mazzo di carte, che non ha né Rank né Suit), la gestione è più complicata.
+Se non vogliamo trattarli come `null` per l'ambiguità che tale valore introduce, un'altra opzione è creare un metodo booleano nella classe che restituisce se l'istanza ha il valore nullo (es. `isJoker()`): tuttavia, questo apre le porte a errori da parte dell'utente, che potrebbe dimenticarsi di fare tale controllo e usare l'oggetto come fosse qualunque altro.
+
+Per creare un oggetto che corrisponda al __concetto di nessun valore__ o __valore neutro__ nasce allora il pattern NullObject: si crea all'interno della classe o dell'interfaccia un _oggetto statico_ chiamato `NULL` che fornisce _particolari implementazioni dei metodi_ della stessa per realizzare l'idea di valore nullo a livello di dominio.
+In questo modo tale oggetto mantiene l'identità della classe rimanendo però sufficientemente separato dagli altri valori; inoltre, la presenza di implementazioni specifiche dei metodi evita il lancio di eccezioni ambigue.
+
+```java
+public interface CardSource {
+    Card draw();
+    boolean isEmpty();
+
+    public static CardSource NULL = new CardSource() {
+        public boolean isEmpty() { 
+            return true; 
+        }
+        public Card draw() {
+            assert !isEmpty();
+            return null;
+        }
+    }
+}
+```
+
+Quindi possiamo notare che il concetto del NullObject pattern è quello di creare un oggetto in cui viene definito un comportamento specifico per ogni metodo che rispecchia ciò che accadrebbe nel caso in cui il metodo venisse chiamato su null nel normale flusso di istruzioni.
\ No newline at end of file
diff --git a/src/08_patterns/07_strategy.md b/src/08_patterns/07_strategy.md
new file mode 100644
index 0000000000000000000000000000000000000000..1a5adfc093ab0cd7261d5974ceec9680c8085251
--- /dev/null
+++ b/src/08_patterns/07_strategy.md
@@ -0,0 +1,75 @@
+# <big>S</big>TRATEGY / <big>D</big>ELEGATION
+
+Talvolta nelle nostre classi vogliamo definire __comportamenti diversi per diverse istanze__: la soluzione classica dei linguaggi Object-Oriented è la creazione di una gerarchia di classi in cui le classi figlie sovrascrivano i metodi della classe genitore.
+Tuttavia, questo espone a delle problematiche: cosa fare se per esempio la classe genitore cambia aggiungendo un metodo che una delle classi figlie non dovrebbe poter implementare (es. `RubberDuck` come figlia di `Duck`, che aggiunge il metodo `fly()`)?
+
+Non volendo violare il principio Open-Close, non siamo intenzionati a rimuovere il metodo incriminato, per cui dobbiamo cercare altre soluzioni. Una prima idea sarebbe quella di sopperire al fatto che la classe genitore non sappia chi sono i suoi figli con costrutti proprietari del linguaggio:
+
+- una classe `Final` non permette di ereditare, ma questo non ci permetterebbe di differenziare il comportamento;
+- una classe `Sealed` (aggiunta di Java 17) sceglie esplicitamente chi possano essere i suoi figli: in questo modo si può evitare che la classe figlia problematica non possa ereditare, ma si tratta comunque di una soluzione parziale.
+
+Non si può neanche pensare di fare semplicemente l'override nella classe figlia del metodo aggiunto facendo in modo che lanci un'eccezione: si avrebbe infatti una inaccettabile violazione del principio di sostituzione di Liskov, che afferma sostanzialmente che un'istanza di una sottoclasse deve poter essere usata senza problemi al posto dell'istanza di una classe genitore.
+
+Una soluzione migliore si basa invece sul concetto di __delega__, che sostituisce all'ereditarietà la _composizione_.
+Fondamentalmente si tratta di individuare ciò che cambia nell'applicazione e separarlo da ciò che rimane fisso: si creano delle _interfacce per i comportamenti da diversificare_ e una _classe concreta che implementa ogni diverso comportamento_ possibile.
+All'interno della classe originale si introducono dunque degli __attributi di comportamento__, impostati al momento della costruzione o con dei setter a seconda della dinamicità che vogliamo permettere: quando viene richiesto il comportamento a tale classe essa si limiterà a chiamare il proprio "oggetto di comportamento".
+Nell'esempio delle `Duck`, per esempio, la struttura è la seguente:
+
+```plantuml
+@startuml
+scale 1024 width
+
+interface FlyBehavior << interface >> {
+    + {abstract} fly()
+}
+class Duck {
+    + performQuack()
+    + performFly()
+    + swim()
+    + {abstract} display()
+}
+FlyBehavior <-o Duck
+interface QuackBehavior << interface >> {
+    + {abstract} quack()
+}
+Duck o-> QuackBehavior
+class FlyWithWings implements FlyBehavior {
+    + fly()
+}
+class FlyNoWay implements FlyBehavior {
+    + fly()
+} 
+class Quack implements QuackBehavior {
+    + quack()
+}
+class Mute implements QuackBehavior {
+    + quack()
+}
+class Squeak implements QuackBehavior {
+    + quack()
+}
+hide empty fields
+@enduml
+``` 
+
+Come si vede, qui non c'è scritto da nessuna parte che una `Duck` deve volare, ma solo che deve definire la sua "politica di volo" incorporando un `FlyBehaviour`.
+
+La differenziazione dei comportamenti si fa dunque _a livello d'istanza_ e non di classe: il pattern definisce una famiglia algoritmi e li rende tra di loro intercambiabili tramite _encapsulation_.
+Per questo motivo tale pattern è usato in situazioni anche molto diversa da quella con cui l'abbiamo introdotto: un altro esempio presente in Java è l'interfaccia `Comparator`.
+
+```plantuml
+@startuml
+class Client {}
+interface AbstractStrategy << interface >> {
+    + {abstract} void doSomething()
+}
+Client .> AbstractStrategy
+class ConcreteStrategy1 implements AbstractStrategy {
+    + void doSomething()
+}
+class ConcreteStrategy2 implements AbstractStrategy {
+    + void doSomething()
+}
+hide empty fields
+@enduml
+```
\ No newline at end of file
diff --git a/src/08_patterns/08_observer.md b/src/08_patterns/08_observer.md
new file mode 100644
index 0000000000000000000000000000000000000000..524912fc53fc70a307f88ca86c926e8c2520ddd8
--- /dev/null
+++ b/src/08_patterns/08_observer.md
@@ -0,0 +1,174 @@
+# <big>O</big>BSERVER
+
+Molto spesso capita di avere nei nostri programmi una serie di elementi che vanno tenuti sincronizzati: pensiamo per esempio ad una ruota dei colori che deve aggiornare i valori RGB quando l'utente seleziona un punto con il mouse. Abbiamo cioè uno __stato comune__ che va mantenuto coerente in tutti gli elementi che lo manipolano.
+
+Nella realizzazione di questa funzionalità si rischia di cadere nell'anti-pattern delle _pairwise dependencies_ in cui ogni vista dello stato deve conoscere tutte le altre: si ha cioè un forte accoppiamento e una bassissima espandibilità, in quanto per aggiungere una vista dobbiamo modificare tutte le altre.
+Ovviamente basta avere poco più di due diverse viste perché il numero di dipendenze (e dunque di errori) cresca esponenzialmente: questo anti-pattern è proprio tutto il contrario del principio di separazione, che predicava forte coesione interna e pochi accoppiamenti esterni.
+
+```plantuml
+@startuml
+entity IntegerPanel {}
+entity SliderPanel {}
+entity TextPanel {}
+IntegerPanel ..> SliderPanel
+IntegerPanel ..> TextPanel
+SliderPanel ..> IntegerPanel
+SliderPanel .> TextPanel
+TextPanel ..> IntegerPanel
+TextPanel .> SliderPanel
+hide members
+@enduml
+```
+
+La soluzione proposta dal pattern Observer è dunque quella di estrarre la parte comune (lo _stato_) e isolarlo in un oggetto a parte, detto __Subject__: tale oggetto verrà osservato da tutte le viste, le cui classi prendono ora il nome di __Observer__.
+Si sta cioè __centralizzando__ la gestione dello stato: abbiamo cioè \\( n \\) classi che osservano una classe centrale e reagiscono ad ogni cambiamento di stato di quest'ultima.
+Si tratta una situazione talmente comune che in Java erano presenti delle classi (ora deprecate in quanto non _thread-safe_) per realizzare questo pattern: `java.util.Observer` e `java.util.Observable`.
+
+Ma come fanno gli Observer a sapere che il Subject è cambiato?
+L'idea di fare un continuo _polling_ (chiedo "Sei cambiato?" al Subject), non è ovviamente sensata, in quanto bloccherebbe l'esecuzione sprecando tantissime risorse.
+Invertiamo invece la responsabilità con un'architettura __event-driven__: gli Observer si _registrano_ al Subject, che li informerà quando avvengono cambiamenti di stato.
+
+```plantuml
+@startuml
+class Observable {
+    + addObserver(Observer)
+    + removeObserver(Observer)
+    + notifyObservers()
+}
+interface Observer << interface >> {
+    + {abstract} update(Observable, Object)
+}
+Observable o-> Observer
+class ConcreteObserver implements Observer {
+    + update(Observable, Object)
+}
+class ConcreteObservable extends Observable {
+    + getState() : State
+    + setState(State)
+}
+ConcreteObservable <. ConcreteObserver
+hide empty fields
+@enduml
+```
+
+Restano però da capire un paio di cose.
+Bisogna innanzitutto spiegare _come colleghiamo Observer e Subject_: come si vede in figura, esiste una classe `Observable` che funge da base da cui ereditare per ogni Subject; vi è poi un'interfaccia `Observer` che gli Observer concreti devono ovviamente implementare.
+
+A questo punto gli Observer si possono sottoscrivere al Subject semplicemente attraverso l'uso delle sue funzioni `addObserver()` e `removeObserver()`, venendo così sostanzialmente inseriti o rimossi nella lista interna degli Observer interessati. \
+Una volta che lo stato del Subject viene cambiato, solitamente attraverso una serie di metodi pubblici che permettano a tutti di modificarlo (`setState()`), esso chiama dunque il suo metodo `notifyObservers()`: questo altro non fa che ciclare su tutti gli Observer sottoscritti chiamandone il metodo `update(Observable, Object)`, dove:
+
+- `Observable` è il Subject di cui è stato modificato lo stato (l'uso di interfacce permette di sottoscrivere un Observer a più Subject tra cui disambiguare al momento dell'update)
+- `Object` è la parte di stato che è cambiata (_Object_ perché il tipo dipende ovviamente dal Subject in questione)
+
+Sul metodo di notifica del cambiamento di stato esistono però due diverse filosofie, __push__ e __pull__, ciascuna con i suoi campi applicativi prediletti: vediamole dunque singolarmente, evidenziando quando e come esse sono utilizzate.
+
+## push
+
+In questo caso l'argomento Observable di `update` viene messo nullo, mentre __nell'Object viene passata la totalità dello stato__ del Subject:
+
+```java
+// Observable
+@Override
+public void notifyObservers() {
+
+    for (Observer observer : observers) {
+        observer.update(null, state);
+    }
+
+}
+
+// Observer
+@Override
+public void update(Observable model, Object state) {
+
+    if (state instanceof Integer intValue) {
+        doSomethingOn(intValue);
+    }
+
+}
+```
+
+Come si vede, dovendo definire come reagire al cambiamento di stato in `update` l'Observer dovrà innanzitutto fare un down-casting per ottenere un oggetto della classe corretta.
+Avendo la responsabilità di tale casting l'Observer dovrà conoscere precisamente la struttura dello stato del Subject, creando una _forte dipendenza_ che potrebbe creare problemi di manutenibilità.
+
+Un altro problema di questo approccio è che gli Observer sono solitamente interessati a una piccola porzione dello stato del Subject, quindi passarlo tutto come parametro potrebbe sovraccaricare inutilmente la memoria.
+
+## pull
+
+Con questo approccio, invece di mandare lo stato all'`update` __viene passato il Subject stesso__, il quale conterrà uno o più metodi per accedere allo stato (`getState`):
+
+```java
+// Observable
+@Override
+public void notifyObservers() {
+
+    for (Observer observer : observers) {
+        observer.update(this, null);
+    }
+
+}
+
+// Observer
+@Override
+public void update(Observable model, Object state) {
+
+    if (model instanceof ConcreteObservable cModel) {
+        doSomethingOn(cModel.getState());
+    }
+
+}
+```
+
+Sebbene comporti un passaggio in più poiché l'Observer deve chiamare un metodo del Subject quando riceve la notifica, questo cambio di prospettiva offre due vantaggi: in primo luogo non viene passato tutto lo stato, il che fa risparmiare molta memoria; inoltre, il Subject potrebbe decidere di rendere disponibili sottoinsiemi diversi dello stato con getter diversi, mostrando così ad ogni Observer solo le informazioni per esso rilevanti.
+
+Inoltre, sebbene anche in questo caso sia richiesto un casting (da Observable al Subject), questo approccio rende meno dipendenti dalla rappresentazione interna del Subject: fintanto che la firma dei getter non cambia lo stato interno del Setter può cambiare senza problemi.
+
+## Approccio ibrido e dipendenze
+
+Partiamo col dire che molto spesso nei casi reali gli approcci _push_ e _pull_ sono ibridati tra di loro: ad `update` viene passato sia il Subject che quella parte di stato utile a tutti gli Observer, mentre qualora gli serva qualcosa di più specifico essi se lo andranno a prendere con il getter.
+
+Il vero problema di entrambi gli approcci è però quello delle dipendenze: nel caso _push_ dipendiamo dalla rappresentazione interna del Subject, mentre nel caso _pull_ dalla sua classe concreta.
+Poiché tale dipendenza non è facilmente eliminabile, piuttosto che lasciarla nascosta nel casting conviene __esplicitarla__:
+
+- all'interno dell'Observer salvo l'istanza di Observable a cui mi sono sottoscritto, così al momento dell'`update` posso verificare direttamente che l'istanza sia quella al posto di fare un casting;
+
+- creiamo una classe `State` e l'aggreghiamo sia nell'Observer che nell'Observable concreto in modo che essa nasconda la rappresentazione reale dello stato.
+
+Otteniamo dunque un codice simile al seguente:
+
+```java
+public class State { /* rappresentazione interna dello stato */ }
+
+public class Observable {
+    private State stato;
+    private List<Observer> observers = new ArrayList<>();
+
+    public void addObserver(@NotNull Observer obs) { observers.add(obs); }
+    public void removeObserver(@NotNull Observer obs) { observers.remove(obs); }
+    public void notifyObservers() {
+        for (Observer obs: observers) update(this, stato);
+    }
+}
+
+public class Subject extends Observable {
+
+    public void setState(State nuovoStato) { ... }
+    public State getState() { return super.stato; }
+    /* Opzionale: altri metodi getter */
+}
+
+public interface Observer {
+    public void update(Observable subject, Object stato);
+}
+
+public class ConcreteObserver {
+    private Observable mySubject;
+
+    @Override
+    public void update(Observable subject, Object stato) {
+        if (subject == mySubject) {
+            ...
+        }
+    }
+}
+```
\ No newline at end of file
diff --git a/src/08_patterns/09_adapter.md b/src/08_patterns/09_adapter.md
new file mode 100644
index 0000000000000000000000000000000000000000..083b4aaf5a3833147ac11080181bf2521d363234
--- /dev/null
+++ b/src/08_patterns/09_adapter.md
@@ -0,0 +1,122 @@
+# <big>A</big>DAPTER
+
+Spesso nei programmi che scriviamo capita di dover __far collaborare interfacce diverse__ di componenti non originariamente sviluppati per lavorare insieme.
+Questo capita in una miriade di situazioni, ma volendone citare alcune:
+
+- in un ambito di sviluppo COTS (_Component Off The Shelf: sviluppiamo solo ciò che non è disponibile tramite librerie o codice open-source_) riutilizziamo tanti componenti presi dal mercato, non pensati per essere compatibili;
+- sviluppando ed evolvendo un programma in modo _incrementale_ capita di dover integrare componenti nuovi con componenti vecchi (_legacy_) per garantire una certa continuità nell'esperienza utente.
+
+Da tutta una serie di situazioni simili è nato il bisogno di creare delle strutture che permettessero di rendere compatibili componenti già esistenti, ovvero creare della _"colla"_ in grado di legare i componenti tra loro per soddisfare le specifiche del sistema.
+È così ben presto scaturito il pattern __Adapter__, un pattern ormai molto diffuso che consiste nel creare vari moduli che possano essere incollati o adattati ad altre strutture in modo da renderle utilizzabili incrementalmente e in modo controllato. 
+
+Sebbene sia già utilizzato molto spesso, talvolta anche inconsciamente, approfondiamo il pattern in questa sede non solo per imparare a usarlo con più criterio, ma anche perché di esso esistono due "versioni":
+
+- __Class Adapter__: adatta una classe.
+- __Object Adapter__: adatta un oggetto di una classe.
+
+Come vedremo, questi due pattern sono molto simili a livello di schema UML ma abbastanza differenti da rendere importante capire quale usare in quali contesti, comprendendo vantaggi e svantaggi di entrambi.
+
+### Class Adapter
+
+```plantuml
+@startuml
+scale 500 width
+
+class Client {}
+interface Target << interface >> {
+    + {abstract} request()
+}
+Client .> Target
+class Adaptee {
+    + oldRequest()
+}
+class ClassAdapter implements Target {
+    + request()
+}
+Adaptee <|-- ClassAdapter
+hide empty fields
+@enduml
+```
+
+Come si vede dallo schema UML, per permettere a un _Client_ di comunicare tramite un'interfaccia _Target_ con un componente concreto vecchio detto _Adaptee_ il Class Adapter utilizza una classe concreta che __implementa l'interfaccia Target__ e __estende la classe Adaptee__, ereditandone così i metodi e la vecchia interfaccia: all'interno di tale classe potremo dunque limitarci a _rimappare le funzionalità_ richieste dalla nuova interfaccia su quella vecchia, implementando qualcosa solo se strettamente necessario e comunque sfruttando la logica già presente della classe estesa.
+
+```java
+public class Adapter extends Adaptee implements Target {
+    @Override
+    public void request() {
+        this.oldRequest();
+    }
+}
+```
+
+In questo modo il client utilizzerà l'adapter come se fosse l'oggetto completo, non accorgendosi che quando ne chiama un metodo in realtà il codice eseguito è quello appartenente alla vecchia classe già esistente: in un __unica istanza__ si sono dunque riunte l'interfaccia vecchia e quella nuova.
+
+Vediamo dunque quali sono i pro e i contro di questo approccio. È utile innanzitutto notare che estendendo l'Adaptee la classe Adapter ha parziale accesso alla sua rappresentazione interna, un vantaggio non da poco quando si considera quanto questo faciliti l'eventuale modifica di funzionalità; inoltre, essa ne eredita le definizioni dei metodi, e se questi non devono cambiare tra la vecchia interfaccia e la nuova si può evitare di ridefinirli totalmente, risparmiando così parecchio codice.
+
+Inoltre, un'istanza della classe Adapter può essere utilizzata attraverso __entrambe le interfacce__ in quanto implementa quella nuova ed eredita quella vecchia; questo aspetto può essere considerato sia un vantaggio che uno svantaggio: se infatti da un lato ciò è molto utile in sistemi che evolvono incrementalmente e in cui dunque alcune componenti potrebbero volersi riferire ancora alla vecchia interfaccia, d'altro canto questo aspetto impedisce di imporre tassativamente che l'oggetto sia utilizzato solo tramite l'interfaccia nuova.
+
+Va poi notato che questo approccio perde un po' di senso nel caso in cui si debba adattare un'_interfaccia_ e non una classe, in quanto implementare entrambe le interfacce non permette di ereditare codice o funzionalità da quella vecchia.
+Inoltre, il Class Adapter potrebbe presentare problemi relativi all'ereditarietà multipla, non supportata da alcuni linguaggi a oggetti (es. Java).
+
+### Object Adapter
+
+```plantuml
+@startuml
+scale 500 width
+
+class Client {}
+interface Target << interface >> {
+    + {abstract} request()
+}
+Client .> Target
+class ObjectAdapter implements Target {
+    + request()
+}
+class Adaptee {
+    + oldRequest()
+}
+ObjectAdapter o-> Adaptee
+hide empty fields
+@enduml
+```
+
+Come abbiamo già detto più volte, spesso conviene prediligere la _composizione_ rispetto all'ereditarietà: al pattern del Class Adapter si contrappone dunque l'Object Adapter, che invece di estendere la classe Adaptee __contiene una sua istanza__ e __delega__ ad essa tramite la vecchia interfaccia le chiamate ai metodi dell'interfaccia nuova, eventualmente operando i necessari rimaneggiamenti.
+
+```java
+public class Adapter implements Target {
+    private final Adaptee adaptee;
+
+    public Adapter(Adaptee adaptee) {
+        assert adaptee != null;
+        this.adaptee = adaptee;
+    }
+
+    @Override
+    public void request() {
+        adaptee.oldRequest();
+    }
+}
+```
+
+Anche in questo caso il client non si accorge di nulla, e in particolare non sarebbe nemmeno in grado di dire con certezza se l'Adapter utilizzato sia un Class Adapter o un Object Adapter: a lui la scelta del paradigma è del tutto trasparente.
+
+Rispetto al Class Adapter l'Object Adapter presenta differenti punti di forza e di debolezza, e il primo di questi ultimi è rappresentato dal fatto che invece di avere un'unica istanza che racchiuda entrambe le interfacce con questo pattern abbiamo invece _due istanze_ (Adapter e Adaptee contenuto), cosa che può costituire un notevole spreco di memoria in certe situazioni. 
+
+Inoltre, aver sostituito l'ereditarietà con la composizione ha lo sgradevole effetto di non permettere all'Adapter di vedere in alcun modo la rappresentazione protetta dell'Adaptee, che esso dovrà invece manipolare unicamente tramite la sua interfaccia pubblica.
+Si è poi costretti a _reimplementare ogni metodo_ anche se questo non è cambiato dall'interfaccia vecchia a quella nuova, in quanto è comunque necessario operare la delega all'Adaptee.
+
+Tuttavia, l'Object Adapter si rivela particolarmente utile nel caso ad essere adattata debba essere un'_interfaccia_: non soffrendo di problemi di ereditarietà, un Object Adapter ha la peculiarità di poter adattare chiunque implementi la vecchia interfaccia, ovvero un'intera _gerarchia_ di classi potenzialmente non ancora esistenti!
+
+#### Class Adapter vs Object Adapter
+
+Class Adapter e Object Adapter hanno ciascuno i propri vantaggi e svantaggi che li rendono più adatti ad essere utilizzati in diverse situazioni.
+Volendo fare un confronto tra i due approcci proponiamo dunque la seguente tabella:
+
+| Aspetto | Class Adapter | Object Adapter |
+|---------|---------------|----------------|
+| Accesso all'Adaptee | <span style="color:green">L'Adapter può accedere ad attribuiti e metodi protetti dell'Adaptee</span> | <span style="color:red">L'Adapter può interagire con l'Adaptee solo tramite la sua interfaccia pubblica</span>|
+| Riuso del codice | <span style="color:green">Non richiede di reimplementare i metodi che non cambiano</span> | <span style="color:red">Qualunque metodo va reimplementato per fare la delega</span> |
+| Uso della memoria | <span style="color:green">Un'unica istanza</span> | <span style="color:red">Due istanze obbligatorie</span> |
+| Adozione delle interfacce | <span style="color:#ff9900">L'istanza può essere usata con entrambe le interfacce</span> | <span style="color:#ff9900">L'istanza può essere usata solo tramite la nuova interfaccia</span> |
+| Problemi di ereditarietà multipla | <span style="color:red">Possibili</span> | <span style="color:green">No</span>|
+| Adattamento delle interfacce | <span style="color:red">Non è indicato</span> | <span style="color:green">Adattando un'interfaccia può adattare un'intera gerarchia di classi</span>|
diff --git a/src/08_patterns/10_facade.md b/src/08_patterns/10_facade.md
new file mode 100644
index 0000000000000000000000000000000000000000..8b77b7c438f506c1e5a186250d0faa232f9be850
--- /dev/null
+++ b/src/08_patterns/10_facade.md
@@ -0,0 +1,11 @@
+# <big>F</big>ACADE
+
+Costruendo un sistema complesso può capitare di dover definire una serie di interfacce molto specifiche e dettagliate per i propri componenti in modo che questi possano lavorare correttamente in concerto tra di loro.
+Il problema sorge però quando un Client, dovendo accedere al sistema, si ritrova costretto a dover interagire direttamente con i sottosistemi che lo compongono, cosa che lo obbliga a sviscerare i funzionamenti interni dello stesso per ottenere un comportamento tutto sommato semplice.
+
+Lo scopo del pattern Facade è allora quello di __fornire un'interfaccia unificata e semplificata a un insieme di interfacce separate__: spesso infatti l'uso comune di un sistema si riduce un paio di operazioni ottenibili combinando varie funzionalità fornite dal package; invece di richiedere al Client di operare tale composizione facciamo ricadere sulle nostre spalle tale compito costruendo una _classe_ che faccia da _interfaccia standard_ al sistema.
+
+![Facade](/assets/09_facade.png)
+
+Si noti come questo non impedisca al Client di usare anche le funzionalità più complesse, ma metta solo ulteriormente a disposizione un'interfaccia che gli permetta di sfruttare facilmente quelle più frequentemente utilizzate.
+Volendo fornire un esempio nella vita reale, un telecomando fornisce un'interfaccia semplice ai controlli della televisione, permettendo di regolare il volume e cambiare canale con semplicità: aprendo però uno sportellino ecco che ci vengono forniti tutti i comandi più specifici.
diff --git a/src/08_patterns/11_composite.md b/src/08_patterns/11_composite.md
new file mode 100644
index 0000000000000000000000000000000000000000..580bfa26d8bce2892b092127f2f0eb5bdde96ad0
--- /dev/null
+++ b/src/08_patterns/11_composite.md
@@ -0,0 +1,48 @@
+# <big>C</big>OMPOSITE
+
+Immaginiamo di dover modellare un file system in un'applicazione: esso sarà composto di File e Directory, le quali dovranno essere in grado di contenere al loro interno File e ulteriori Directory; dovremo cioè ottenere una struttura ad albero di Directory avente dei File come foglie.
+Se però molte funzionalità del file system operano in modo analogo sia sui File che sulle Directory (_es. creazione, cancellazione, ottenimento della dimensione etc._), come possiamo gestire queste due classi in modo uniforme per evitare di duplicare il codice?
+
+Per gestire simili strutture ad albero che rappresentano _insiemi e gerarchie di parti_ viene introdotto il pattern __Composite__: esso mira a gestire oggetti singoli, gruppi e persino gruppi di gruppi in maniera uniforme e trasparente in modo che un client non interessato alla struttura gerarchica possa utilizzarli senza accorgersi delle differenze.
+
+```plantuml
+@startuml
+scale 500 width
+
+interface Component << interface >> {
+    + {abstract} sampleOperation()
+}
+class Leaf implements Component {
+    + sampleOperation()
+}
+class Composite {
+    + sampleOperation()
+    + add(Component)
+    + remove(Component)
+}
+Composite .|> Component
+Composite "0..1" o-> "0..n" Component
+hide empty fields
+@enduml
+```
+
+Abbiamo quindi gli oggetti singoli, rappresentati dalla classe _Leaf_, e gli oggetti composti rappresentati dalla classe _Composite_.
+Per realizzare l'uniformità di gestione dobbiamo introdurre un livello di astrazione, quindi Leaf e Composite implementano una stessa __interfaccia Component__ contenente la definizione delle operazioni comuni. \
+L'uso dell'interfaccia comune permette di definire all'interno di Composite le operazioni di aggiunta e rimozione di oggetti al gruppo in modo generale, permettendo cioè che un _Composite aggreghi sia Leaf che altri Composite_.
+
+A proposito di tale aggregazione, dallo schema UML possiamo notare le relative cardinalità: "0..n" dal lato del Composite e "0..1" da quello del Component.
+Esse indicano che:
+
+- Un'istanza di Composite aggrega 0 più istanze di Component al suo interno: in questo modo si permette che al momento della creazione il Composite sia totalmente vuoto; se questo non ha alcun senso logico nell'applicazione si può invece modificare la cardinalità in "1..n" imponendo che al costruttore di Composite venga passato un Component iniziale da contenere;
+
+- Un'istanza di Component può essere contenuta in al più un'istanza di Composite: può cioè essere libero o aggregato in un gruppo, ma non può appartenere contemporaneamente a più gruppi, cosa che forza una struttura strettamente ad albero.
+
+Nella maggior parte dei casi un'istanza Composite utilizzerà gli oggetti aggregati per implementare effettivamente i metodi descritti dall'interfaccia comune, delegando a loro l'esecuzione effettiva e limitandosi ad elaborare i risultati.
+Riprendendo l'esempio di prima, per conoscere la dimensione di una Directory sarà sufficiente sommare le dimensioni dei File e delle altre Directory in essa contenuti.
+
+Il patter Composite presenta numerosi vantaggi, ma non è nemmeno esente da criticità.
+L'uso di un'interfaccia comune per Leaf e Composite permette al client di non preoccuparsi del tipo dell'oggetto con cui sta interagendo, in quanto ogni Component è in grado di eseguire le operazioni descritte nell'interfaccia in modo indistinguibile; tuttavia, questo implica che non è possibile distinguere tra oggetti singoli e composti. \
+Inoltre, l'uso dell'interfaccia per l'aggregazione nei Composite rende impossibile imporre dei controlli su cosa possa contenere un certo tipo di Composite: non si può per esempio forzare che raggruppi solo certi tipi di elementi, o che l'albero di composizione abbia profondità al più pari a tre.
+
+Un "dialetto" del pattern tenta di risolvere il problema dell'indistinguibilità tra Leaf e Composite introducendo nell'interfaccia Component un metodo `getComposite` che in un Composite restituisca `this` e in una Leaf restituisca `null`.
+L'uso di valori nulli e la necessità di strani casting rende però pericolosa l'adozione di questa versione del pattern.
diff --git a/src/08_patterns/12_decorator.md b/src/08_patterns/12_decorator.md
new file mode 100644
index 0000000000000000000000000000000000000000..b198415857f829082ccfa9560b80400a48f42db3
--- /dev/null
+++ b/src/08_patterns/12_decorator.md
@@ -0,0 +1,210 @@
+# <big>D</big>ECORATOR
+
+Immaginiamo di voler modellare con degli oggetti una grande varietà di pizze differenti sia per la base (_es. normale, integrale, senza glutine..._) che per gli ingredienti che vi si trovano sopra.
+Per ogni diversa varietà di pizza vorremmo ottenere un oggetto aderente a un'interfaccia comune `Pizza` il cui metodo `toString()` elenchi la base e gli ingredienti che la compongono.
+
+Un primo approccio _statico_ a questo problema consiste nel creare una gerarchia di classi che contenga una classe per ogni possibile combinazione di base e ingredienti, che d'ora in avanti chiameremo "__decorazioni__".
+
+```java
+public interface Pizza {}
+
+public class BaseNormale implements Pizza {
+    public String toString() { 
+        return "Sono una pizza con: base normale"; 
+    }
+}
+
+public class BaseIntegrale implements Pizza {
+    public String toString() { 
+        return "Sono una pizza con: base integrale"; 
+    }
+}
+
+public class BaseNormaleSalame extends BaseNormale {
+    public String toString() { 
+        return "Sono una pizza con: base normale, salame"; 
+    }
+}
+
+public class BaseNormaleSalamePeperoni extends BaseNormaleSalame {
+    public String toString() {
+        return "Sono una pizza con: base normale, salame, peperoni"; 
+    }
+}
+
+...
+```
+
+Come è subito ovvio, però, questo approccio risulta assolutamente da evitare per una serie di motivi: in primo luogo l'esplosione combinatoria dovuta all'accoppiamento di ogni possibile base e insieme di decorazioni, e in secondo luogo l'estrema difficoltà che comporterebbe una futura aggiunta di decorazioni.
+
+L'ideale sarebbe invece poter __aggiungere funzionalità e caratteristiche dinamicamente__, restringendo la gerarchia ad un'unica classe le cui istanze possano essere "decorate" su richiesta al momento dell'esecuzione. \
+La soluzione più semplice a questo nuovo problema parrebbe quella che viene definita una <big>G</big>OD CLASS (o _fat class_), ovvero un'unica classe in cui tramite attributi booleani e `switch` vengono attivate o disattivate diverse decorazioni.
+
+```java
+public class GodPizza {
+
+    boolean baseNormale = false;
+    boolean baseIntegrale = false;
+    ...
+
+    boolean salame = false;
+    boolean pancetta = false;
+    boolean peperoni = false;
+    ...
+
+    public void setBaseNormale(boolean status) { baseNormale = status; }
+    public void setBaseIntegrale(boolean status) { baseIntegrale = status; }
+    ...
+
+    public void setSalame(boolean status) { salame = status; }
+    public void setPancetta(boolean status) { pancetta = status; }
+    public void setPeperoni(boolean status) { peperoni = status; }
+    ...
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder("Sono una pizza con: ");
+        if (baseNormale) sb.append("base normale, ");
+        if (baseIntegrale) sb.append("base integrale, ");
+        ...
+        if (salame) sb.append("salame, ");
+        if (pancetta) sb.append("pancetta, ");
+        if (peperoni) sb.append("peperoni, ");
+        ...
+        sb.removeCharAt(sb.length() - 1);
+        sb.removeCharAt(sb.length() - 1);
+        return sb.toString();
+    }
+}
+```
+
+Si tratta però questo di un chiaro anti-pattern, una soluzione che sebbene invitante e semplice in un primo momento da realizzare nasconde delle criticità non trascurabili.
+Si tratta infatti di una chiara violazione dell'Open-Close Principle, in quanto per aggiungere un decoratore è necessario modificare la God Class; inoltre, tale classe diventa molto velocemente gigantesca, zeppa di funzionalità tra loro molto diverse (_scarsa separazione delle responsabilità_) e decisamente infernale da leggere, gestire e debuggare in caso di errori.
+
+Introduciamo dunque il pattern __Decorator__, la soluzione più universalmente riconosciuta per questo tipo di situazioni.
+
+```plantuml
+@startuml
+scale 500 width
+
+class Client 
+interface Component << interface >> {
+    + {abstract} sampleOperation()
+}
+Client .> Component
+class BaseComponent implements Component {
+    + sampleOperation()
+}
+abstract class Decorator implements Component {
+    + Decorator(Component)
+}
+Decorator "0..1" o--> "1" Component
+class ConcreteDecorator implements Decorator {
+    + ConcreteDecorator(Component)
+    + sampleOperation()
+}
+
+hide empty fields
+@enduml
+```
+
+A prima vista lo schema UML ricorda molto quello del pattern Composite: abbiamo un'interfaccia _Component_ implementata sia da un _ConcreteComponent_, ovvero una base della pizza nel nostro esempio, sia da una __classe astratta Decorator__, la quale è poi estesa da una serie di _ConcreteDecorator_.
+A differenza del Composite, tuttavia, qui ciascun Decorator aggrega __una e una sola istanza di Component__: tali decoratori sono infatti dei "wrapper", degli oggetti che _ricoprono_ altri per aumentarne dinamicamente le funzionalità. \
+È importante notare che i Decorator ricevono come oggetto da ricoprire al momento della costruzione un _generico Component_, in quanto questo permette ai decoratori di decorare oggetti già decorati.
+Questo approccio "ricorsivo" permette di creare una catena di decoratori che definisca a runtime in modo semplice e pulito oggetti dotati di moltissime funzionalità aggiunte.
+I decoratori esporranno infatti i metodi definiti dall'interfaccia __delegando__ al Component contenuto l'esecuzione del comportamento principale e aggiungendo la propria funzionalità a posteriori: in questo modo la "base" concreta eseguirà il proprio metodo che verrà successivamente arricchito dai decoratori in maniera del tutto trasparente al Client.
+
+```java
+public interface Pizza { String toString(); }
+
+public class BaseNormale implements Pizza {
+    public String toString() { 
+        return "Io sono una pizza con: base normale"; 
+    }
+}
+
+public class BaseIntegrale implements Pizza {
+    public String toString() { 
+        return "Io sono una pizza con: base integrale"; 
+    }
+}
+
+public abstract class IngredienteDecorator implements Pizza {
+    private Pizza base;
+
+    public IngredienteDecorator(Pizza base) { this.base = base; }
+
+    public String toString() {
+        return base.toString();
+    }
+}
+
+public class IngredienteSalame extends IngredienteDecorator {
+    public IngredienteSalame(Pizza base) { super(base); }
+
+    @Override
+    public String toString() { return super.toString() + ", salame"; }
+}
+
+public class IngredientePeperoni extends IngredienteDecorator {
+    public IngredientePeperoni(Pizza base) { super(base); }
+
+    @Override
+    public String toString() { return super.toString() + ", peperoni"; }
+}
+```
+
+```java
+public class Client {
+    public static void Main() {
+        // Voglio una pizza con salame, peperoni e base integrale
+        Pizza salamePeperoni = 
+            new IngredientePeperoni(
+                new IngredienteSalame(
+                    new BaseIntegrale()
+                )
+            );
+    }
+}
+
+```
+
+Vista la somiglianza, inoltre, pattern Decorator e Composite sono facilmente combinabili: si può per esempio immaginare di creare gruppi di oggetti decorati o decorare in un solo colpo gruppi di oggetti semplicemente facendo in modo che Composite, Decorator e classi concrete condividano la stessa interfaccia Component.
+
+Possiamo poi notare una cosa: al momento della costruzione un Decorator salva al proprio interno l'istanza del Component da decorare.
+Come sappiamo questo darebbe luogo ad un'_escaping reference_, ma in questo caso il comportamento è assolutamente voluto: dovendo decorare un oggetto è infatti sensato pensare che a quest'ultimo debba essere lasciata la possibilità di cambiare e che debba essere il decoratore ad adattarsi a tale cambiamento.
+
+
+È interessante poi osservare la classe astratta Decorator: in essa viene infatti inserita tutta la logica di composizione, permettendo così di creare nuovi decoratori con estrema facilità.
+Spesso, inoltre, se i decoratori condividono una certa parte di funzionalità aggiunte queste vengono anch'esse estratte nella classe astratta creando invece un metodo vuoto protetto che i decoratori reimplementeranno per operare la loro funzionalità aggiuntiva.
+
+```java
+public abstract class IngredienteDecorator implements Pizza {
+    private Pizza base;
+
+    public IngredienteDecorator(Pizza base) { this.base = base; }
+
+    public String toString() {
+        return base.toString() + nomeIngrediente();
+    }
+
+    protected String nomeIngrediente() { return ""; }
+}
+
+public class IngredienteSalame extends IngredienteDecorator {
+    public IngredienteSalame(Pizza base) {super(base);}
+
+    @Override
+    public String nomeIngrediente() { return ", salame"; }
+}
+
+public class IngredientePeperoni extends IngredienteDecorator {
+    public IngredientePeperoni(Pizza base) {super(base);}
+
+    @Override
+    public String nomeIngrediente() { return ", peperoni"; }
+}
+```
+
+Si noti come l'uso della visibilità `protected` renda l'override del metodo possibile anche al di fuori del package, aumentando così la facilità di aggiunta di nuovi decoratori.
+
+Volendo vedere un esempio concreto di utilizzo di questo pattern è sufficiente guardare alla libreria standard di Java: in essa infatti gli `InputStream` sono realizzati seguendo tale schema.
diff --git a/src/08_patterns/13_state.md b/src/08_patterns/13_state.md
new file mode 100644
index 0000000000000000000000000000000000000000..45201216b3bd03ced11676a8fcf98044f69ea7d3
--- /dev/null
+++ b/src/08_patterns/13_state.md
@@ -0,0 +1,73 @@
+# <big>S</big>TATE
+
+Come sappiamo, le macchine a stati finiti sono uno dei fondamenti teorici dell'informatica: si tratta di oggetti matematici che modellano sistemi in grado di evolvere, ovvero il cui __comportamento varia in base allo stato__ in cui si trovano.
+
+Volendo rappresentare un oggetto di questo tipo la prima idea potrebbe essere quella di realizzare il cambio di comportamento con una serie di `if` e `switch`, un approccio che come abbiamo già visto numerose volte diventa presto difficilmente sostenibile. \
+In alternativa ad esso si introduce invece lo __State pattern__ che mantenendo l'astrazione delle macchine a stati finiti permette di modellare facilmente il cambiamento di comportamento di un oggetto al modificarsi dello stato.
+Si noti che rimanendo legato al concetto di automa a stati finiti uno dei punti di forza di questo pattern è la semplicità di apportare delle modifiche al codice quando le specifiche di ciò che è stato modellato tramite una macchina a stati finiti cambiano.
+
+Un esempio di utilizzo di questo pattern potrebbe essere un software di editing di foto, in cui l'utente ha a disposizione una toolbar con diversi strumenti che gli permettono di compiere operazioni diverse sullo stesso piano di lavoro (_comportamenti diversi dell'azione "tasto sinistro sullo schermo" in base al tool selezionato_).
+
+```plantuml
+@startuml
+style 1024 width
+
+class Context {
+    - state: State
+    + sampleOperation()
+    + setState(State)
+}
+interface State << interface >> {
+    + {abstract} sampleOperation(Context)
+}
+class ConcreteState implements State {
+    + sampleOperation(Context)
+}
+Context <..  ConcreteState
+Context "   1" o-> State
+hide empty fields
+note left of Context::"sampleOperation()"
+this.state.sampleOperation(this)
+end note
+@enduml
+```
+
+In un automa a stati finiti le componenti fondamentali sono tre:
+
+- gli _stati_, tra cui si distingue lo _stato corrente_;
+- le _azioni_ che si possono intraprendere in qualunque stato;
+- le _transizioni_ da uno stato all'altro come effetto ulteriore di un'azione (_es. vim che con 'i' entra in modalità inserimento se era in modalità controllo_).
+
+Come si vede dallo schema UML, il pattern State cerca di modellare ciascuna di queste componenti: un'__interfaccia State__ raggruppa la definizione di tutte le _azioni_, rappresentate da metodi, mentre __una classe concreta per ogni stato__ definisce che effetto hanno tali azioni quando ci si trova al suo interno con l'implementazione dei suddetti metodi. \
+Infine, una classe __Context__ contiene un riferimento ad uno stato che rappresenta lo _stato corrente_ e delega ad esso la risposta alle azioni (che possono essere viste come degli "eventi"); essa espone inoltre un metodo `setState(State)` che permette di modificare lo stato corrente.
+
+```java
+public class Context {
+    private State state;
+
+    public void setState(@NotNull State s) {
+        state = s;
+    }
+
+    public void sampleOperation() {
+        state.sampleOperation(this)
+    }
+}
+```
+
+Rimane dunque solo da definire come si realizzano le _transizioni_ di stato: chi ha la responsabilità di cambiare lo stato corrente?
+Esistono due diversi approcci, ciascuno dei quali presenta delle criticità:
+
+- __gli State realizzano le transizioni__: volendo rimanere aderenti al modello degli automi a stati finiti, possiamo permettere che gli stati concreti chiamino il metodo `setState` del Context all'interno della loro implementazione dei metodi se come effetto di un'azione lo stato corrente cambia.
+Tuttavia, poiché `setState` chiede in input lo stato a cui transizionare questo approccio richiede che _gli stati si conoscano tra di loro_: si introduce così una _dipendenza tra stati_ non chiaramente visibile nello schema UML e si ha uno _sparpagliamento della conoscenza_ sulle transizioni che rende questo metodo un po' "sporco".
+
+- __il Context realizza le transizioni__: con questa seconda strategia è compito del contesto eseguire le transizioni di stato, evitando così che gli stati si debbano conoscere; l'unico depositaria della conoscenza sulle transizioni è la classe Context.
+Ciascuna azione viene dunque intrapresa in due step: il Context richiama il corrispondente metodo dello stato corrente e successivamente ne __intercetta il risultato__; può dunque decidere tramite esso se cambiare stato e eventualmente a quale stato transizionare. \
+Si tratta tuttavia di un ritorno al _table-driven design_ fatto di `if` e `switch` da cui ci eravamo voluti allontanare: come in quel caso, l'approccio risulta fattibile soltanto finché ci sono poche possibili transizioni.
+Inoltre, se una transizione non dipende dal risultato di un'azione ma da _come_ questa è stata eseguita questo approccio è totalmente impossibile in quanto tale tipo di conoscenza non è presente nella classe Context.
+
+Per via delle difficoltà poste dal secondo approccio si sceglie spesso di effettuare le transizioni all'interno degli stati: questo permette di rendere esplicito e atomico il passaggio di stato. \
+A tal proposito, è interessante notare come le istanze degli stati concreti non posseggano alcuna informazione di stato in quanto il Context a cui si riferiscono viene passato loro al momento della chiamata dei rispettivi metodi: al di là della loro identità essi sono completamente __stateless__.
+Si tratta di un approccio molto utile in caso si debbano modellare più macchine a stati finiti dello stesso tipo, in quanto l'assenza di stato rende le stesse istanze degli stati concreti __condivisibili__ tra diversi Context, in una sorta di pattern Singleton.
+
+Volendo trovare ulteriori analogie con altri pattern, il pattern State ricorda nello schema il pattern Strategy: la differenza sta però nel fatto che i diversi stati concreti sono a conoscenza l'uno dell'altro, mentre le strategie erano tra di loro completamente indipendenti.
diff --git a/src/08_patterns/14_factory.md b/src/08_patterns/14_factory.md
new file mode 100644
index 0000000000000000000000000000000000000000..175f3e839dd2f926b339efcf99310aa2d0a04bbd
--- /dev/null
+++ b/src/08_patterns/14_factory.md
@@ -0,0 +1,71 @@
+# <big>F</big>ACTORY METHOD
+
+Talvolta capita che un certo Client sia interessato a creare un oggetto non in base al suo tipo quanto all'_interfaccia_ che esso implementa: ad esso non importa conoscere la classe di cui l'oggetto è un'istanza perché essa non ha alcuna rilevanza nel suo contesto.
+Tuttavia, la normale creazione di un oggetto tramite la keyword `new` richiede di esplicitare la classe a cui esso appartiene, costringendo così il Client ad approfondire inutilmente la sua conoscenza sui tipi che implementano l'interfaccia a cui è interessato.
+
+Per evitare questo tipo di situazione introduciamo uno dei cosiddetti __pattern creazionali__, ovvero legati alla creazione di oggetti: stiamo parlando del pattern dei __Factory methods__.
+Esso definisce una classe astratta _Creator_ dotata di _metodi fabbrica_ astratti che restituiscono un'istanza di un tipo aderente all'interfaccia _Product_ a cui il Client è interessato: a quale classe appartenga effettivamente tale istanza (_Product concreto_) è però lasciato ad un _Creator concreto_ tra i tanti che estendono la classe astratta; idealmente dovrebbe esserci un creatore concreto per ogni tipo di prodotto concreto che implementa l'interfaccia Product.
+
+```plantuml
+@startuml
+scale 400 width
+
+interface Product << interface >> {
+}
+abstract class Creator {
+    + {abstract} factoryMethod()
+    + anOperation()
+}
+class ConcreteCreator implements Creator {
+    + factoryMethod()
+}
+class ConcreteProduct implements Product {
+}
+ConcreteProduct <. ConcreteCreator
+hide empty fields
+@enduml
+```
+
+Questo pattern definisce dunque un'__interfaccia per creare un Product ma lascia al Creator concreto la scelta di cosa creare effettivamente__: in questo modo all'interno della classe astratta Creator è possibile scrivere dei metodi che richiedono la creazione di un Product pur senza sapere di preciso il tipo dell'oggetto che verrà creato, in quanto questo sarà determinato dall'implementazione di `factoryMethod` del creatore concreto.
+Si sfruttano dunque al massimo grado __polimorfismo__ e __collegamento dinamico__, in quanto il tipo dell'oggetto da creare viene deciso a runtime: poiché nemmeno il Creator conosce il tipo concreto dei Product creati risulta dunque subito chiaro perché i factory methods non possano essere metodi statici di tale classe. \
+I factory methods rappresentano un esempio dell'utilità delle astrazioni permesse dai linguaggi ad oggetti: in un contesto in cui normalmente non è possibile fare overriding, come un costruttore, la soluzione è quella di virtualizzare il tutto con la creazione di metodi che possono essere esportati in classi concrete.
+Per questo motivo i factory method vengono talvolta detti anche _virtual constructors_, "costruttori virtuali".
+
+Per capire meglio il funzionamento del pattern, vediamo un esempio di come esso può essere utilizzato.
+Consideriamo un software capace di aprire contemporaneamente più documenti di tipo differente in diverse pagine, come per esempio Microsoft Word o Excel: al loro interno, quando viene creato un nuovo file vengono fatte una serie di operazioni generiche (creare la nuova pagina, mostrare vari popup...), ma ad un certo punto è necessario creare un oggetto che rappresenti il nuovo documento e il cui tipo dipende dunque dal documento creato.
+Il codice di creazione del nuovo oggetto `Documento` non può dunque trovarsi in un metodo della classe astratta `Application` (Creator) insieme con il resto delle operazioni generiche in quanto specifico della tipologia di documento creato: è dunque necessario virtualizzare la creazione dell'oggetto in un metodo `createDocument()` implementato da una serie di sottoclassi concrete `MyApplication` (ConcreteCreator) ciascuna specifica per un tipo di documento.
+
+```plantuml
+@startuml
+scale 1024 width
+
+interface Document << interface >> {
+    {abstract} Open()
+    {abstract} Close()
+    Save()
+    Revers()
+}
+abstract class Application {
+    + {abstract} CreateDocument()
+    + NewDocument()
+    + OpenDocument()
+}
+class MyApplication implements Application {
+    + CreateDocument()
+}
+class MyDocument implements Document {
+}
+MyDocument <. MyApplication
+Document <-o "     docs" Application
+note right of Application::"NewDocument()"
+Document doc = new CreateDocument();
+docs.add(doc)
+doc.Open()
+end note
+
+note right of MyApplication::"CreateDocument()"
+return new MyDocument
+end note
+hide empty fields
+@enduml
+```
diff --git a/src/08_patterns/15_abstract-factory.md b/src/08_patterns/15_abstract-factory.md
new file mode 100644
index 0000000000000000000000000000000000000000..dbf89adbb98917aaa2324987a4bcaf7d2f3d7f6a
--- /dev/null
+++ b/src/08_patterns/15_abstract-factory.md
@@ -0,0 +1,39 @@
+# <big>A</big>BSTRACT FACTORY
+
+Vediamo ora una generalizzazione del Factory method pattern che si utilizza quando, al posto di creare un solo oggetto aderente ad un'interfaccia, è necessario creare _più oggetti aderenti a varie interfacce i cui tipi concreti siano però compatibili tra di loro_.
+
+Immaginiamo per esempio di aver progettato un'applicazione cross-platform e di doverne creare la User Interface: essa dovrà avere stili diversi in base al sistema operativo sui cui si sta eseguendo.
+Non conoscendo su quale os si starà operando, il resto dell'applicazione gestirà gli elementi dell'UI tramite delle opportune interfacce che nascondano il tipo concreto delle istanze, il quale determinerà lo stile con cui esse verranno rappresentate: sarà però fondamentale che _tutti gli elementi dell'UI condividano lo stesso stile_ in modo da non creare un'orrendo arlecchino.
+
+Ecco dunque che introduciamo il pattern delle __Abstract Factory__, un metodo in grado di fornire un'__interfaccia per creare famiglie di oggetti compatibili tra loro senza specificare la loro classe concreta__ così da garantire una certa __omogeneità__ all'insieme. \
+Per fare ciò il pattern propone di creare un'interfaccia _AbstractFactory_ contenente la definizione di un factory method per ogni tipo di prodotto astratto (_Product_) e una serie di _ConcreteFactory_ che restituiranno dei _ConcreteProduct_ in uno specifico stile: in questo modo, interagendo con una Factory concreta un Client potrà ottenere in modo a lui trasparente una serie di prodotti concreti coerenti in stile tra di loro.
+
+```plantuml
+@startuml
+scale 500 width
+
+class Client {
+}
+interface AbstractFactory {
+    + createProductA()
+    + createProductA()
+}
+interface AbstractProduct <<interface>> {
+}
+Client --> AbstractFactory
+Client --> AbstractProduct
+class ConcreteFactory implements AbstractFactory {
+    + createProductA()
+    + createProductA()
+}
+class ConcreteProduct implements AbstractProduct {
+}
+hide empty fields
+@enduml
+```
+
+Tornando al problema della User Interface, volendo sfruttare l'Abstract Factory pattern dobbiamo creare un'interfaccia `GUIFactory` che contenga la dichiarazione di due metodi creazionali, `createButton()` e `createCheckbox()`: questi permetteranno al client di creare un bottone e una checkbox nello stile specificato dalla classe concreta della factory; per ciascuno di tali elementi dell'UI dobbiamo dunque creare un'interfaccia prodotto, ovvero rispettivamente le interfacce `Button` e `Checkbox`.
+All'interno delle classi factory concrete tali metodi creazionali restituiranno però dei prodotti concreti nello stile specifico della factory da cui sono prodotti: così, per esempio, una `MacFactory` (per lo stile di MacOs) creerà `MacButton` e `MacCheckbox`, mentre una `WinFactory` (per lo stile di Windows) creerà `WindowsButton` e `WinCheckbox`. \
+In questo modo la nostra applicazione dovrà possedere al suo interno unicamente un riferimento alla factory adatta al sistema operativo su cui sta girando e potrà creare tramite essa tutti gli elementi di UI di cui avrà bisogno senza preoccuparsi di specificare ogni volta lo stile: la factory concreta glielo restituirà sempre nello stile selezionato inizialmente.
+
+![Esempio abstract factory](/assets/09_esempio-abstract-factory.png)
diff --git a/src/08_patterns/16_mvc.md b/src/08_patterns/16_mvc.md
new file mode 100644
index 0000000000000000000000000000000000000000..0e2736350eca64fcec29b0a33f4997389c0a9cff
--- /dev/null
+++ b/src/08_patterns/16_mvc.md
@@ -0,0 +1,46 @@
+# <big>M</big>ODEL VIEW CONTROLLER
+
+Spesso nelle applicazioni capita che uno stesso dato sia riportato tramite diverse __viste__ all'interno dell'interfaccia utente: il colore di un testo, per esempio, potrebbe essere rappresentato contemporaneamente da una terna di valori RGB, dal suo valore esadecimale e da uno slider di colori.
+Si tratta di modi differenti di rappresentare la medesima __informazione condivisa__, che viene replicata più volte per dare all'utente diversi modi in cui visualizzarla. \
+La condivisione di un medesimo valore porta però con sé un problema: se tale dato viene modificato dall'utente interagendo con una delle viste è necessario che tale _modifica venga propagata a tutte le altre viste_ in modo da mantenere l'informazione __coerente__.
+
+Abbiamo dunque bisogno di un framework che ci permetta di mantenere un'informazione condivisa in modo efficiente e pulito e che permetta di rappresentarla facilmente sotto diversi punti di vista: l'invitante soluzione di fare semplicemente sì che le viste comunichino direttamente i cambiamenti del dato l'una con l'altra si rivela infatti velocemente impraticabile.
+Il pattern __Model View Controller__ (MVC) propone invece di suddividere la gestione del dato e dell'interazione con l'utente in tre tipologie di classi:
+
+- __Model__: un'unica classe contenente lo __stato condiviso__; si tratta dell'unico depositario dell'informazione con cui tutte le viste dovranno comunicare per aggiornare i dati mostrati.
+- __View__: una serie di classi che costituiscono l'__interfaccia con l'utente__; esse mostrano il dato secondo il loro specifico punto di vista e permettono all'utente di interagire con l'applicazione.
+- __Controller__: ciascuna vista possiede infine una classe di controllo collegata che si occupa della __logica dell'applicazione__; ogni volta che l'utente interagisce con una vista tale interazione viene passata al relativo Controller, che si occuperà di rispondere all'input eventualmente modificando lo stato condiviso nel Model.
+
+Abbiamo dunque una suddivisione dell'applicazione in tre tipi di componenti differenti che cooperano tra di loro senza però essere _strettamente_ dipendenti l'uno dall'altro.
+Un tipico ciclo di interazione tra le tre componenti funziona infatti come mostrato in figura: 
+
+1. Una View riceve un'interazione da parte dell'utente e comunica tale evento al proprio Controller;
+2. Il Controller gestisce l'interazione e se essa richiede un cambiamento dello stato comune chiede al Model di modificare il proprio contenuto;
+3. Come ulteriore passaggio, il Controller aggiorna il dato mostrato dalla View ad esso associata prima ancora che il modello sia cambiato;
+4. Ricevuta la richiesta, il Model aggiorna l'informazione condivisa e notifica _tutte_ le View del cambiamento: in questo modo esso non avrà effetto solo nella vista che ha ricevuto l'input dell'utente ma in tutte;
+5. Le View ricevono la comunicazione del fatto che il Model è cambiato e aggiornano la propria informazione mostrata recuperando il dato aggiornato dal modello (politica _pull_).
+
+![MVC](/assets/09_model-view-controller.png)
+
+Questo modello di interazione circolare permette di separare l'interfaccia utente (_view_) dall'interfaccia dello stato comune (_model_) e dalla logica del cambiamento di stato (_controller_): grazie alla mediazione del Controller le View non hanno bisogno di conoscere direttamente la struttura dei dati contenuti nel Model, cosa che ci permette di riutilizzare le stesse View, e dunque le stesse interfacce utente, per dati diversi (es. una casella di testo è una View e non dipende dal dato che ci si inserisce). \
+È inoltre interessante notare come un Controller potrebbe voler comunicare dei _cambiamenti virtuali_ alla View da cui è partito un input prima ancora che al Model venga chiesta un eventuale modifica dello stato.
+Nel caso ci siano errori nell'input inserito dall'utente, infatti, esso va informato in qualche modo: il Controller non cambierà dunque lo stato condiviso ma solo lo stato dalla relativa View in modo da mostrare un qualche messaggio d'errore.
+Similmente, se i dati inseriti sono già presenti nel Model (cosa che il Controller non può sapere a priori) quest'ultimo potrebbe avvisare il Controller di tale evenienza al momento della richiesta di cambiamento: esso dovrà dunque nuovamente notificare l'utente che l'inserimento dei dati non è andato a buon fine aggiornando la propria View.
+
+Portiamo ora attenzione su un altro aspetto: nell'insieme dei meccanismi che realizzano il pattern Model View Controller si possono riscontrare una serie di altri pattern che abbiamo già trattato.
+Per agevolare la comprensione del funzionamento di questo nuovo "mega-pattern", vediamo quindi quali sono i pattern utilizzati al suo interno:
+
+- __Observer__, poiché _le View sono Observer del Model_: ogni vista si registra come Observer del modello in modo che il Model, in pieno stile Observable, le notifichi dei suoi cambiamenti di stato.
+Spesso la strategia di aggiornamento delle viste è qui quella __pull__, ovvero quella secondo cui agli Observer viene passato un riferimento all'oggetto Observable in modo che siano loro stessi a recuperare i dati di cui hanno bisogno tramite opportuni metodi getter: questo permette infatti di memorizzare nello stesso Model i dati di diverse View. \
+Va inoltre fatto notare che se l'interfaccia esposta dalle View è un'_interfaccia a eventi_, come per esempio un'interfaccia grafica (es. un click sullo schermo genera un evento), _anche la comunicazione tra View e Controller può avvenire tramite il pattern Observer_: ciascun Controller si registra infatti come Observer degli eventi che avvengono sulla View.
+- __Strategy__, poiché _i Controller sono Strategy per le View_: poiché ad ogni vista è collegato uno e un solo Controller che regola come la vista reagisca agli input dell'utente, i Controller possono essere visti come strategie di gestione degli eventi generati dalle viste.
+Poiché le viste sono componenti sostanzialmente "stupidi" che risolvono le interazioni dell'utente delegando al proprio Controller la loro gestione, questo approccio permette per esempio di gestire viste identiche in modi diversi semplicemente cambiando il Controller ad esse associato: così, per esempio, è possibile rendere una casella di testo read-only oppure modificabile senza modificare in alcun modo la classe della relativa vista e rispettando così l'Open-Close Principle.
+- __Composite__, poiché _le View sono spesso composte da più Component_: quando le View rappresentano interfacce grafiche (GUI) esse sono spesso realizzate componendo diversi elementi tra di loro (es. aree di testo, bottoni, etc...).
+Per questo motivo è spesso prevalente il pattern Composite nella loro implementazione, utile specialmente per quanto riguarda la creazione su schermo dell'interfaccia, che viene disegnata pezzo per pezzo.
+
+In conclusione, il Model è in grado di interagire con tutte le viste che l'osservano tramite un unico comando (_update_), mentre le View comunicano con il Model passando attraverso il Controller, che fa da una sorta di "Adapter" tra i due.
+Questo permette allo stesso dato di avere interfacce disomogenee senza alcun tipo di problema riguardante la coerenza dello stesso.
+
+Tuttavia, il problema principale del pattern Model View Controller è la _dipendenza circolare_ tra le tre componenti: le view comunicano ai rispettivi controller gli eventi, questi li elaborano e aggiornano il modello il quale a sua volta avvisa le view dei cambiamenti di stato.
+Questa struttura fortemente interconnessa rende difficoltoso lo sviluppo e il testing in quanto non esiste un chiaro punto da cui partire a costruire: si potrebbe pensare di fare mocking delle view e iniziare a sviluppare il resto, ma questo approccio porta comunque a una serie di inutili complicazioni; bisogna inoltre considerare che il testing delle view è spesso particolarmente complesso coinvolgendo varie funzioni di libreria o funzioni grafiche. \
+Come vedremo nel prossimo paragrafo, per ovviare a questo problema si decide spesso di spezzare il circolo vizioso di Model, View e Controller modificando lievemente le rispettive dipendenze.
diff --git a/src/08_patterns/17_mvp.md b/src/08_patterns/17_mvp.md
new file mode 100644
index 0000000000000000000000000000000000000000..4df25e88cae638ee253dbda3eef1de5f6a567137
--- /dev/null
+++ b/src/08_patterns/17_mvp.md
@@ -0,0 +1,11 @@
+# <big>M</big>ODEL VIEW PRESENTER
+
+Come preannunciato esiste una variante del Model View Controller chiamata __Model View Presenter__ che fornisce una soluzione al problema del testing delle viste e delle relative interfacce grafiche.
+Questo nuovo pattern eleva il ruolo del Controller, ora chiamato _Presenter_, a completo _intermediario tra View e Model_ in entrambi i sensi di comunicazione: non solo dunque le View delegano ai rispettivi Presenter la gestione delle interazioni con l'utente, ma al momento del cambiamento dell'informazione condivisa il Model notifica non direttamente le viste ma i Presenter stessi, i quali avranno dunque il compito di aggiornare la propria View per mostrare il dato modificato. 
+
+![MVP](/assets/09_model-view-presenter.png)
+
+Model e View perdono dunque alcun legame diretto, facendo apparire sempre più i Presenter come Adapter tra stato concreto (_model_) e stato virtuale mostrato all'utente (_view_).
+La rottura di tale legame facilita il testing delle viste poiché invece di verificare che una vista e la rispettiva controparte grafica abbiano ricevuto e processato correttamente un aggiornamento del dato da parte del Model è sufficiente verificare che un update del Model provochi nei Presenter un aggiornamento del dato mostrato dalla propria View: siamo dunque riusciti a __isolare l'interfaccia logica da quella grafica__, rendendo più semplice il testing di entrambe e sfoggiando un esempio importante del cosiddetto _design for testing_.
+
+In ultimo, utilizzando questo pattern è importante fare attenzione di mantenere segreta la rappresentazione interna del Model ai Presenter e viceversa, evitando in particolar modo eventuali _escaping reference_: la separazione delle responsabilità costruita con la suddivisione dei dati dalla loro logica di gestione perderebbe infatti alcuna valenza se si legassero troppo strettamente Model e Presenter.
diff --git a/src/08_patterns/18_builder.md b/src/08_patterns/18_builder.md
new file mode 100644
index 0000000000000000000000000000000000000000..b04f33122e4f5887684e92bb34e5b04ad967683a
--- /dev/null
+++ b/src/08_patterns/18_builder.md
@@ -0,0 +1,157 @@
+# <big>B</big>UILDER
+
+Può talvolta capitare che l'inizializzazione di un'istanza di una classe richieda un numero molto grande di _parametri_, alcuni dei quali _obbligatori_ e altri _facoltativi_.
+Come si realizzano i costruttori della classe in questo tipo di situazioni?
+
+## Telescoping constructor pattern
+
+L'approccio più immediato a questo problema è quello dei __costruttori telescopici__ (_telescoping constructor pattern_): all'interno della classe si realizza _un costruttore completo_ che richiede tutti i parametri e una serie di _costruttori secondari_ che invece prendono i parametri obbligatori e _diverse combinazioni dei parametri opzionali_, rimappando poi spesso la propria esecuzione sul costruttore completo tramite l'assegnamento di valori di default ai parametri non ricevuti.
+
+```java
+public class MyClass {
+    private final T0 optionalField1;
+    private final T1 mandatoryField;
+    private final T2 optionalField2;
+
+    public MyClass(T1 mf) {
+        this(defaultValue1, mf, defaultValue2);
+    }
+
+    public MyClass(T1 mf, T0 of) {
+        this(of, mf, defaultValue2);
+    }
+
+    public MyClass(T1 mf, T2 of) {
+        this(defaultValue1, mf, of);
+    }
+
+    public MyClass(T1 mf, T0 of1, T2 of2) {
+        this.optionalField1 = of1;
+        this.optionalField2 = of2;
+        this.mandatoryField = mf;
+    }
+}
+```
+
+Questa tecnica si rivela però presto molto poco funzionale: innanzitutto il numero di costruttori da realizzare cresce esponenzialmente nel numero di parametri opzionali, rendendo la classe estremamente confusionaria. \
+Sorgono inoltre dei problemi nel caso di _parametri opzionali dello stesso tipo_, in quanto è impossibile disambiguare tra di essi al momento della definizione dei costruttori: con due parametri opzionali dello stesso tipo, per esempio, non sarebbe possibile distinguere il costruttore che assegni il primo ma non il secondo e viceversa (si noti come non si può nemmeno distinguere tramite il nome del costruttore in quanto questo deve necessariamente essere lo stesso della classe).
+Se linguaggi come Python risolvono questo problema imponendo che il chiamante di un costruttore espliciti il nome del parametro opzionale che sta assegnando, questo tipo di meccanismo non esiste in Java: ciò ci costringerebbe quindi a far sì che nei costruttori vengano passati o tutti i parametri dello stesso tipo o nessuno di essi.
+
+## JavaBeans pattern
+
+Per risolvere i problemi appena visti la prossima soluzione che viene in mente è dunque quella di fornire un _unico costruttore_ che prenda in input _solamente i parametri obbligatori_ e creare poi una serie di _setter per i parametri opzionali_: si tratta del cosiddetto __pattern JavaBeans__.
+
+```java
+public class MyClass {
+    private T0 optionalField1;
+    private T1 mandatoryField;
+    private T2 optionalField2;
+
+    public MyClass(T1 mf) {
+        this.mandatoryField = mf;
+    }
+
+    public void setOptionalField1(T0 of) {
+        this.optionalField1 = of;
+    }
+
+    public void setOptionalField2(T2 of) {
+        this.optionalField2 = of;
+    }
+}
+```
+
+Anche questo approccio presenta tuttavia delle notevoli difficoltà.
+In primo luogo, un oggetto costruito con il pattern JavaBeans _non può essere immutabile_ in quanto richiede la presenza di setter per i propri attributi opzionali (che dunque non possono essere `final`): possiamo dunque creare solo oggetti mutabili. \
+Un problema forse più grave è inoltre che questo pattern ammette la presenza di _momenti nella vita di un oggetto in cui esso non è stato ancora costruito completamente_: tra la creazione e l'assegnamento tramite setter dei parametri opzionali, infatti, l'istanza si trova in uno stato non finito e dunque non consistente che potrebbe creare numerosi problemi in sistemi di tipo concorrente o multi-thread.
+
+## Builder pattern
+
+Gli autori del libro Effective Java propongono dunque un nuovo pattern che prende gli aspetti migliori della prima e della seconda soluzione finora proposta risolvendo al tempo stesso i problemi di entrambe: essa permetterà infatti di creare oggetti immutabili (rendendo gli attributi `final`) e di assegnare solo alcuni dei parametri opzionali senza generare problemi di inconsistenza o di sovrapposizione dei tipi degli attributi.
+Questo pattern creazionale prende il nome di __Builder__.
+
+```plantuml
+@startuml
+style 500 width
+
+class MyClass {
+    - OptionalField : T0
+    - MandatoryField : T1
+
+    - MyClass(Builder)
+}
+class MyClass.Builder {
+    + Builder(T1)
+    + withOptionalField(T0) : Builder
+    + build() : MyClass
+}
+MyClass +--> MyClass.Builder
+hide empty fields
+@enduml
+```
+
+Data una classe da costruire `MyClass` avente parametri obbligatori e opzionali il primo passo è quello di rendere __privato__ il suo costruttore, il quale prenderà in input non più una lista di parametri ma un'istanza di una __nuova classe `Builder`__.
+Tale classe viene definita come una _classe statica, pubblica e interna_ a `MyClass`, con la quale condivide il tipo e il numero di attributi obbligatori e opzionali (questi ultimi subito inizializzati al loro valore di default). \
+Seguendo il pattern JavaBeans, la classe Builder esporrà un costruttore pubblico contenente solo i parametri obbligatori e una serie di setter per i parametri opzionali.
+Ma a che pro costruire un oggetto della classe Builder quando quella che volevamo ottenere era un'istanza di `MyClass`?
+La risposta sta nella definizione __metodo `build()`__: tramite esso, il Builder restituirà un'istanza di MyClass inizializzata con propri i parametri obbligatori e opzionali; essendo una classe interna, infatti, il Builder sarà l'unico in grado di accedere al costruttore privato di `MyClass`.
+
+```java
+public class MyClass {
+    private final T0 optionalField1;
+    private final T1 mandatoryField;
+    private final T2 optionalField2;
+
+    private MyClass(Builder builder) {
+        mandatoryField = builder.mandatoryField;
+        optionalField1 = builder.optionalField1;
+        optionalField2 = builder.optionalField2;
+    }
+
+    public static class Builder {
+        private T1 mandatoryField;
+        private T0 optionalField1 = defaultValue1;
+        private T2 optionalField2 = defaultValue2;
+
+        public Builder(T1 mf) {
+            mandatoryField = mf;
+        }
+
+        public Builder withOptionalField1(T0 of) {
+            optionalField1 = of;
+            return this;
+        }
+
+        public Builder withOptionalField2(T2 of) {
+            optionalField2 = of;
+            return this;
+        }
+
+        public MyClass build() {
+            return new MyClass(this);
+        }
+    }
+}
+```
+
+Questo pattern è particolarmente intelligente per una serie di motivi: innanzitutto, rendendo privato il costruttore di `MyClass` ci si assicura che le sue istanze siano costruite unicamente tramite il Builder.
+A tal proposito, il fatto che `Builder` sia una classe __statica__ è di non poca importanza: questo permette infatti di creare una sua istanza senza prima istanziare la classe che la contiene, cosa che come abbiamo visto sarebbe impossibile essendo il costruttore di MyClass privato.
+Per creare un'istanza di Builder è dunque sufficiente la seguente sintassi:
+
+```java
+MyClass.Builder = new MyClass.Builder(...);
+```
+
+Si potrebbe notare che essendo statica la classe Builder potrà accedere solamente agli elementi statici di `MyClass`, ma questo non costituisce un problema: come abbiamo visto, essa dovrà solamente richiamarne il costruttore, che per sua stessa natura è sempre statico. \
+È importante notare che non vale però il contrario: `MyClass`, una volta ricevuta un'istanza di Builder come parametro del costruttore, può benissimo accedere ai suoi campi privati e sfrutta questa possibilità per copiare i valori dei parametri obbligatori e opzionali che il Builder ha ricevuto all'interno dei propri attributi.
+Assegnando tali valori al momento della creazione, gli attributi di `MyClass` potranno quindi anche essere `final`, permettendo così la creazione di oggetti immutabili.
+
+Un altro particolare da sottolineare è che i setter degli attributi opzionali del Builder sono setter un po' "spuri", in quanto invece di non ritornare nulla _ritornano il Builder stesso_: questo permette infatti di concatenare più setter l'uno con l'altro ottenendo così una notazione più fluente.
+È possibile infatti creare inline un'istanza di Builder, settare direttamente i suoi parametri opzionali e infine richiamare il metodo `build()` per ottenere facilmente un'istanza di `MyClass`:
+
+```java
+MyClass inst = (new MyClass.Builder(mandatoryField).withOptionalField1(optionalField1)).build();
+```
+
+L'utilizzo di un Builder risolve inoltre eventuali problemi dovuti alla concorrenza: quando viene chiamato il metodo `build()` l'istanza di `MyClass` viene restituita già completa, ovvero con tutti i parametri obbligatori e opzionali al valore desiderato (o di default se nessun setter è stato chiamato).
+Abbiamo così eliminato la possibilità di inconsistenze e creazioni parziali delle istanze di `MyClass`.
diff --git a/src/09_uml/00_index.md b/src/09_uml/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..df5c3b227b0f5600863f2e3f59acaa017ba2a884
--- /dev/null
+++ b/src/09_uml/00_index.md
@@ -0,0 +1,14 @@
+# UML
+
+__UML__ (_Unified Modeling Language_) è un linguaggio di _modeling_ il cui scopo è determinare uno standard comune nella rappresentazione visuale del software.
+
+UML rappresenta in realtà una famiglia di formalismi, che si concretizzano nei concetti di __diagrammi__.
+
+- [**Class diagram**](./01_class-diagram.md)
+- [**Sequence diagram**](./02_sequence-diagram.md)
+- [**State diagram**](./03_state-diagram.md)
+- [**Superstate**](./04_superstate.md)
+- [**Use cases diagram**](./05_use-cases.md)
+- [**Activity diagram**](./06_activity-diagram.md)
+- [**Component diagram**](./07_component-diagram.md)
+- [**Deployment diagram**](./08_deployment-diagram.md)
diff --git a/src/09_uml/01_class-diagram.md b/src/09_uml/01_class-diagram.md
new file mode 100644
index 0000000000000000000000000000000000000000..96aa65cef79eaa9927d8c09a7efe5c3b759df5d4
--- /dev/null
+++ b/src/09_uml/01_class-diagram.md
@@ -0,0 +1,116 @@
+# Class diagram
+
+## Concetto e struttura
+
+Lo scopo del __diagramma delle classi__ è fornire una vista statica del software (una sorta di "fotografia") tramite la rappresentazione delle sue classi, corredate di metodi, attributi e relazioni.
+
+<!-- Hardcoded diagram because the PlantUML jekyll plugin produces a malformed version -->
+<p class="plantuml-parent" align="center">
+    <object class="plantuml" style="width: 70%" data="/assets/11_UML-base.svg"></object>
+</p>
+
+I componenti identificabili in un diagramma delle classi sono:
+- __oggetti__ (_Classi_ e _Interfacce_), rispettivamente riconoscibili per le lettere "C" e "I" nella parte superiore di ogni blocco. 
+```plantuml
+@startuml
+
+hide fields
+hide methods
+class Card
+interface Comparable
+@enduml
+```
+<p class="tab"> Esiste anche il marcatore "A", che rappresenta una classe astratta.
+Inoltre, per i diagrammi UML relativi a Java si può usare la lettera "E" per rappresentare le classi enum;</p>
+
+```plantuml
+@startuml
+
+hide fields
+hide methods
+abstract CardStack
+enum Suit
+@enduml
+```
+
+- __metodi__: preceduti da un cerchio e dal tipo di valore ritornato;
+```plantuml
+@startuml
+
+class Deck {
+    + void shuffle()
+    + Card draw()
+    + void sort()
+}
+@enduml
+```
+- __attributi__: preceduti da un quadrato, corrispondono agli attributi dell'oggetto;
+```plantuml
+@startuml
+
+class Card {
+    - Rank rank
+    - Suit suit
+}
+@enduml
+```
+- __relazioni__: frecce che connettono gli oggetti.
+
+È possibile rappresentare il _cerchio_ dei metodi e il quadrato degli attributi con colori diversi in base alla visibilità. 
+In Java, ad esempio, si può usare il <span style="color:green">verde</span> per la visibilità `public`, l'<span style="color:orange">arancio</span> per `protected` e il <span style="color:red">rosso</span> per `private`.
+
+Valgono anche due regole sintattiche generali:
+- se una scritta è in _corsivo_ vuol dire che all'elemento corrispondente manca qualche definizione ed è dunque da considerarsi __astratto__;
+- se una scritta è <u>sottolineata</u> vuol dire che l'elemento corrispondente (tipicamente metodo o attributo) è __statico__, ovvero ha una visibilità a livello di classe e non a livello di istanza (_i.e_ è possibile riferircisi ad esso anche senza avere precedentemente istanziato la classe).
+
+### Relazioni
+
+Nel diagramma delle classi UML esistono relazioni di diversi tipi.
+Ogni relazione viene rappresentata tramite una specifica forma di freccia:
+- __frecce tratteggiate__ (___associazione___): sono le più generiche e indicano una relazione "gerarchica" tra classi.
+Ciò che c'è scritto nella classe da cui parte la freccia dipende dal codice che c'è nella classe a cui arriva la freccia (_e.g._ `Deck` dipende da `Collections`);
+```plantuml
+@startuml
+hide fields
+hide methods
+class Deck
+class Collections
+Deck .> Collections
+@enduml
+```
+- __frecce con rombo bianco__ (___aggregazione___): indica che all'interno della classe (_e.g._ `Deck`) è presente una collezione (in questo caso una lista) di \(n\) oggetti (`Card` nell'esempio). \
+Questa relazione non è più tra classi, bensì tra _istanze_ delle classi (_e.g._ un'istanza di `Deck` aggrega da 0 a 52 carte);
+```plantuml
+@startuml
+hide fields
+hide methods
+class Deck
+class Card
+Deck .o Card
+@enduml
+```
+- __frecce con rombo nero__ (___composizione___): è utilizzata quando si hanno degli elementi che sono _fisicamente_ collegati tra loro (non solo virtualmente come nel caso delle carte). \
+Senza l'uno l'altro non può vivere e viceversa. \
+Un esempio può essere la rappresentazione del concetto di _aereo_: senza il _motore_ l'aereo non può esistere, poichè il primo è un oggetto indispensabile per funzionamento del secondo. Specularmente, non accadrà mai che il motore passi ad un altro aereo (a differenza delle carte che possono passare a più mani).
+```plantuml
+@startuml
+hide fields
+hide methods
+class Aereo
+class Motore
+Aereo .* Motore
+@enduml
+```
+- __frecce con la punta a triangolo__ (___implementazione___): una classe può _implementare_ una classe astratta o un'interfaccia.
+```plantuml
+@startuml
+hide fields
+hide methods
+class Card
+interface Comparable
+Card .|> Comparable
+@enduml
+```
+
+La direzione delle frecce è importante perché indica il _senso_ della relazione. \
+Per esempio, il mazzo _conosce_ le carte che contiene, ma le carte _non conoscono_ i mazzi di cui fanno parte &#8211; è per questo che la direzione della freccia va da `Deck` a `Card` e non viceversa.
diff --git a/src/09_uml/02_sequence-diagram.md b/src/09_uml/02_sequence-diagram.md
new file mode 100644
index 0000000000000000000000000000000000000000..a27176ae8155b8bdf4c68b4f22bb8fbee6b79d1d
--- /dev/null
+++ b/src/09_uml/02_sequence-diagram.md
@@ -0,0 +1,16 @@
+# Sequence diagram
+
+## Concetto e struttura
+
+Lo scopo del __diagramma di sequenza__ è rappresentare il flusso di interazione tra attori all'interno di un software nel tempo.
+Di seguito ne è indicato un esempio.
+
+![Sequence diagram](/assets/11_sequence-diagram-example.png)
+
+Si compone di alcuni elementi chiave:
+- __attori__: rappresentano le entità coinvolte nel processo; spesso sono _oggetti_;
+- __invocazioni__: identificano chiamate di metodo su un attore da parte di un altro e sono rappresentate con una freccia che va da _sinistra verso destra_. \
+La parte a sinistra è il _chiamante_ e la parte a destra è il _chiamato_;
+- __valori di ritorno__: visualizzati tramite una freccia tratteggiata che va da _destra verso sinistra_;
+- __cicli__: aree rettangolari etichettate con il termine `loop` che specificano la presenza di un ciclo in una certa zona del diagramma;
+- __condizioni__: aree rettangolari etichettate con il termine `opt` che specificano la necessità di verificare alcune condizioni prima di entrare nella zona corrispondente.
diff --git a/src/09_uml/03_state-diagram.md b/src/09_uml/03_state-diagram.md
new file mode 100644
index 0000000000000000000000000000000000000000..645daff584b422a818d7db146e7c416acb998977
--- /dev/null
+++ b/src/09_uml/03_state-diagram.md
@@ -0,0 +1,32 @@
+# State diagram
+
+## Concetto e struttura
+
+L'obiettivo del **diagramma di stato** è fornire un'astrazione di comportamento significativa che sia comune all'intera classe.
+
+La sua struttura deriva dai classici *State Charts*, dei quali costituisce un'ulteriore astrazione.
+
+Al fine di comprendere meglio i diagrammi di stato, può essere utile ricordare che:
+
+> Negli _State Charts_, un automa è una sestupla \\(\langle S, \\, I, \\, U; \\; \delta, \\, t, \\, s_0 \rangle\\).
+> - \\(S\\): insieme finito e non vuoto degli stati;
+> - \\(I\\): insieme finito dei possibili ingressi;
+> - \\(U\\): insieme finito delle possibili uscite;
+> - \\(\delta\\): funzione di transizione;
+> - \\(t\\): funzione di uscita;
+> - \\(s_0\\): stato iniziale.
+
+Negli _State Diagram_, ogni _stato_ è rappresentato da un rettangolo e lo _stato iniziale_ è indicato da un pallino nero.
+
+![](/assets/11_state-diagram-example.png)
+
+Nel diagramma le frecce possono avere diversi significati:
+- **evento/azione**: semplice e immediata transizione da uno stato ad un altro;
+- **guardie**: disambiguano le transazioni in uscita da uno stato che sono legate ad uno stesso evento;
+- **_time event_**: rappresentano eventi temporizzati. 
+    * *After(duration)*: indicano un tempo massimo di permanenza nello stato destinazione. \
+    Allo scadere del timer, lo stato cambia.
+* **_change event_**: rappresentano eventi che si innescano al verificarsi di un cambiamento.
+    * *When(condition)*: indicano eventi espressi in termini di valori degli attributi.
+
+Il verificarsi di eventi non esplicitamente marcati da un arco deve portare alla terminazione dell'esecuzione e al sollevamento di un errore.
diff --git a/src/09_uml/04_superstate.md b/src/09_uml/04_superstate.md
new file mode 100644
index 0000000000000000000000000000000000000000..34027d705d92f22b6361d17359427392474327b8
--- /dev/null
+++ b/src/09_uml/04_superstate.md
@@ -0,0 +1,17 @@
+# Superstate
+
+Ulteriore evoluzione dello State Diagram, il **Superstate** consente di rappresentare più facilmente una "gerarchia" di stati.
+
+La transizione in uno stato può quindi condurre ad un'altra FSM concettualmente "innestata".
+
+![Esempio superstate](/assets/11_superstate-example.png)
+
+Nel caso d'esempio, lo stato `acceso` possiede al suo interno un ulteriore diagramma di stato.
+
+## Ulteriori aggiunte
+
+* è possibile associare al diagramma uno stato **_history_**, il cui scopo è memorizzare lo stato storico prima dell'interruzione dell'FSM;
+* è possibile rendere il diagramma capace di rappresentare il concetto di **concorrenza** tramite la divisione in **regioni** (ognuna regolata da una propria FSM).
+Le regioni possono essere attive contemporaneamente. I confini tra regioni, come mostrato nell'esempio, sono identificati da linee tratteggiate.
+
+![Esempio concorrenza](/assets/11_concurrency-example.png)
diff --git a/src/09_uml/05_use-cases.md b/src/09_uml/05_use-cases.md
new file mode 100644
index 0000000000000000000000000000000000000000..6fd10a3fbed33b4055600f74b3a9ad7834260809
--- /dev/null
+++ b/src/09_uml/05_use-cases.md
@@ -0,0 +1,131 @@
+# Use cases diagram
+
+## Concetto e struttura
+
+I **diagrammi dei casi d'uso** rappresentano l'astrazione di un insieme di scenari tra loro correlati. \
+Essi adottano un linguaggio che verte alla risoluzione di esigenze comunicative tramite un lessico potenzialmente meno tecnico.
+Tale natura "informale" li rende ottimi mezzi di comunicazione col *cliente*.
+
+Possono essere utilizzati, ad esempio, per:
+* eplicitare differenti modalità di fare un compito;
+* stabilire quale dovrebbe essere la normale interazione nello scenario e le eccezioni che possono verificarsi.
+
+Infatti, ogni _scenario_ è corredato di:
+* __pre e post condizioni__ da rispettare;
+* __flusso di esecuzione__ da percorrere in condizioni normali;
+* eventuali __eccezioni__ e loro possibili trattamenti.
+
+Infine, parte della versatilità degli *Use Case diagrams* risiede nella loro capacità di collegarsi, eventualmente, ad altri tipi di diagrammi (*Sequence, Activity, etc*) che possono essere impiegati per descriverne in modo più approfondito il flusso.
+
+## Scenari
+
+I componenti di ogni scenario si dividono in **Attori** e **Casi d'Uso**.
+
+```plantuml
+@startuml
+scale 500 width
+
+:Actor: - (Use)
+"Attore" as Actor
+"Caso d'uso" as (Use)
+@enduml
+```
+
+In generale il *collegamento* tra un attore e un caso d'uso rappresenta una __relazione di partecipazione__, *i.e* "Questo attore partecipa a questo caso d'uso".
+
+L'interazione può comunque essere denominata.
+
+Sono contemplati anche collegamenti fra un caso d'uso e un altro (vedi [paragrafo dedicato](#assoc-ucuc)).
+
+## Identificazione degli attori
+
+Gli _attori_ non sono necessariamente persone fisiche.
+Possono corrispondere anche a dei **ruoli** o addirittura ad un **sistema esterno**.
+
+Ogni attore è un'entità esterna al sistema ed interagisce direttamente con esso, fungendo allo stesso tempo da *fonte* e *destinatario* di informazione.
+
+Ci sono due attori particolari:
+
+* **attore beneficiario**: colui che trae beneficio dall'interazione con lo use case, *i.e.* chi è **interessato** a quella funzionalità. \
+Gli altri attori possono cambiare, ma il beneficiario rimmarrà probabilmente lo stesso;
+* **attore primario**: colui che avvia l'interazione con lo use case.
+
+## Identificazione use case
+Il miglior modo di identificare i casi d'uso è interrogarsi su due fronti:
+
+* **sistema**: _"quali funzionalità si desidera che il sistema possieda?"_;
+* **attori beneficiari**: _"cosa vogliono?"_, _"come agiscono?"_, _"perchè si interfacciano col sistema?"_ e _"cosa si aspettano?"_.
+
+## Associazioni
+
+Ogni diagramma dei casi d'uso deve seguire due convenzioni per quanto riguarda le associazioni.
+
+* > Ogni attore deve avere almeno un'interazione con un caso d'uso.
+
+Un attore che non dovesse possedere alcuna associazione con un caso d'uso sarebbe impossibilitato a interagire col sistema e rappresentarlo nel diagramma non avrebbe alcun senso.
+
+* > Ogni caso d'uso deve essere associato ad almeno un attore.
+
+Un caso d'uso che non coinvolge alcun attore è un caso d'uso che, per definizione, non ha senso di esistere, poichè nessuno è in grado di interagirvi.
+
+### <a name="assoc-ucuc"></a>Relazioni _use case - use case_
+Esistono due tipologie di relazioni tra use case:
+* **inclusione (*include*)**: relazione che esprime il predicato *"far parte di"*.  \
+Chi include conosce sempre gli inclusi, ma non viceversa.
+La parte inclusa *deve* essere eseguita;
+* **estensione (*extend*)**: relazione che viene utilizzata per rappresentare casi eccezionali che specificano comportamenti particolari in alcuni use case.
+
+### Generalizzazione
+
+L'associazione di **generalizzazione** rappresenta un particolare tipo di relazione, applicabile sia ad una coppia *attore - attore* che ad una coppia *use case - use case*.
+
+La sua semantica dipende dal contesto a cui viene applicata:
+* **tra attori**: permette di esplicitare eventuali relazioni tra ruoli. \
+Ad esempio un ruolo potrebbe includerne un altro.
+* **tra use case**: la semantica è simile all'*extend*, ma senza punti d'estensione. Infatti, alcune parti della descrizione vengono *ereditate* e altre vengono *sostituite*. Non si applica  il secondo principio della Liskov.
+
+## Esempi di utilizzo
+
+Nel seguente diagramma,
+
+```plantuml
+@startuml
+scale 800 width
+
+"Book Borrower" as BB
+"Extend Loan" as (ext)
+"Borrow copy of a book" as (bor)
+"Check for reservation" as (chk)
+BB -- ext
+BB -- bor
+ext ..> chk : << include >>
+bor ..> chk : << include >>
+@enduml
+```
+
+l'attore _Book Borrower_ è associato alle seguenti operazioni:
+- prendere in prestito un libro;
+- chiedere l'estensione del prestito di un libro.
+
+In entrambi i casi, il bibliotecario deve controllare l'esistenza di una richiesta di prenotazione per il libro.
+
+Il prossimo diagramma è differente:
+
+```plantuml
+@startuml
+scale 1024 width
+
+"Book Borrower" as BB
+usecase bor as "Borrow copy of book
+--
+**extension points**
+status validation:
+after confirming identity"
+"Refuse loan" as (ref)
+BB - bor
+bor <. ref : << extend >>
+@enduml
+```
+
+_rifiuta il prestito_ può essere l'__estensione__ di un comportamento normale come _prendi in prestito il libro_.
+In quest'ultimo ci sono dei punti di estensione in cui vengono fatti dei controlli, come la verifica dello stato di prestito del libro o dell'identità del richiedente.
diff --git a/src/09_uml/06_activity-diagram.md b/src/09_uml/06_activity-diagram.md
new file mode 100644
index 0000000000000000000000000000000000000000..20ed81397bf17aca151f3fe505da5ee4658c5569
--- /dev/null
+++ b/src/09_uml/06_activity-diagram.md
@@ -0,0 +1,51 @@
+# Activity diagram
+
+## Concetto e struttura
+
+Gli _activity diagram_ presentano una conformazione simile agli _state diagram_, ma con svariate differenze:
+* al posto degli stati __vi sono le *attività*__;
+* non si usano più le __transizioni__ etichettate tramite eventi &#8211; queste sono quasi tutte __implicitamente temporizzate__;
+* possono esserci ___azioni_ dentro le attività__;
+* le *attività* possono rappresentare __elementi esterni__ al sistema.
+
+La peculiare capacità di collegarsi con attività esterne fa sì che sia possibile utilizzare gli activity diagram come __collante__ con e tra diversi casi d'uso.
+
+Inoltre la __visuale parzialmente informale__, eppure leggermente più tecnica e profonda rispetto ai diagrammi dei casi d'uso, rende gli activity diagram un __ottimo mezzo di comunicazione interna__ (*e.g.* con un manager).
+
+## Livelli di astrazione
+
+Si possono utilizzare i diagrami delle attività per:
+- descrivere la logica interna di un __business process__ (caso più comune);
+- descrivere il __flusso interno di un metodo__, con eventuali indicazioni di (pseudo)concorrenza;
+- dettagliare il __flusso di un caso d'uso__, ovvero chiarire meglio il suo flusso di esecuzione rispetto ad altri diagrammi (*e.g.* Sequence Diagram). 
+Questa rappresentazione è assai utile nei casi in cui, ad esempio, la concorrenza è un fattore rilevante.
+
+
+## Sincronizzazione
+
+![Esempio activity diagram](/assets/11_activity-example.png)
+
+Attraverso l'uso di barre si possono stabilire dei punti di sincronizzazione (JOIN). \
+I JOIN, se non diversamente specificato, vengono considerati in ___AND___.
+
+È però possibile porre dei vincoli diversi per stabilire i criteri di soddisficamento della barra di sincronizzazione (come una __*OR*__).
+
+## Decisioni
+
+![Esempio activity decision diagram](/assets/11_activity-decision-example.png)
+
+È possibile specificare nel flusso di esecuzione dei momenti di __decisione__.
+I corsi d'azione intraprendibili in questi frangenti sono rappresentati tramite degli archi.
+
+Le decisioni devono rispettare due proprietà:
+* gli archi collegati alla decisione devono essere __mutualmente esclusivi__;
+* l'__unione__ delle condizioni di decisione deve essere sempre vera.
+
+È bene puntualizzare che i punti di decisione sono _veri_ momenti di decisione umana, non banali valutazioni di una condizione logica.
+
+## Swim lane
+
+![Swim lane esempio](/assets/11_activity-swim-lane-example.png)
+
+Si può partizionare il diagramma al fine di rappresentare, sulle singole _activity_, delle particolari responsibilità.
+Queste vengono visualizzate tramite delle _"corsie"_ verticali che identificano _chi_ svolge una determinata attività.
diff --git a/src/09_uml/07_component-diagram.md b/src/09_uml/07_component-diagram.md
new file mode 100644
index 0000000000000000000000000000000000000000..01be7e2bc2bfff77ada91af6118abc1b5c6c1377
--- /dev/null
+++ b/src/09_uml/07_component-diagram.md
@@ -0,0 +1,25 @@
+# Component diagram
+
+## Concetto e struttura
+
+Lo scopo del __diagramma dei componenti__ è rappresentare e raggruppare i componenti del sistema. \
+_"Componente"_ è un termine trasversale che include file, librerie, documenti _etc._ (ma che è diverso dal concetto di _classe_!).
+
+Il diagramma include:
+* __componenti__: rettangoli che rappresentano una funzione di sistema ben determinata. \
+Possono essere _annidati_;
+* __interfacce__: cerchi che indicano le interfacce implementate o utilizzate dai componenti. \
+I collegamenti con i componenti indicano la presenza di una dipendenza;
+* __stereotipi__: racchiusi tra i caratteri `<<>>`, etichettano e identificano una serie di funzionalità appartenenti ad uno stesso "gruppo".
+
+![Esempio component diagram](/assets/11_component-diagram-example.png)
+
+Si noti che un componente può usarne un altro conoscendone solo l'interfaccia.
+
+## Identificare i componenti
+
+Alcune linee guida per identificare e rappresentare correttamente i componenti sono:
+- capire quali parti del sistema sono rimpiazzabili facilmente e/o sono versionate separatamente;
+- identificare quali parti del sistema svolgono una funzione ben determinata;
+- pensare in termini di "gerarchia" dei componenti;
+- chiarire l'esistenza di dipendenze con altri componenti e di dipendenze con le interfacce.
diff --git a/src/09_uml/08_deployment-diagram.md b/src/09_uml/08_deployment-diagram.md
new file mode 100644
index 0000000000000000000000000000000000000000..97689d2e4cd4ac832fd97e8d7de51beb84aae140
--- /dev/null
+++ b/src/09_uml/08_deployment-diagram.md
@@ -0,0 +1,12 @@
+# Deployment diagram
+
+Il _Deployment diagram_ permette di rappresentare la __dislocazione fisica__ delle risorse. \
+Più precisamente, specifica la dislocazione fisica delle _istanze dei componenti_.
+
+La conformazione del diagramma è quindi molto simile a quella del diagramma dei componenti, ma con qualche differenza:
+* i __nodi__ del sistema indicano macchine fisiche;
+* i __collegamenti__ tra nodi eplicitano le modalità di comunicazione tra gli stessi (_e.g._ RMI, HTTP).
+
+![Esempio deployment diagram](/assets/11_deployment-diagram-example.png)
+
+Il Deployment diagram risulta di particolare utilità per il _deployer_, _i.e._ la figura che si occupa dell'installazione fisica del sistema. 
diff --git a/src/10_mocking/00_index.md b/src/10_mocking/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..1263ab7ac476d48430c273a79485ea957d4b4c69
--- /dev/null
+++ b/src/10_mocking/00_index.md
@@ -0,0 +1,19 @@
+# Mocking
+
+Un aspetto importante da considerare durante la scrittura dei test è la chiarezza del loro _scopo_.
+
+Chiunque li legga deve essere in grado di determinare rapidamente quale comportamento si sta testando.
+Tuttavia, questo può risultare molto difficile se i test non sono strutturati in modo ottimale. 
+L'obiettivo di un test può essere molto confusionario se, ad esempio, vengono invocati senza alcun criterio diversi comportamenti del **system under test** (SUT, ciò che _sta venendo testato_), _e.g._ alcuni per impostare lo stato pre-test (fixture) del SUT, altri per utilizzare il SUT e altri ancora per verificare lo stato post-test del SUT.
+
+Un modo per rendere evidente ciò che si sta testando è strutturare ogni test in modo che abbia quattro fasi distinte, eseguite in sequenza:  
+1. **SET UP**: si inizializza tutto il necessario affinché il SUT _esibisca il comportamento atteso_ e il test, successivamente, sia in grado di _osservare il risultato effettivo_ (ad esempio, creare i vari Test Double).
+2. **EXERCISE**: si interagisce con il SUT, facendo dunque eseguire il codice che si vuole effettivamente testare.
+3. **VERIFY**: si fa tutto il necessario per determinare se il risultato atteso è stato ottenuto o meno (_e.g._ tramite asserzioni di vario tipo).
+4. **TEARDOWN**: fase di pulizia atta a riportare l'ambiente nello stato in cui è stato trovato.
+
+Per aumentare ulteriormente la leggibilità dei nostri test è desiderabile anche fare in modo che ogni metodo di test verifichi una e una sola funzionalità.
+Ciò non significa che un metodo di test che verifica più funzionalità sia scorretto, ma fornirà sicuramente una minore localizzazione delle anomalie rispetto a un gruppo di test che testano singole  funzionalità; in altre parole, sarà meno leggibile e contraddistinto da una logica più complessa.
+
+- [**Test Double**](./01_test-double/00_index.md)
+- [**Mockito**](./01_mockito.md)
diff --git a/src/10_mocking/01_mockito.md b/src/10_mocking/01_mockito.md
new file mode 100644
index 0000000000000000000000000000000000000000..3f99aac8df0d5cfb3ec713a1978fa6dd3b2adbb3
--- /dev/null
+++ b/src/10_mocking/01_mockito.md
@@ -0,0 +1,145 @@
+# Mockito
+
+**Mockito** è un framework di testing open source per Java rilasciato sotto la licenza MIT. 
+Il framework facilita di gran lunga la creazione di mock objects e in generale di tutti i tipi di Test Double, permettendo quindi di concentrarsi sulla scrittura della logica di testing.   
+Inoltre, l'impiego di mockito aumenta notevolmente la leggibilità dei test.  
+
+## Creare Test Double
+
+Mockito mette a disposizione principalmente due metodi per creare Test Double: il metodo `mock()` e il metodo `spy()`.
+
+Il metodo `mock()` è usato per creare **Test Double** (dummy, stub o mock objects) a partire da una determinata classe o interfaccia: l'oggetto creato si presenterà con la stessa interfaccia (_metodi e firme di questi ultimi_) del tipo specificato in fase di costruzione.
+Di default, per ogni metodo dell'oggetto reale, il Test Double creato fornisce **un'implementazione minimale**.
+Questo si limiterà a restituirà dei valori di default per il tipo di ritorno del metodo oppure a non fare nulla se il metodo ritorna `void`.  
+Ad esempio, se si crea un oggetto con `mock()` a partire da una classe che ha un metodo `getValue()` che restituisce un `int`, il metodo `getValue()` del Test Double restituirà 0, che è il valore predefinito per un `int` in Java.  
+Il Test Double può essere configurato, mediante opportuna operazione di _stubbing_ anche per restituire valori specifici o lanciare eccezioni qualora vengano chiamati determinati metodi.
+
+Il metodo `spy()` viene utilizzato per creare spy objects a partire da oggetti reali.
+Quello che si ottiene è un oggetto che ha le stesse funzionalità dell'oggetto originale, ma che può essere utilizzato per fare il "tracciamento" delle chiamate ai suoi metodi e per verificare che esse vengano portate a termine come previsto.
+A differenza degli oggetti creati con il metodo `mock()`, uno spy continuerà a chiamare il metodo reale, a meno che non si specifichi il contrario.
+
+## Stubbing
+
+```java
+when(mockedObj.methodname(args)).thenXXX(values);
+```
+- args: values, matchers, argumentCaptor
+- matchers: anyInt(), argThat(is(closeTo(1.0, 0.001)))
+- thenXXX: thenReturn, thenThrows, thenAnswer, thenCallRealMethod
+
+Il metodo `when()` insieme ai vari `thenXXX()` (es `thenReturn()`, `thenThrow()`) è usato per specificare il comportamento di un Test Double (stub mock o spy obj) quando viene chiamato un suo determinato metodo.
+Si supponga, ad esempio, di avere una classe _Foo_ con un metodo `getValue()` che restituisce un `int`.
+Per fare in modo che restituisca un valore diverso dallo 0 (default per int), è possibile scrivere:
+```java
+Foo foo = mock(Foo.class);
+when(foo.getValue()).thenReturn(42);
+```
+Da questo momento in poi, il metodo `getValue()` del Test Double restituirà l'intero 42 ogni volta che verrà chiamato.  
+Ovviamente il metodo `when()` può essere usato per specificare il comportamento di qualsiasi metodo del Test Double.
+Se quindi viene scritto:
+```java
+Foo foo = mock(Foo.class);
+when(foo.someMethod()).thenThrow(new SomeException());
+```
+il Test Double lancerà un'eccezione di tipo `SomeException` quando viene chiamato il suo metodo `someMethod()`. 
+Questo permette, per esempio, di testare il comportamento del nostro codice quando viene lanciata un'eccezione senza dover implementare l'oggetto reale.
+
+Anche i metodi del tipo `doXXX()` (es. `doReturn()`, `doThrow()`, `doAnswer()`, `doNothing()`) sono usati per specificare il comportamento del Test Double quando viene chiamato un suo metodo.
+Tuttavia, a differenza del metodo `when()`, questi possono essere usati anche per specificare il comportamento di un metodo che ha come tipo di ritorno `void`; è consigliabile usarli solo in questo caso, oppure in tutti i casi in cui utilizzare il metodo `when()` risulterebbe difficile (_e.g._ metodi con tipi di ritorno non banali come `Optional`).
+Questi metodi si utilizzano come segue:
+  ```java
+doXXX(values).when(mockedObj).methodname(args)
+```
+
+## Verifying
+
+Con oggetti di tipo mock o spy si desidera spesso verificare l'occorrenza di una chiamata con certi parametri. 
+Mockito permette di farlo con il metodo `verify()`: è possibile verificare che un metodo sia stato chiamato, con quali parametri e per quante volte.
+```java
+verify(mockedclass, howMany).methodname(args)
+```
+Il parametro _howMany_ del metodo verify specifica il numero di volte che il metodo associato all'oggetto mockato deve essere chiamato durante l'esecuzione del test.
+
+Le possibili opzioni sono:
+
+-   `times(n)`: verifica che `methodname()` sia stato chiamato esattamente `n` volte;
+-   `never()`: verifica che `methodname()` non sia mai stato chiamato;
+-   `atLeastOnce()`: verifica che `methodname()` sia stato chiamato almeno una volta;
+-   `atLeast(n)`: verifica che `methodname()` sia stato chiamato almeno `n` volte;
+-   `atMost(n)`: verifica che `methodname()` sia stato chiamato al massimo `n` volte.
+
+Se si desidera verificare l'ordine delle occorrenze delle chiamate ai metodi di un oggetto, si può utilizzare il metodo `inOrder()`:
+```java
+InOrder inO = inOrder(mock1, mock2, ...)
+inO.verify...
+```
+
+È possibile anche catturare un parametro per farci delle asserzioni.
+```java
+ArgumentCaptor<Person> arg = ArgumentCaptor.forClass(Person.class);
+verify(mock).doSomething(arg.capture());
+assertEquals("John", arg.getValue().getName());
+```
+
+## Argument Matchers
+
+Quando si esegue un'operazione di stubbing oppure quando si verifica la chiamata a un metodo, al posto di specificare i valori precisi (values) si può utilizzare quello che è un **argument matcher**. \\
+Questo agisce come un segnaposto che corrisponde a qualsiasi valore corretto (_i.e._ che soddisfa la condizione di match), consentendo di specificare il comportamento senza dover conoscere il valore esatto dell'argomento che sarà passato al metodo.
+Alcuni possibili matcher sono:
+
+* `any()`, `anyInt()`, `anyString()`, etc.: questi metodi sono usati per creare degli argument matcher, che permettono di specificare che un particolare argomento del metodo può essere qualsiasi valore di un particolare tipo.
+Per esempio, si può utilizzare `anyInt()` per specificare che un argomento può essere un qualsiasi valore int.
+Questo risulta utile qualora si desideri fare lo stub di un metodo che restituisca un valore indipendentemente dagli argomenti passatigli.
+
+* `eq()`: questo metodo viene utilizzato per creare un argument matcher che corrisponde a un valore specifico.
+Per esempio, si può utilizzare `eq(42)` per specificare che un argomento deve avere il valore 42 per poter essere confrontato.
+Ciò è utile quando si vuole fare lo stub di un metodo in modo che questo restituisca un valore solo quando viene chiamato con argomenti specifici.
+
+* Il metodo `argThat()` è un modo più generale per specificare argument matchers.
+Permette di creare matcher personalizzati implementando l'interfaccia `ArgumentMatcher`.
+Questa definisce un metodo `matches()` che può essere utilizzato per determinare se un particolare argomento corrisponde al matcher.
+Tale metodologia risulta utile quando si vuole abbinare gli argomenti in maniere più complesse o articolate rispetto ai matcher di argomenti di tipo `any()`.
+
+
+## Reset a Test Double
+
+Infine, il metodo `reset()` è usato per ripristinare un Test Double al suo stato iniziale, cancellando qualsiasi metodo che era stato precedentemente ridefinito.
+È utile quando si vuole riutilizzare un Test Double in più test.
+
+
+## Esempio di testing con pattern <big>O</big>BSERVER (PULL)
+
+```java
+@Test
+void modelTest {
+    // setup
+    Model model = new Model();
+    Observer obs = mock(Observer.class);
+    Observer obs1 = mock(Observer.class);
+
+    // exercise
+    model.addObserver(obs);
+    model.addObserver(obs1);
+    model.setTemp(42.0, scale);
+
+    // verify
+    verify(obs).update(eq(model), eq("42.0"));
+    verify(obs1).update(eq(model), eq("42.0"));
+}
+```
+
+Test di un observer con un modello non generico, ma di cui si ha solo interfaccia di cui viene fornita una versione dummy:
+
+```java
+@Test
+void observerTest {
+    abstract class MockObservableIModel extends Observable implements Model {};
+    MockOBservableIModel model = mock(MockObservableIModel.class);
+    when(model.getTemp()).thenReturn(42.42);
+
+    observer.update(model, null);
+
+    verify(model).getTemp();
+    assertThat(val).isCloseTo(42.42, Offset.offset(.01));
+}
+```
diff --git a/src/10_mocking/01_test-double/00_index.md b/src/10_mocking/01_test-double/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..999de641f35b74893ac4c9297c235c2a51d693dd
--- /dev/null
+++ b/src/10_mocking/01_test-double/00_index.md
@@ -0,0 +1,180 @@
+<style>
+td, tr, table {
+   border: none!important;
+}
+.border tr {
+   border-top: 1px solid black !important;
+   border-bottom: 1px solid black !important;
+   font-size: 0.9em !important;
+}
+.border td{
+   padding: 5px !important;
+}
+.row {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  width: 100%;
+}
+
+.column {
+  display: flex;
+  flex-direction: column;
+  flex-basis: 0;
+flex-shrink: 2!important;
+  flex: 1;
+}
+.zoom img {
+  width: 95%!important;
+}
+.zoom1 img {
+  width: 60%!important;
+}
+</style>
+
+# Test Double
+
+<div class="row">
+<div class="zoom1 column" markdown="1">
+
+```plantuml
+@startuml
+
+hide footbox
+
+participant A
+activate A
+
+A -> SUT: method()
+activate SUT
+
+SUT --> A: result
+deactivate SUT
+deactivate A
+
+@enduml
+```
+
+</div>
+<div class="column zoom1" markdown="1">
+
+```plantuml
+@startuml
+
+hide footbox
+
+participant A
+activate A
+
+A -> SUT: method()
+activate SUT
+deactivate SUT
+
+A -> SUT: getStatus()
+activate SUT
+
+SUT --> A: result
+deactivate SUT
+deactivate A
+
+@enduml
+```
+
+</div>
+<div class="column zoom" markdown="1">
+
+```plantuml
+@startuml
+scale 150 height
+scale 150 width
+
+hide footbox
+
+participant A
+activate A
+
+A -> SUT: method()
+activate SUT
+
+SUT -> outputDOC: doOutput()
+activate outputDOC
+
+outputDOC --> SUT
+deactivate outputDOC
+
+SUT --> A
+deactivate SUT
+
+A -> outputDOC: getStatus()
+activate outputDOC
+
+outputDOC --> A: result
+deactivate outputDOC
+deactivate A
+
+@enduml
+```
+
+</div>
+<div class="column zoom" markdown="1" style="position: relative;">
+
+```plantuml
+@startuml
+scale 200 height
+
+hide footbox
+
+participant A
+activate A
+
+A -> SUT: method()
+activate SUT
+
+SUT -> inputDOC: getInput()
+activate inputDOC
+
+inputDOC --> SUT: data
+deactivate inputDOC
+
+SUT --> A
+deactivate SUT
+
+A -> SUT: getStatus()
+activate SUT
+
+SUT --> A: result
+deactivate SUT
+deactivate A
+
+@enduml
+```
+
+</div>
+</div>
+
+Può risultare assai difficile testare un SUT che dipende da componenti software non utilizzabili per un motivo o per l'altro.
+Questi componenti prendono il nome di **depended-on component** (DOC) e i problemi che questi possono far emergere durante la stesura di un test sono molteplici.
+Ad esempio, i DOC potrebbero non essere disponibili in quel momento, non restituire i risultati che servono a un determinato test (o restituirli solo tramite artifici troppo complessi) oppure perché la loro esecuzione avrebbe effetti collaterali indesiderati.
+In altri casi ancora, la strategia di testing adottata richiede un maggiore controllo o più visibilità sul comportamento interno del SUT e l'utilizzo di un DOC reale rende l'operazione complessa.
+
+Quando si scrive un test in cui non si può (o si sceglie di non) usare il vero componente da cui si è dipendenti, si può sostituire quest'ultimo con un Test Double, durante la fase di set up.
+
+**Test Double** è un termine generico utilizzato per indicare un qualunque oggetto con cui si sostituisce un DOC reale a scopo di test.  
+Ovviamente, a seconda del tipo di test che si sta eseguendo, si può codificare diversamente il comportamento del Test Double.
+Non è necessario che questo si comporti come il DOC reale: il suo scopo è solo quello di fornire le stesse API in modo che la sua presenza risulti essere trasparente al SUT. 
+In altre parole, per il SUT interagire con il DOC reale o con il Test Double deve essere esattamente la stessa cosa.
+L'utilizzo di Test Double rende possibile la scrittura di test che precedentemente risultavano troppo articolati, complicati o dispersivi da realizzare.
+
+Il **mocking** è la tecnica di testing che ci permette di sostituire i DOC reali con i vari Test Double.
+Effettuare mocking permette di ottenere test più efficienti, affidabili e puliti, consentendo agli sviluppatori di isolare il SUT in un ambiente più controllato.
+
+Come si può osservare dall'immagine sottostante, vi sono diversi tipi di Test Double:  
+
+![Test doubles](/assets/10_test-doubles.png)
+
+- [**Dummy objects**](./01_dummy-objects.md)
+- [**Stub objects**](./02_stub-objects.md)
+- [**Mock objects**](./03_mock-objects.md)
+- [**Spy objects**](./04_spy-objects.md)
+- [**Fake objects**](./05_fake-objects.md)
+- [**Riepilogo**](./06_riepilogo.md)
diff --git a/src/10_mocking/01_test-double/01_dummy-objects.md b/src/10_mocking/01_test-double/01_dummy-objects.md
new file mode 100644
index 0000000000000000000000000000000000000000..10e6887e8b2e24069604d95e74d3383408de6556
--- /dev/null
+++ b/src/10_mocking/01_test-double/01_dummy-objects.md
@@ -0,0 +1,61 @@
+# Dummy Objects
+
+Nella maggior parte dei test è necessario fare in modo che il SUT si trovi in uno stato opportuno prima di quella che è la fase di exercise; a tale scopo, durante la fase di set up, vengono effettuate chiamate ad alcuni suoi metodi.
+Questi possono prendere come argomenti degli oggetti che, a volte, vengono solo memorizzati in variabili d'istanza e non sono di fatto mai utilizzati nel codice testato.
+É dunque necessario crearli unicamente per conformarsi alla firma di qualche metodo che si pianifica di chiamare nella fase di set up.
+La costruzione di questi oggetti può essere non banale e aggiunge una complessità del tutto superflua al test.
+
+In questi casi, si può passare come argomento un **dummy object**.
+Questi oggetti sono una forma degenere di Test Double in quanto esistono solo per poter essere passati da un metodo all'altro e non vengono mai realmente utilizzati.
+La loro utilità risiede nell'eliminare la necessità di costruire gli oggetti reali.
+
+Si noti che un dummy object non è la stessa cosa di un _null object_.
+Un dummy object non viene utilizzato dal SUT, quindi il suo comportamento è irrilevante.
+Al contrario, un null object viene utilizzato dal SUT, ma è progettato per non fare nulla o produrre un risultato sempre "innocuo".
+
+<table>
+<tbody>
+<tr>
+<td>Senza Mockito</td>
+<td>Con Mockito</td>
+</tr>
+<tr>
+<td>
+<div markdown="1">
+
+```java
+@Test 
+public void testDummy() {
+    MyClass dummy = ??;
+
+    List<MyClass> SUT = new ArrayList<MyClass>();
+    
+    SUT.add(dummy);
+
+    assertThat(SUT.size()).isEqualTo(1);
+}
+```
+
+</div>
+</td>
+<td>
+<div markdown="1">
+
+  ```java
+  @Test 
+  public void testDummy() {
+      MyClass dummy = mock(MyClass.class);
+      
+      List<MyClass> SUT = new ArrayList<MyClass>();
+      
+      SUT.add(dummy); 
+      
+      assertThat(SUT.size()).isEqualTo(1);
+  } 
+  ```
+</div>
+
+</td>
+</tr>
+</tbody>
+</table>
diff --git a/src/10_mocking/01_test-double/02_stub-objects.md b/src/10_mocking/01_test-double/02_stub-objects.md
new file mode 100644
index 0000000000000000000000000000000000000000..42636f62014478912c2878f0e20227621d60b707
--- /dev/null
+++ b/src/10_mocking/01_test-double/02_stub-objects.md
@@ -0,0 +1,65 @@
+# Stub Objects
+
+Altre volte risulta difficile testare il SUT perché il suo comportamento dipende dai cosiddetti _input indiretti_: valori restituiti da altri componenti software (DOC) con i quali interagisce.
+Gli **input indiretti** possono essere valori di ritorno dei metodi del DOC, parametri aggiornati, errori o eccezioni sollevate dal DOC.
+
+![Stub object](/assets/10_stub-object.png)
+
+In presenza di input indiretti la verifica del comportamento del SUT richiede di sostituire i DOC reali con Test Double che immettano gli input desiderati nel SUT.
+Test Double con questo scopo prendono il nome di **stub object**: sostituiscono un componente reale, da cui dipende il SUT, e forniscono risposte (input) "preconfezionate" alle sole chiamate fatte durante il testing.
+L'utilizzo di stub consente al test di forzare la realizzazione di determinati scenari particolari o di interesse specifico.
+
+<table>
+<tbody>
+<tr>
+<td>Senza Mockito</td>
+<td>Con Mockito</td>
+</tr>
+<tr>
+<td>
+<div markdown="1">
+
+```java
+@Test
+public void testConStub() {
+    MyClass stub = ??;
+    MyList<int> SUT = new MyList<int>();
+
+    SUT.add(stub.getValue(0));  // deve ritornare 4
+    SUT.add(stub.getValue(1));  // deve ritornare 7
+    SUT.add(stub.getValue(1));  // deve ritornare 3
+        
+    res = SUT.somma();
+    
+    assertThat(res).isEqualTo(14);
+}
+```
+
+</div>
+</td>
+<td>
+<div markdown="1">
+
+```java
+@Test
+public void testConStub() {
+    MyClass stub = mock(MyClass.class);
+    when(stub.getValue(0)).thenReturn(4);
+    when(stub.getValue(1)).thenReturn(7, 3);
+    
+    MyList<int> SUT = new MyList<int>();
+    SUT.add(stub.getValue(0));  // deve ritornare 4
+    SUT.add(stub.getValue(1));  // deve ritornare 7
+    SUT.add(stub.getValue(1));  // deve ritornare 3
+        
+    res = SUT.somma();
+    
+    assertThat(res).isEqualTo(14);
+}
+```
+
+</div>
+</td>
+</tr>
+</tbody>
+</table>
diff --git a/src/10_mocking/01_test-double/03_mock-objects.md b/src/10_mocking/01_test-double/03_mock-objects.md
new file mode 100644
index 0000000000000000000000000000000000000000..7e6cf7432f0b7bfc685860bd0b4cf55083aa43c1
--- /dev/null
+++ b/src/10_mocking/01_test-double/03_mock-objects.md
@@ -0,0 +1,70 @@
+# Mock Objects
+
+Il comportamento del SUT può includere azioni che non possono essere osservate attraverso la sua API pubblica, ma che sono osservate o sperimentate da altri sistemi o componenti dell'applicazione.
+Tali attività ricadono sotto il nome di **output indiretti** del SUT.
+Gli output indiretti possono includere chiamate a metodi di un altro componente, record inseriti in un database, record scritti su un file _etc_.
+
+![Mock object](/assets/10_mock-object.png)
+
+Testare il comportamento del SUT può voler dire anche verificare che gli output indiretti siano quelli corretti e a tale scopo servono punti di osservazione appropriati.
+Un **punto di osservazione** è un modo con cui il test può ispezionare lo stato post-exercise del SUT. 
+I punti di osservazione utili a verificare gli output indiretti sono costituiti da Test Double che prendono il nome di **mock object**.
+Questi intercettano gli output indiretti del SUT nella fase di exercise e permettono di confrontarli con gli output attesi in un momento successivo (_i.e._ la fase di verifying).
+
+Un mock object è dunque utilizzato per instrumentare e controllare le chiamate fatte dal SUT.
+In genere, l'oggetto Mock include anche la funzionalità di uno Stub; deve infatti essere in grado di restituire valori al SUT, anche se l'enfasi è posta sulla _verifica_ delle chiamate effettuate e non dal loro risultato.
+
+<table>
+<tbody>
+<tr>
+<td>Senza Mockito</td>
+<td>Con Mockito</td>
+</tr>
+<tr>
+<td>
+<div markdown="1">
+
+```java
+@Test
+public void testConMock() {
+    MyClass mock = ??;
+    
+    MyList<int> SUT = new MyList<int>();
+    
+    res = SUT.somma(mock);
+    
+    assertThat(res).isEqualTo(14);
+    // assert che getValue è stata chiamata 3 volte
+    // prima una volta con parametro 0 e poi...
+}
+```
+
+</div>
+</td>
+<td>
+<div markdown="1">
+
+```java
+@Test 
+public void testConMock() {
+     MyClass mock = mock(MyClass.class);
+     
+     when(mock.getValue(0)).thenReturn(4); 
+     when(mock.getValue(1)).thenReturn(7,3);
+
+     MyList<int> SUT = new MyList<int>();
+
+     res = SUT.somma(mock);
+     
+     assertThat(res).isEqualTo(14); 
+     InOrder io = inOrder(mock); 
+     io.verify(mock).getValue(0); 
+     io.verify(mock, times(2)).getValue(1);
+} 
+```
+
+</div>
+</td>
+</tr>
+</tbody>
+</table>
diff --git a/src/10_mocking/01_test-double/04_spy-objects.md b/src/10_mocking/01_test-double/04_spy-objects.md
new file mode 100644
index 0000000000000000000000000000000000000000..ce5c1b6403bbc3fe83884597eddc7ca427570f01
--- /dev/null
+++ b/src/10_mocking/01_test-double/04_spy-objects.md
@@ -0,0 +1,59 @@
+# Spy objects
+
+![Spy object](/assets/10_spy-object.png)
+
+Un altro modo per implementare punti di osservazione che controllino e instrumentino le chiamate effettuate dal SUT su determinati DOC sono gli **spy object**.
+A differenza dei mock, questi sono costruiti a partire da oggetti reali.  
+Successivamente alla fase d'interazione con il SUT (exercise), durante la fase di verifica dei risultati (verify), il test confronta le chiamate effettuate dal SUT sul Test Spy con il comportamento desiderato (expected).
+
+<table>
+<tbody>
+<tr>
+<td>Senza Mockito</td>
+<td>Con Mockito</td>
+</tr>
+<tr>
+<td>
+<div markdown="1">
+
+```java
+@Test
+public void testConSpy() {
+        MyClass spy = ??;
+
+        MyList<int> SUT = new MyList<int>();
+
+        res = SUT.somma(spy);
+
+        assertThat(set).isEqualTo(14);
+        // assert che getValue è stata chiamata 3 volte
+        // prima una volta con parametro 0 e poi...    
+        }
+```
+
+</div>
+</td>
+<td>
+<div markdown="1">
+
+```java
+@Test 
+public void testConSpy() {
+     MyClass spy = spy(new MyClass());
+
+     MyList<int> SUT = new MyList<int>();
+
+     res = SUT.somma(spy);
+     
+     assertThat(res).isEqualTo(14); 
+     InOrder io = inOrder(spy); 
+     io.verify(spy).getValue(0); 
+     io.verify(spy, times(2)).getValue(1);
+} 
+```
+
+</div>
+</td>
+</tr>
+</tbody>
+</table>
diff --git a/src/10_mocking/01_test-double/05_fake-objects.md b/src/10_mocking/01_test-double/05_fake-objects.md
new file mode 100644
index 0000000000000000000000000000000000000000..728bafb60421e0b680278a778fee83596a828e09
--- /dev/null
+++ b/src/10_mocking/01_test-double/05_fake-objects.md
@@ -0,0 +1,5 @@
+# Fake Objects
+
+![Fake object](/assets/10_fake-object.png)
+
+Un **fake object** è un oggetto reale che implementa a tutti gli effetti le funzionalità del DOC, ma per farlo impiega una qualche "scorciatoia" in una maniera che non risulterebbe applicabile ad un contesto di produzione _e.g._ database in memoria invece di un database reale, soluzioni inefficienti, parti di codice open source utilizzabili solo in fase di testing.
diff --git a/src/10_mocking/01_test-double/06_riepilogo.md b/src/10_mocking/01_test-double/06_riepilogo.md
new file mode 100644
index 0000000000000000000000000000000000000000..d08dc85e8978fdbc23aa2b950b3e6b326bb1b152
--- /dev/null
+++ b/src/10_mocking/01_test-double/06_riepilogo.md
@@ -0,0 +1,54 @@
+# Riepilogo
+
+La tabella sottostante fornisce un riepilogo di ciò che rappresenta ciascuna variante dei Test Double.
+
+<table class="border">
+    <tr>
+        <td style="width: 15%!important"><b>Test Double</b></td>
+        <td style="width: 20%!important"><b>Purpose</b></td>
+        <td><b>Has behavior?</b></td>
+        <td style="width: 20%!important"><b>Injects Indirect <br>Inputs into SUT</b></td>
+        <td style="width: 20%!important"><b>Handles Indirect <br>Outputs of SUT</b></td>
+        <td style="width: 15%!important"><b>Values Provided <br>by Test(er)</b></td>
+    </tr>
+    <tr>
+        <td>Dummy Object</td>
+        <td>Utilizzato come segnaposto quando è necessario passare un argomento a un metodo</td>
+        <td>NO</td>
+        <td>NO, mai usato</td>
+        <td>NO, mai usato</td>
+        <td>Nessuno</td>
+    </tr>
+    <tr>
+        <td>Stub Object</td>
+        <td>Fornisce risposte preconfezionate alle sole chiamate fatte durante il testing</td>
+        <td>SI</td>
+        <td>SI</td>
+        <td>NO, li ignora</td>
+        <td>Input indiretti per il SUT</td>
+    </tr>
+    <tr>
+        <td>Mock Object</td>
+        <td>Instrumentare e controllare le chiamate</td>
+        <td>SI</td>
+        <td>Opzionale</td>
+        <td>Verifica la correttezza rispetto alle aspettative.</td>
+        <td>Input indiretti per il SUT (opzionali) e output indiretti attesi dal SUT</td>
+    </tr>
+    <tr>
+        <td>Spy Object</td>
+        <td>Instrumentare e controllare le chiamate ad oggetti reali</td>
+        <td>SI</td>
+        <td>Opzionale</td>
+        <td>Li cattura per una verifica successiva</td>
+        <td>Input indiretti per il SUT (opzionali)</td>
+    </tr>
+    <tr>
+        <td>Fake Object</td>
+        <td>Permette di eseguire test che altrimenti sarebbero impossibili o avrebbero effetti collaterali indesiderati (es test molto lenti)</td>
+        <td>SI</td>
+        <td>NO</td>
+        <td>Li utilizza</td>
+        <td>Nessuno</td>
+    </tr>
+</table>
\ No newline at end of file
diff --git a/src/11_verifica-convalida/00_index.md b/src/11_verifica-convalida/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..a16cae44b079dc024490b51e859e9ca934ea760d
--- /dev/null
+++ b/src/11_verifica-convalida/00_index.md
@@ -0,0 +1,4 @@
+# Verifica e convalida
+
+- [**Terminologia**](./01_terminologia.md)
+- [**Tecniche**](./02_tecniche.md)
diff --git a/src/11_verifica-convalida/01_terminologia.md b/src/11_verifica-convalida/01_terminologia.md
new file mode 100644
index 0000000000000000000000000000000000000000..419d1df8cbc157228b83e9780046ff66323c2d99
--- /dev/null
+++ b/src/11_verifica-convalida/01_terminologia.md
@@ -0,0 +1,114 @@
+# Terminologia
+
+## Verifica e convalida
+
+Verifica e convalida sono due termini con un significato apparentemente molto simile ma che celano in realtà una differenza non banale tra loro:
+- per _verifica (della correttezza)_ si intende l'attività di confronto del software con le __specifiche__ (formali) prodotte dall'analista;
+- per _convalida (dell'affidabilità)_ si intende l'attività di confronto del software con i __requisiti__ (informali) posti dal committente.
+
+Ci sono quindi due punti critici che vanno a sottolineare maggiormente questa differenza:
+- requisiti e specifiche sono spesso __formulati diversamente__.
+Solitamente i _requisiti_, essendo scritti dal committente, sono formulati in un linguaggio più vicino al dominio di quest'ultimo.
+Diversamente, le _specifiche_ sono scritte in un linguaggio più vicino al dominio dello sviluppatore, spesso in maniera formale e poco ambigua;
+- è facile che i requisiti __cambino__ in corso d'opera mentre le specifiche rimangano congelate; questo aspetto dipende molto dai contratti tra committente e il team di sviluppo.
+
+La definizione dei _requisiti_ forniti dal cliente è immediata ma informale: scrivere dei test che li _convalidano_ può risultare molto complicato.
+Invece, è più semplice validare le _specifiche_ attraverso test in quanto sono scritte dal team di sviluppo e sono quindi più formali e complete.
+
+Ad ogni modo, nelle attività di verifica e convalida si cercano degli __errori__, ma la parola "errore" stessa può assumere molti significati a seconda del contesto.
+È quindi importante capire di quale _errore_ si sta parlando, introducendo termini diversi, come _malfunzionamento_ e _difetto_.
+
+N.B: Esistono dei glossari e vocabolari di terminologia comune redatti dalla IEEE, ad esempio [Systems and software engineering —
+Vocabulary](/assets/12_ieee-vocabulary.pdf) che possono essere addottati dagli sviluppatori come standard in modo da snellire la comunicazione tra di loro.
+
+## Malfunzionamento (guasto/failure)
+
+Un malfunzionamento è uno __scostamento__ dal corretto funzionamento del programma.
+
+__Non dipende dal codice__ e in generale non ci si accorge di esso osservando il codice ma solo da un punto di vista più esterno, utilizzando il programma.
+Il malfunzionamento potrebbe riguardare sia le specifiche (quindi relativo alla fase di _verifica_) che i requisiti (fase di _convalida_, ovvero "non rispetta le aspettative").
+Secondo il vocabolario citato in precedenza:
+
+> __failure:__ 
+> 1. termination of the ability of a product to perform a required function or its inability to perform within previously specified limits. \
+> _ISO/IEC 25000:2005, Software Engineering — Software product Quality Requirements and Evaluation (SQuaRE) — Guide to SQuaRE.4.20._
+> 2. an event in which a system or system component does not perform a required function within specified limits.
+> 
+>_NOTE: A failure may be produced when a fault is encountered_
+
+### Esempio
+
+Di seguito è illustrato un esempio di malfunzionamento.
+```java
+static int raddoppia(int par) {
+    int risultato;
+    risultato = (par * par);
+    return risultato;
+}
+
+static void main(String[] args) {
+    int risultato = raddoppia(3);
+    System.out.println(risultato);  // 9
+}
+```
+La funzione dovrebbe ritornare il doppio del numero in ingresso, ma se passiamo 3 in argomento verrà ritornato 9.
+
+## Difetto (anomalia/fault)
+Un difetto è il __punto del codice__ che causa il verificarsi del malfunzionamento.
+
+È __condizione necessaria__ (ma non sufficiente) per la verifica di un malfunzionamento.
+
+> __fault:__
+> 1. a manifestation of an error in software.
+> 2. an incorrect step, process, or data definition in a computer
+program. 
+> 3. a defect in a hardware device or component. Syn: bug
+> 
+> _NOTE: A fault, if encountered, may cause a failure._
+
+Nell'esempio di codice precedente, il difetto è in `risulato = (par * par)`.
+
+Il _difetto_ è condizione _non sufficiente_ per il verificarsi di un _malfunzionamento_: ad esempio, __non si verificano malfunzionamenti__ in caso l'argomento passato sia 0 oppure 2.
+Il raddoppio in quei casi avverrebbe in maniera corretta. 
+
+Un altro esempio di tale è proprietà è il caso in cui esistono _"più anomalie che si compensano"_: se si sta utilizzando una libreria per operazioni su temperature in gradi Fahrenheit, ponendo il caso che stia partendo da gradi Celsius, dovrà essere effettuata una conversione.
+Se in questa conversione è presente un'anomalia che però si riflette allo stesso modo in fase di riconversione per restituire il risultato, le due anomalie combinate non si manifestano in un malfunzionamento.
+
+Spesso le anomalie si annidano nella gestione di casi particolari o eccezionali del programma in quanto il flusso di controllo ordinario è solitamente il più testato.
+
+## Sbaglio (mistake)
+
+Uno sbaglio è la causa di un’anomalia. 
+Si tratta in genere di un errore umano.
+
+>__mistake:__
+>1. a human action that produces an incorrect result
+> 
+>_NOTE: The fault tolerance discipline distinguishes between a human action (a mistake), its manifestation (a hardware
+or software fault), the result of the fault (a failure), and the amount by which the result is incorrect (the error)._
+
+
+Relativamente all'esempio precedente, possibili sbagli possono essere:
+- __errori di battitura__ (scrivere `*` invece di `+`);
+- __concettuali__ (non sapere cosa vuol dire _raddoppiare_);
+- relativi alla __padronanza del linguaggio__ (credere che `*` sia il simbolo dell’addizione).
+
+È importante capire quale sia la _causa_ di uno sbaglio in modo da poter intraprendere azioni correttive per il futuro (_es. studiare meglio la sintassi del linguaggio_).
+
+### Esempio notevole: _il caso Ariane 5_
+
+<iframe width="560" height="315" src="https://www.youtube.com/embed/PK_yguLapgA?start=67" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+
+[Wikipedia: Ariane 5 notable launches](https://en.wikipedia.org/wiki/Ariane_5#Notable_launches 'Ariane 5 - Notable Launches')
+
+Il 4 giugno 1996 il primo volo di prova del razzo Ariane 5 è fallito a causa di un problema al software di controllo che ha portato all'autodistruzione del missile.
+
+Il _malfunziamento_ è palese: il razzo è esploso e chiaramente non era il comportamento richiesto.
+
+Qual era l'_anomalia_? Il malfunziamento si è verificato per una eccezione di overflow, sollevatosi durante una conversione da un 64 bit float a un 16 bit signed int che indicava il valore della velocità orizzontale. 
+Questo ha bloccato sia l'unità principale che il backup dell'unità stessa.
+
+Lo _sbaglio_? Tale eccezione non veniva gestita perché questa parte del software era stata ereditata da Ariane 4, modello di razzo antecedente a Ariane 5, la cui traiettora faceva sì che non si raggiungessero mai velocità orizzontali non rappresentabili con int 16 bit. 
+La variabile incriminata non veniva protetta per gli _"ampi margini di sicurezza"_ (a posteriori, non così ampi).
+
+Il comportamento della variabile non era mai stato analizzato con i dati relativi alla traiettoria da seguire.
diff --git a/src/11_verifica-convalida/02_tecniche.md b/src/11_verifica-convalida/02_tecniche.md
new file mode 100644
index 0000000000000000000000000000000000000000..3c43b6e0b29ad50b594e8b811e769400c90263a4
--- /dev/null
+++ b/src/11_verifica-convalida/02_tecniche.md
@@ -0,0 +1,87 @@
+# Tecniche
+
+## Classificazione delle tecniche
+
+Nell'ambito della _verifica e convalida_ è possibile classificare le tecniche di analisi in due categorie:
+- __tecniche statiche__, basate sull'analisi degli elementi sintattici del codice. \
+Ad esempio: metodi formali, analisi del dataflow e modelli statistici;
+- __tecniche dinamiche__, basate sull'esecuzione del programma. \
+Ad esempio: testing e debugging.
+
+In generale, __è più facile determinare tecniche dinamiche__ rispetto alle tecniche statiche.
+Per contro, una volta ideate e a patto di avere dimensioni del codice ragionevoli e costrutti sintattici non troppo complessi le __tecniche statiche sono più veloci__ nell'analizzare il codice e, soprattutto, più complete dato che le tecniche dinamiche lavorano sui possibili stati del programma - che possono essere infiniti.
+
+Ovviamente diverse metodologie di verifica e convalida avranno i rispettivi pro e contro.
+Come si possono dunque confrontare queste tecniche?
+
+![Classificazione tecniche verifica e convalida](/assets/12_classificazione-tecniche-di-verifica-e-convalida.jpg)
+
+Nell'immagine sopra è possibile osservare una _piramide immaginaria a 3 dimensioni_ che riassume dove si posizionano le tecniche di verifica e convalida relativamente le une con le altre.
+La cima della piramide rappresenta il __punto ideale__ a cui tendere, nel quale è possibile affermare di esser riusciti a verificare perfettamente una proprietà arbitraria attraverso una prova logica (dal lato statico) o una ricerca esaustiva su tutti gli stati del problema (dal lato dinamico).
+
+Tale punto ideale è __praticamente impossibile__ da raggiungere per la stragrande maggioranza dei problemi che siamo interessati a risolvere. 
+Bisogna scegliere da quale versante iniziare la scalata della piramide: __lato verde__ (approccio statico) o __lato blu__ (approccio dinamico)?
+
+Più ci si posiziona verso il basso, più si degenera in:
+<ul>
+<li>
+
+__estrema semplificazione delle proprietà__ (in basso a sinistra): si stanno in qualche modo _rilassando_ eccessivamente gli obiettivi che si vogliono raggiungere.
+
+Ad esempio, se si vuole dimostrare che si sta usando un puntatore in maniera corretta e nel farlo si sta semplicemente controllando che non valga `null`, è _cambiata_ la proprietà che si vuole come obiettivo (controllare che un puntatore non valga `null` __non significa che__ lo si stia usando nel modo corretto);
+</li>
+<li>
+
+__<span id="innaccuratezza_pessimistica">estrema inaccuratezza pessimistica</span>__ (in basso al centro): è dovuta all'approccio pessimistico che ha come mantra:
+> _"Se non riesco a dimostrare l'assenza di un problema assumo che il problema sia presente"_
+
+Ad esempio, si manifesta nei compilatori quando non riescono a dimostrare che una determinata funzione che deve ritornare un valore ritorni effettivamente un valore per tutti i possibili cammini `if` / `else if` / eccetera. La mancanza di capacità nel dimostrare l'assenza di un problema non ne implica la presenza di uno.
+</li>
+<li>
+
+__estrema <span id="innaccurettezza_ottimistica">inaccuratezza ottimistica</span>__ (in basso a destra): è dovuta all'approccio ottimistico che ha come mantra:
+> _"Se non riesco a dimostrare la presenza di un problema assumo che questo non sia presente"_
+
+È una possibile deriva degli approcci legati al testing: con esso si cercano malfunzionamenti, se a seguito dei test non ne vengono trovati allora si assume che il programma funzioni correttamente.
+</li>
+</ul>
+
+A metà strada tra questi estremi inferiori e l'estremo superiore ideale si posizionano quindi le varie tecniche di verifica e convalida, ciascuna più o meno legata ai tra approcci sopra descritti.
+Tra queste evidenziamo le dimostrazioni con metodi formali, il testing e il debugging.
+
+## Metodi formali
+
+L'approccio dei metodi formali tenta di dimostrare l'_assenza_ di anomalie nel prodotto finale. \
+Si possono utilizzare diverse tecniche (spiegate nelle lezioni successive), come:
+- analisi di dataflow;
+- dimostrazione di correttezza delle specifiche logiche.
+
+Questo approccio segue la linea dell'_<a href="#innaccuratezza_pessimistica">inaccuratezza pessimistica</a>_.
+
+## Testing
+
+Il testing è l'insieme delle tecniche che si prefiggono di rilevare __malfunzionamenti__. \
+Attraverso il testing non si può dimostrare la correttezza ma solo aumentare la _fiducia_ dei clienti rispetto all'affidabilità del prodotto.
+
+Le tecniche di testing possono essere molto varie e si raggruppano in:
+- __white box__: si ha accesso al codice da testare e si possono cercare anomalie guardandolo da un punto di vista interno;
+- __black box__: non si ha accesso al codice ma è possibile testare e cercare malfunzionamenti tramite le interfacce esterne;
+- __gray box__: non si ha accesso al codice ma si ha solo un'idea dell'implementazione ad alto livello. \
+Per esempio, se sappiamo che il sistema segue il pattern <big>M</big>ODEL <big>V</big>IEW <big>C</big>ONTROLLER ci si può aspettare che certe stimolazioni portino a chiamate al database mentre altre no.
+
+Come è chiaro, questo approccio segue una logica di _<a href="#innaccuretezza_ottimistica">inaccuratezza ottimistica</a>_.
+
+È inoltre interessante notare che il Test Driven Development (TDD) adotta una filosofia di testing completamente black box: imponendo che venga scritto prima il test del codice questo non può assumere niente sul funzionamento interno dell'oggetto di testing.
+
+## Debugging
+
+Dato un _programma_ e un _malfunzionamento noto e riproducibile_, il debugging permette di localizzare le __anomalie__ che causano i malfunzionamenti.
+A differenza del testing, infatti, è richiesta la conoscenza a priori di un malfunzionamento prima di procedere con il debugging.
+
+Molto spesso viene usato il debugging al posto del __testing__, almeno a livello di terminologia: questo è un problema perché il debugging non è fatto per la "grande esecuzione" ma al contrario per esaminare in maniera granulare (a volte anche passo passo per istruzioni macchina) una determinata sezione di codice in esecuzione con lo scopo di trovare l'anomalia che provoca un malfunzionamento.
+Se si usassero le tecniche di debugging per effettuare il testing il tempo speso sarebbe enorme: il debugging osserva infatti _stati interni_ dell'esecuzione e per rilevare un malfunzionamento in questo modo sarebbe necessario osservare tutti i possibili - e potenzialmente infiniti - stati del programma.
+
+Due possibili approcci al debugging sono:
+- partendo da una malfunzionamento _noto_ e _riproducibile_ si avvia una procedura di analisi basata sulla __produzione degli stati intermedi__ dell'esecuzione del programma: _passo passo_ (a livello a piacere, da istruzione macchina a chiamata di funzione) si controllano tutti gli stati di memoria alla ricerca di uno inconsistente;
+- ___divide-et-impera___: il codice viene smontato sezione per sezione e componente per componente in modo da poter trovare il punto in cui c'è l'anomalia. 
+Si possono mettere breakpoint o _"print tattiche"_.
diff --git a/src/12_testing/00_index.md b/src/12_testing/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..9966e0e4e9d5c0c0ab1ca3e530fa8e2d5ef176bf
--- /dev/null
+++ b/src/12_testing/00_index.md
@@ -0,0 +1,8 @@
+# Testing
+
+- [**Definizioni**](./01_definizioni.md)
+- [**Proprietà**](./02_proprieta.md)
+- [**Utilità di un test**](./03_utilita.md)
+- [**Criteri di selezione**](./04_criteri-noti/00_index.md)
+- [**Altri criteri**](./05_altri-criteri/00_index.md)
+- [**Mappa finale implicazioni tra criteri di copertura**](./06_mappa.md)
diff --git a/src/12_testing/01_definizioni.md b/src/12_testing/01_definizioni.md
new file mode 100644
index 0000000000000000000000000000000000000000..e28b03e47d90f998d6e1491364d411f7881c18ce
--- /dev/null
+++ b/src/12_testing/01_definizioni.md
@@ -0,0 +1,88 @@
+# Definizioni
+
+## Correttezza
+
+La maggior parte dei problemi che si verificano durante lo sviluppo di un progetto sono causati da _problemi di comunicazione_.
+Ci possono essere incomprensioni quando le informazioni passano da una figura all'altra, come quando ci si interfaccia tra cliente, analista e programmatore.
+Il programmatore dovrà adattare il proprio linguaggio per farsi comprendere dal cliente prestando maggiore attenzione alla formalità e alla chiarezza della comunicazione con il passare del tempo.
+Più i concetti sono spiegati chiaramente, più è difficile incorrere in problemi successivi: è quindi necessario fare attenzione alla __terminologia__ utilizzata.
+
+Partiamo quindi dalle basi: quando un programma si definisce ___corretto___?
+
+Considerando un generico programma \\(P\\) come una funzione da un insieme di dati \\(D\\) (dominio) a un insieme di dati \\(R\\) (codominio) allora:
+
+- \\(P(d)\\) indica l'__esecuzione__ di \\(P\\) su un certo input \\(d \in D\\),
+- il risultato \\(P(d)\\) è __corretto__ se soddisfa le specifiche, altrimenti è scorretto,
+- \\(\operatorname{ok}(P, \\, d)\\) indica la __correttezza__ di \\(P\\) per il dato \\(d\\)
+
+quindi
+
+$$
+\boxed{P \text{ è } \textit{corretto} \Longleftrightarrow \forall d \in D \\:, \text{ } \operatorname{ok}(P, \\, d)}
+$$
+
+A parole, _un programma __è corretto__ quando __per ogni dato__ del dominio vale \\(\operatorname{ok}(P, \\, d)\\)_.
+
+Per indicare la correttezza di programma \\(P\\) si utilizza la notazione \\(\operatorname{ok}(P, \\, D)\\), che appunto indica che \\(P\\) è _corretto_ per qualunque \\(d \in D\\).
+
+## Definizione di test
+
+Durante l'attività di testing ciò che viene fatto è sottoporre il programma a una serie di stimolazioni per saggiarne il comportamento in tali circostanze.
+Eseguire un test vuole quindi dire eseguire il programma con una serie di input appartenenti al suo dominio e confrontare i risultati ottenuti con il risultato atteso secondo le specifiche.
+
+Volendone dare una definizione più rigorosa, _un __test__ è un sottoinsieme del dominio dei dati_ e _un singolo __caso di test__ è un elemento di esso_.
+Un test sono quindi __più stimolazioni__, mentre un caso di test è una __singola stimolazione__. \
+Matematicamente:
+
+- un test \\(T\\) per un programma \\(P\\) è un sottoinsieme del suo dominio \\(D\\);
+- un elemento \\(t\\) di un test \\(T\\) è detto _caso di test_;
+- l'esecuzione di un test consiste nell'esecuzione del programma \\(\forall t \in T \subseteq D\\).
+
+Un programma \\(P\\) supera (o _passa_) un test \\(T\\) se:
+
+$$
+\operatorname{ok}(P, \\, T) \Longleftrightarrow \forall t \in T \\:, \text{ } \operatorname{ok}(P, \\, t)
+$$
+
+Quindi, _un programma è __corretto per un test__ quando __per ogni caso di test__ esso è __corretto___.
+
+Lo scopo dei test è però ricercare comportamenti anomali nel programma per permetterci di correggerli.
+Diciamo quindi che _un test \\(\\, T\\) ha __successo__ se rileva uno o più malfunzionamenti presenti nel programma \\(P\\)_:
+
+$$
+\operatorname{successo}(T, \\, P) \Longleftrightarrow \exists t \in T \\: | \\: \lnot \operatorname{ok}(P, \\, t)
+$$
+
+## Test ideale
+
+Se un test non rileva alcun malfunzionamento __non significa che il programma sia corretto__: come visto nella lezione precedente, il test è un'attività ottimistica e normalmente il passaggio di un test non garantisce l'assenza di anomalie.
+Questo smette però di essere vero nel caso di _test ideali_.
+
+_Un test \\(T\\) si definisce __ideale__ per \\(P\\) se e solo se_
+
+$$\operatorname{ok}(P, \\, T) \Rightarrow \operatorname{ok}(P, \\, D)$$
+
+_ovvero se il superamento del test __implica la correttezza del programma___.
+
+Purtroppo però in generale è ___impossibile_ trovare un test ideale__, come ci suggerisce la seguente ipotesi universalmente accettata:
+
+> __Tesi di Dijkstra__:
+>
+> _Il test di un programma può rilevare la presenza di malfunzionamenti ma non dimostrarne l'assenza._
+>
+> _Non esiste quindi un algoritmo che dato un programma arbitrario \\(P\\) generi un test ideale __finito__ \
+> (il caso \\(T = D\\) non va considerato)._
+
+Notiamo come la tesi escluda esplicitamente il _test esaustivo_ \\(T = D\\), restringendosi a considerare i test finiti (mentre il dominio \\(D\\) potrebbe anche essere infinito).
+Per capire il perché di questa distinzione è sufficiente osservare il seguente esempio:
+
+```java
+static int sum(int a, int b) {
+    return a + b;
+}
+```
+
+In Java un int è espresso su 32 bit, quindi il dominio di questa semplice funzione somma ha cardinalità \\(2^{32} \cdot 2^{32} = 2^{64} \sim 2 \cdot 10^{19}\\).
+Considerando quindi un tempo di esecuzione ottimistico di 1 nanosecondo per ogni caso di test, un test esaustivo che provi tutte le possibili combinazioni di interi impiegherebbe più di 600 anni per essere eseguito per intero.
+
+_Il __test esaustivo__ è quindi __impraticabile__._
diff --git a/src/12_testing/02_proprieta.md b/src/12_testing/02_proprieta.md
new file mode 100644
index 0000000000000000000000000000000000000000..5acfe0ed867b08f8cdd1b0450e214841773a86f8
--- /dev/null
+++ b/src/12_testing/02_proprieta.md
@@ -0,0 +1,72 @@
+# Proprietà
+
+## Affidabilità
+
+_Un criterio di selezione \\(C\\) si dice __affidabile__ se presi due test \\(T_1\\) e \\(T_2\\) in base al criterio allora o entrambi hanno successo o nessuno dei due ha successo_.
+
+$$
+\boxed{
+    \operatorname{affidabile}(C, \\, P) \Longleftrightarrow \left (
+        \forall T_1 \in C, \\, \forall T_2 \in C \\:, \text{ } \operatorname{successo}(T_1, \\, P) \Leftrightarrow \operatorname{successo}(T_2, \\, P)
+    \right )
+}
+$$
+
+## Validità
+
+_Un criterio di selezione si dice __valido___ _se qualora \\(P\\) non sia corretto, allora esiste almeno un test \\(T\\) selezionato in base al criterio \\(C\\) che ha successo e quindi rileva uno o più malfunzionamenti per il programma \\(P\\):_
+
+$$
+\boxed{
+    \operatorname{valido}(C, \\, P) \Longleftrightarrow \left (
+        \lnot \operatorname{ok}(P, \\, D) \Rightarrow \exists T \in C \\: | \operatorname{successo}(T,\,P)
+    \right )
+}
+$$
+
+## Esempio
+
+Si consideri il seguente codice.
+
+```java
+static int raddoppia(int par) {
+    int risultato;
+    risultato = (par * par);
+    return risultato;
+}
+```
+
+Un criterio che seleziona:
+
+- _"i sottoinsiemi di \\(\{0, \\, 2\}\\)”_ è __affidabile__, perché il programma funziona sia con \\(0\\) sia con \\(2\\), ma __non valido__, perché sappiamo che il programma non è corretto e non esiste un test che trovi malfunzionamenti;
+- _"i sottoinsiemi di \\(\{0, \\, 1, \\, 2, \\, 3, \\, 4\}\\)”_ è __non affidabile__, perché i risultati dei casi di test non sono tutti coerenti (e quindi il test \\(T1=\{0,1\}\\) non ha successo mentre \\(T2=\{0, 3\}\\) sì), ma __valido__ perché esiste un test che rileva i malfunzionamenti.
+- _"i sottoinsieme finiti di \\(D\\) con almeno un valore maggiore di \\(18\\)”_ è __affidabile__, perché i risultati dei casi di test sono tutti coerenti, e __valido__ perché rileva i malfunzionamenti.
+
+In questo caso la ricerca di un criterio valido e affidabile era semplice perché conoscevamo già l'anomalia.
+Tuttavia, lo stesso non si può dire di un qualunque programma \\(P\\) in quanto __non si conoscono i malfunzionamenti a priori__ e dunque è molto più difficile trovare criteri validi e affidabili.
+
+## Conclusione
+
+L'obiettivo sarebbe quindi quello di trovare un _criterio valido e affidabile_ sempre.
+Tuttavia ciò è purtroppo impossibile in quanto un criterio di questo tipo selezionerebbe test ideali, che sappiamo non esistere.
+
+Immaginiamo infatti di avere un _criterio valido e affidabile_ e che un test selezionato da esso __non abbia successo__.
+Sapendo che:
+
+- non avendo successo allora non sono stati trovati errori,
+- essendo il criterio affidabile allora tutti gli altri test selezionati da quel criterio non troveranno errori,
+- essendo il criterio valido allora se ci fosse stato un errore almeno uno dei test lo avrebbe trovato
+
+allora il programma è __corretto__, ovvero abbiamo trovato un test che quando non ha successo implica la correttezza del programma: in poche parole, un _test ideale_.
+Esiste quindi un altro modo per implicare la correttezza di un programma:
+
+$$
+\boxed{
+    \operatorname{affidabile}(C, \\, P) \land \operatorname{valido}(C, \\, P) \land T \in C \land \lnot\operatorname{successo}(T, \\, P)
+    \Longrightarrow
+    \operatorname{ok}(P, \\, D)
+}
+$$
+
+In conclusione, trovare un criterio che sia __contemporaneamente__ affidabile e valido significherebbe trovare un criterio che selezioni __test ideali__ che sappiamo non esistere per la _tesi di Dijkstra_.
+Dovremo dunque accontentarci di criteri che garantiscano solo una delle due caratteristiche.
diff --git a/src/12_testing/03_utilita.md b/src/12_testing/03_utilita.md
new file mode 100644
index 0000000000000000000000000000000000000000..4bc95faf2abcca71f3f62b41c2bc93f2e757bd28
--- /dev/null
+++ b/src/12_testing/03_utilita.md
@@ -0,0 +1,16 @@
+# Utilità di un test
+
+Abbandonata la vana speranza di un criterio di selezione universalmente valido che permetta di testare alla perfezione qualunque programma vediamo ora cosa significa _utilizzare_ un criterio di selezione per costruire un test.
+Come sappiamo un test altro non è che un insieme di casi di test, specifici input appartenenti al dominio del programma: un criterio di selezione governa dunque quanti e quali casi di test vengono aggiunti al test che si sta costruendo.
+
+Possiamo quindi ora farci una domanda: quali sono le __caratteristiche__ che __rendono utile__ un caso di test, ovvero che rendono "possibile" o "probabile" che il caso di test evidenzi un malfunzionamento causato da un'anomalia?
+Ebbene, un caso di test utile deve:
+
+- __eseguire il comando che contiene l'anomalia__ – non è altrimenti possibile che il malfunzionamento si manifesti;
+- l'esecuzione del comando che contiene l'anomalia deve portare il sistema in uno
+__stato scorretto__, o per meglio dire __inconsistente__;
+- lo stato inconsistente deve propagarsi fino all'uscita del codice in esame in modo da __produrre un output diverso da quello atteso__;
+
+Un buon criterio di selezione dovrà quindi selezionare test contenenti casi di test utili: ma quanti dovrebbe contenerne?
+Per capire ciò si può utilizzare un metro di misura legato alle caratteristiche del codice: a ogni criterio è infatti possibile associare una __metrica__ che misuri la __copertura__ del test che si sta costruendo e che ci permetta di decidere _quando smettere di aggiungere casi di test_, _quali casi di test è opportuno aggiungere_ e di _confrontare la bontà di test diversi_.
+Aggiungeremo infatti solo casi di test che permettano di aumentare la metrica di copertura, e test in grado di garantire una copertura maggiore saranno inerentemente migliori di test con una copertura minore.
diff --git a/src/12_testing/04_criteri-noti/00_index.md b/src/12_testing/04_criteri-noti/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..9c0dbceeb84a530b659353d170ad13475b54169e
--- /dev/null
+++ b/src/12_testing/04_criteri-noti/00_index.md
@@ -0,0 +1,15 @@
+# Criteri di selezione
+
+Assodato che un test ideale è impossibile da realizzare, come possiamo scegliere un _sottoinsieme del dominio_ che approssimi il più possibile un _test ideale_? \
+Esistono una serie di __criteri di selezione__ che hanno proprio lo scopo di guidare la selezione dei casi di test all'interno del dominio in modo da massimizzare la probabilità che il test abbia successo.
+Prima però di illustrarne alcuni, vediamo quali caratteristiche dovrebbero avere questi criteri.
+
+Esploriamo quindi ora una serie di criteri di selezione, elencandone pro e contro, esplicitandone la metrica di copertura utilizzata e infine confrontandoli tra di loro per comprenderne le relazioni.
+
+- [**Criterio di copertura dei comandi**](./01_comandi.md)
+- [**Criterio di copertura delle decisioni**](./02_decisioni.md)
+- [**Criterio di copertura delle condizioni**](./03_condizioni.md)
+- [**Criterio di copertura delle decisioni e condizioni**](./04_decisioni-condizioni.md)
+- [**Criterio di copertura delle condizioni composte**](./05_condizioni-composte.md)
+- [**Criterio di copertura delle condizioni e decisioni modificate**](./06_condizioni-decisioni-modificate.md)
+- [**Implicazioni tra criteri di copertura**](./07_implicazioni.md)
diff --git a/src/12_testing/04_criteri-noti/01_comandi.md b/src/12_testing/04_criteri-noti/01_comandi.md
new file mode 100644
index 0000000000000000000000000000000000000000..8d754b51211d3ce282fe91bf970ec673acb5d2a7
--- /dev/null
+++ b/src/12_testing/04_criteri-noti/01_comandi.md
@@ -0,0 +1,51 @@
+# Criterio di copertura dei comandi
+
+_Un test \\(\ T\\) soddisfa il __criterio di copertura dei comandi__ se e solo se ogni comando eseguibile del programma è eseguito in corrispondenza di almeno un caso di test \\(t \in T\\)._ \
+La metrica è dunque la frazione di __comandi eseguibili su quelli eseguiti__ dall'intero test.
+
+Consideriamo per esempio il seguente programma in pseudocodice:
+
+<table>
+<thead>
+<tr>
+    <th colspan="2">Esempio 1: copertura dei comandi</th>
+</tr>
+<tr>
+    <td style="width: 50%" align="center">Pseudocodice</td>
+    <td style="width: 50%" align="center">Diagramma di flusso di esecuzione</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+
+```c
+01  void main(){
+02      float x, y;
+03      read(x);
+04      read(y);
+05      if (x != 0)
+06          x = x + 10;
+07      y = y / x;
+08      write(x);
+09      write(y);
+10  }
+```
+</td>
+<td>
+
+![Esempio criterio copertura comandi](/assets/13_criteri-copertura-esempio-1.png)
+</td>
+</tr>
+</tbody>
+</table>
+
+È possibile ricostruire un __diagramma di flusso di esecuzione__ del codice trasformando ogni comando in un nodo del diagramma: _coprire tutti i comandi_ significa quindi visitare tutti i nodi raggiungibili.
+
+Applicare il _criterio di copertura dei comandi_ significa quindi trovare un insieme di casi di test in cui _per ogni nodo esiste un caso di test che lo attraversa_.
+
+Nel nostro esempio il singolo caso di test \\( \langle 3, \\, 7 \rangle\\) risulterebbe quindi sufficiente, dato che la sua esecuzione permette di _coprire_ tutti i comandi del programma.
+Tuttavia, pur massimizzando la metrica di copertura dei comandi tale test non è in grado di rilevare l'anomalia alla riga 7, in cui viene fatta una divisione senza prima controllare che il divisore sia diverso da zero.
+
+Soddisfare il criterio di copertura dei comando __non garantisce__ dunque la correttezza del programma.
+Come sappiamo infatti un'anomalia non sempre genera un malfunzionamento, per cui eseguire semplicemente tutte le righe di codice raggiungibili non assicura di rilevare eventuali errori.
diff --git a/src/12_testing/04_criteri-noti/02_decisioni.md b/src/12_testing/04_criteri-noti/02_decisioni.md
new file mode 100644
index 0000000000000000000000000000000000000000..40a18b986bce025c76f07ac8cadbaad85565381b
--- /dev/null
+++ b/src/12_testing/04_criteri-noti/02_decisioni.md
@@ -0,0 +1,47 @@
+# Criterio di copertura delle decisioni
+
+_Un test \\(\ T\\) soddisfa il __criterio di copertura delle decisioni__ se e solo se ogni decisione (effettiva) viene resa sia vera che falsa in corrispondenza di almeno un caso di test \\(t \in T\\)_. \
+La metrica è quindi la frazione delle __decisioni totali possibili__ presenti nel codice che sono state rese __sia vere che false__ nel test.
+
+Dovendo attraversare ogni possibile flusso di controllo il criterio di copertura delle decisioni __implica il criterio di copertura dei comandi__.
+Estraendo il codice in un diagramma di flusso, infatti, è possibile coprire tutte le decisioni se e solo se ogni arco (_e quindi ogni nodo_) viene attraversato.
+Non è invece vero l'inverso.
+
+<table>
+<thead>
+<tr>
+    <th colspan="2">Esempio 2: copertura delle decisioni</th>
+</tr>
+<tr>
+    <td style="width: 50%" align="center">Pseudocodice</td>
+    <td style="width: 50%" align="center">Diagramma di flusso di esecuzione</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+
+```c
+01  void main(){
+02      float x, y;
+03      read(x);
+04      read(y);
+05      if (x != 0 && y > 0)
+06          x = x + 10;
+07      else
+08          y = y / x
+09      write(x);
+10      write(y);
+11  }
+```
+</td>
+<td>
+
+![Esempio criterio decisioni](/assets/13_criteri-copertura-esempio-2.png)
+</td>
+</tr>
+</tbody>
+</table>
+
+Dall'esempio sopra, un test che soddisfi il suddetto criterio potrebbe includere \\(\{ \langle 3, \\, 7 \rangle, \\, \langle 3, \\, -2 \rangle \}\\).
+Nonostante sia un criterio _"migliore"_ del precedente, la copertura delle decisioni __non garantisce__ la correttezza del programma: nell'esempio il caso \\(\langle 0, \\, 5 \rangle\\) eseguirebbe comunque una divisione per zero.
diff --git a/src/12_testing/04_criteri-noti/03_condizioni.md b/src/12_testing/04_criteri-noti/03_condizioni.md
new file mode 100644
index 0000000000000000000000000000000000000000..b75f2eddb4a29e9cd6c9c2ef329c3f650720f515
--- /dev/null
+++ b/src/12_testing/04_criteri-noti/03_condizioni.md
@@ -0,0 +1,50 @@
+# Criterio di copertura delle condizioni
+
+_Un test \\(\ T\\) soddisfa il __criterio di copertura delle condizioni__ se e solo se ogni singola condizione (effettiva) viene resa sia vera che falsa in corrispondenza di almeno un caso di test \\(\ t \in T\\)_. \
+Similmente ai criteri precedenti, la metrica è quindi la percentuale delle __condizioni__ che sono state rese __sia vere che false__ su quelle per cui è possibile farlo.
+
+Sebbene simile, si tratta di un criterio diverso da quello di copertura delle decisioni: in caso di condizioni composte, come per esempio `x != 0 && y < 3`, la copertura delle decisioni imporrebbe che l'_intera condizione_ sia resa sia vera che falsa, mentre la copertura delle condizioni richiede di rendere vere e false le singole _condizioni atomiche_ `x != 0` e `y < 3` in almeno un caso di test. \
+Come vedremo nell'esempio, ciò non impone quindi di seguire tutti i percorsi sul diagramma di flusso e fa sì che questo criterio __non implica__ il soddisfacimento di nessuno dei precedenti.
+
+<table>
+<thead>
+<tr>
+    <th colspan="2">Esempio 3: copertura delle condizioni</th>
+</tr>
+<tr>
+    <td style="width: 50%" align="center">Pseudocodice</td>
+    <td style="width: 50%" align="center">Diagramma di flusso di esecuzione</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+
+```c
+01  void main(){
+02      float x, y;
+03      read(x);
+04      read(y);
+05      if (x != 0 || y > 0)
+06          y = y / x;
+07      else
+08          y = (y + 2) / x
+09      y = y / x;
+10      write(x);
+11      write(y);
+12  }
+```
+</td>
+<td>
+
+![Esempio criterio decisioni](/assets/13_criteri-copertura-esempio-3.png)
+</td>
+</tr>
+</tbody>
+</table>
+
+Nell'esempio sopra, il test \\( \{ \langle 0, \\, 5 \rangle , \\, \langle 5, \\, -5 \rangle \} \\) __soddisfa il criterio di copertura della condizioni__ \\
+(`x != 0` è falsificato da \\(\langle 0, \\,5 \rangle\\) e verificato da \\(\langle 5, \\, -5 \rangle\\), mentre `y > 0` è verificato da \\(\langle 0, \\, 5 \rangle\\) e falsificato da \\(\langle 5, \\, -5 \rangle\\)), ma __la decisione è sempre vera__.
+
+Sono infatti presenti anomalie alla riga 6 (possibile divisione per zero) e alla riga 8 (overflow e divisione per zero), ma i comandi contenuti nella riga 8 non sono coperti.
+In questo caso più che mai, quindi, la copertura delle condizioni __non garantisce__ la correttezza del programma.
diff --git a/src/12_testing/04_criteri-noti/04_decisioni-condizioni.md b/src/12_testing/04_criteri-noti/04_decisioni-condizioni.md
new file mode 100644
index 0000000000000000000000000000000000000000..1623e2ffac6f7a8cf2cd539801a3e8217038cba2
--- /dev/null
+++ b/src/12_testing/04_criteri-noti/04_decisioni-condizioni.md
@@ -0,0 +1,8 @@
+# Criterio di copertura delle decisioni e condizioni
+
+_Un test \\(\ T\\) soddisfa il __criterio di copertura delle decisioni e delle condizioni__ se e solo se __ogni decisione__ vale sia vero che falso e __ogni condizione__ che compare nelle decisioni del programma vale sia vero che falso per diversi casi di test \\(\ t \in T\\)_.
+
+È – intuitivamente – l'__intersezione__ del criterio di copertura delle decisioni con il criterio di copertura delle condizioni, per cui il soddisfacimento di questo criterio __implica__ sia il criterio di copertura delle condizioni che quello di copertura delle decisioni (e quindi dei comandi).
+
+Nell'esempio 3, il test \\(\{ \langle 0, \\, -5 \rangle, \\, \langle 5, \\, 5 \rangle \}\\) soddisfa il criterio di copertura delle decisioni e condizioni e rileva l'anomalia alla riga 8 ma non quella alla riga 6.
+__Non garantisce__ quindi neanche in questo caso la correttezza del programma.
diff --git a/src/12_testing/04_criteri-noti/05_condizioni-composte.md b/src/12_testing/04_criteri-noti/05_condizioni-composte.md
new file mode 100644
index 0000000000000000000000000000000000000000..c6285edc6bb4f376181ce5fba3d75dc79354cf9a
--- /dev/null
+++ b/src/12_testing/04_criteri-noti/05_condizioni-composte.md
@@ -0,0 +1,9 @@
+# Criterio di copertura delle condizioni composte
+
+_Un test \\(\ T\\) soddisfa il __criterio di copertura delle condizioni composte__ se e solo se ogni possibile composizione delle condizioni base vale sia vero che falso per diversi casi di test \\(\ t \in T\\)_.
+
+Viene cioè testata ogni possibile combinazione di valori delle condizioni atomiche quando queste sono aggregate in condizioni composte: riprendendo per esempio la condizione `x != 0 && y < 3`, vengono testati separatamente i casi \\(\langle V, V\rangle\\), \\(\langle V, F\rangle\\), \\(\langle F, V\rangle\\) e \\(\langle F, F\rangle\\). \
+È quindi facile notare che __questo criterio implica il precedente__ (criterio di copertura delle decisioni e condizioni), implicando a sua volta il criterio di copertura delle decisioni, delle condizioni e dei comandi.
+
+Data la __natura combinatoria__ di questo criterio, all'aumento del numero di condizioni di base _il numero di casi di test_ cresce però troppo rapidamente, motivo per cui il soddisfacimento di questo criterio è considerato __non applicabile__ in pratica.
+Inoltre, dato che le condizioni di base potrebbero non risultare indipendenti tra loro, potrebbero esistere __combinazioni non fattibili__ che non avrebbe alcun senso testare.
diff --git a/src/12_testing/04_criteri-noti/06_condizioni-decisioni-modificate.md b/src/12_testing/04_criteri-noti/06_condizioni-decisioni-modificate.md
new file mode 100644
index 0000000000000000000000000000000000000000..9c9a8b5702bb2ade4ed73a694303b2d34d31175c
--- /dev/null
+++ b/src/12_testing/04_criteri-noti/06_condizioni-decisioni-modificate.md
@@ -0,0 +1,10 @@
+# Criterio di copertura delle condizioni e delle decisioni modificate
+
+Non volendo testare tutte le combinazioni di condizioni, ci si rende presto conto che certe combinazioni sono __più rilevanti__ di altre: se modificando una sola condizione atomica si riesce a modificare l'esito della decisione, allora è molto significativa – indipendentemente dalla sua dimensione.
+Se invece l'esito della decisione non varia, allora la modifica può essere considerata neutra o meno significativa. \
+Il criterio così ottenuto prende il nome di __criterio di copertura delle condizioni e delle decisioni modificate__.
+
+Si dà quindi importanza nella selezione delle combinazioni al fatto che la modifica di una singola condizione base porti a __modificare l'esito della decisione__.
+Per ogni condizione base devono quindi esistere due casi di test che modificano il valore di una sola condizione base e che portino a un diverso esito della decisione: in questo modo, inoltre, il criterio __implica quello di copertura delle condizioni e delle decisioni__.
+
+Si può dimostrare che se si hanno \\(N\\) condizioni base __sono sufficienti \\(N+1\\) casi di test__ per soddisfare questo criterio, decisamente meno di quelli richiesti dal criterio delle condizioni composte.
diff --git a/src/12_testing/04_criteri-noti/07_implicazioni.md b/src/12_testing/04_criteri-noti/07_implicazioni.md
new file mode 100644
index 0000000000000000000000000000000000000000..bb9b91fadd5c73d40c08aa621b5765e2747e078a
--- /dev/null
+++ b/src/12_testing/04_criteri-noti/07_implicazioni.md
@@ -0,0 +1,6 @@
+# Implicazioni tra criteri di copertura
+
+![Implicazioni tra criteri di copertura](/assets/13_criteri-copertura-implicazione.png)
+
+Ecco dunque uno schema delle implicazioni tra i vari criteri di copertura.
+Come si vede, il criterio delle condizioni composte va considerato troppo oneroso e quindi non applicabile, mentre gli altri criteri possono invece essere utilizzati anche nell'ambito di progetti di dimensioni reali.
diff --git a/src/12_testing/05_altri-criteri/00_index.md b/src/12_testing/05_altri-criteri/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..c1785ec9df11cc671461d1864d1e607904b15c71
--- /dev/null
+++ b/src/12_testing/05_altri-criteri/00_index.md
@@ -0,0 +1,48 @@
+# Altri criteri
+
+I criteri visti finora __non considerano i cicli__ e possono essere soddisfatti da test che percorrono ogni ciclo al più una volta.
+Molti errori però si verificano durante __iterazioni successive alla prima__, come per esempio quando si superano i limiti di un array.
+
+Occorre quindi sviluppare dei criteri che tengano conto anche delle iterazioni e stimolino i cicli un numero di volte sufficiente.
+
+<table>
+<thead>
+<tr>
+    <th colspan="2">Esempio 4: copertura delle iterazioni</th>
+</tr>
+<tr>
+    <td style="width: 50%" align="center">Pseudocodice</td>
+    <td style="width: 50%" align="center">Diagramma di flusso di esecuzione</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+
+```c
+01  void main() {
+02      float a, b, x, y;
+03      read(x);
+04      read(y);
+05      a = x;
+06      b = y;
+07      while (a != b) {
+08          if (a > b)
+09              a = a - b;
+10          else
+11              b = b - a;
+12      }
+13      write(a);
+14  }
+```
+</td>
+<td>
+
+![Esempio criteri di copertura](/assets/13_criteri-copertura-esempio-4.png)
+</td>
+</tr>
+</tbody>
+</table>
+
+- [**Criterio di copertura dei cammini**](./01_cammini.md)
+- [**Criterio di \\(n\\)-copertura dei cicli**](./02_cicli.md)
diff --git a/src/12_testing/05_altri-criteri/01_cammini.md b/src/12_testing/05_altri-criteri/01_cammini.md
new file mode 100644
index 0000000000000000000000000000000000000000..09f74c98f17da55aa00178ff844f120184d7afce
--- /dev/null
+++ b/src/12_testing/05_altri-criteri/01_cammini.md
@@ -0,0 +1,6 @@
+# Criterio di copertura dei cammini
+
+_Un test \\(\ T\\) soddisfa il __criterio di copertura dei cammini__ se e solo se ogni cammino del grafo di controllo del programma viene percorso per almeno un caso di \\(t \in T\\)_. \
+La metrica è quindi il rapporto tra i __cammini percorsi__ e __quelli effettivamente percorribili__.
+
+Questo criterio è molto generale ma è spesso impraticabile, anche per programmi semplici: la presenza di cicli imporrebbe infatti di testare tutti gli infiniti cammini che li attraversano un numero arbitrario di volte. Il criterio è quindi considerato __non applicabile__ in pratica.
diff --git a/src/12_testing/05_altri-criteri/02_cicli.md b/src/12_testing/05_altri-criteri/02_cicli.md
new file mode 100644
index 0000000000000000000000000000000000000000..9e10e8a50831a3979a16e1036c9371b8ee4d1a77
--- /dev/null
+++ b/src/12_testing/05_altri-criteri/02_cicli.md
@@ -0,0 +1,20 @@
+# Criterio di \\(n\\)-copertura dei cicli
+
+_Un test \\(\ T\\) soddisfa il __criterio di \\(\bf{\it{n}}\\)-copertura__ se e solo se ogni cammino del grafo contenente al massimo un numero d'iterazioni di ogni ciclo non superiore a \\(n\\) viene percorso per almeno un caso di test \\(\ t \in T\\)._
+
+La definizione sopra non significa che il test deve eseguire \\(n\\) volte un ciclo, ma che per ogni numero \\(k\\) compreso tra 0 e \\(n\\) deve esistere un caso di test che esegue tale ciclo \\(k\\) volte.
+Si sta quindi __limitando il numero massimo di percorrenze__ dei cicli. \
+Di conseguenza, al crescere di \\(n\\) il numero di test aumenta molto rapidamente.
+Inoltre, fissare \\(n\\) a livello di programma può non essere un'azione così semplice: il numero d'iterazioni che necessita un ciclo per essere testato a fondo può essere __molto differente__ a seconda del caso.
+
+Per cercare di minimizzare il numero di test spesso il criterio applicato è quello di __\\(\bf{2}\\)-copertura dei cicli__.
+Si tratta infatti del numero minimo che permette comunque di testare tutte le casistiche principali:
+
+- zero iterazioni;
+- una iterazione;
+- _più di una_ iterazione.
+
+Il caso \\(n = 2\\) è cioè il minimo per considerare casistiche non banali: dando uno sguardo all'esempio sopra, infatti, con \\(n = 1\\) il ciclo (`while`) sarebbe stato indistinguibile da una semplice selezione (`if`); testando due iterazioni si incominciano a testare le vere caratteristiche del ciclo.
+Esso permette cioè di testare non solo i comandi che compongono il ciclo, ma anche sue le pre/post-condizioni ed eventuali invarianti.
+
+A differenza del criterio di copertura dei cammini, il criterio di \\(n\\)-copertura è considerato __applicabile__ a programmi reali.
diff --git a/src/12_testing/06_mappa.md b/src/12_testing/06_mappa.md
new file mode 100644
index 0000000000000000000000000000000000000000..5a816050400c33c6caecf7e3ec08d6310a3820b3
--- /dev/null
+++ b/src/12_testing/06_mappa.md
@@ -0,0 +1,5 @@
+# Mappa finale delle implicazioni tra criteri di selezione
+
+Aggiungendo i criteri di copertura che considerano esplicitamente i cicli si ottiene il seguente schema di implicazione tra tutti i criteri di selezione.
+
+![Mappa implicazioni criteri](/assets/13_implicazioni-estese-criteri-copertura.png)
diff --git a/src/13_analisi-statica/00_index.md b/src/13_analisi-statica/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..99e7a075c154bd08b0d20152f44ca822a90de592
--- /dev/null
+++ b/src/13_analisi-statica/00_index.md
@@ -0,0 +1,16 @@
+# Analisi statica
+
+Come abbiamo detto nella lezione precedente, il testing dell'esecuzione del programma non è però l'unica cosa che possiamo fare per aumentare la fiducia nostra e del cliente nella correttezza del programma.
+Un'altra importante iniziativa in tal senso è l'ispezione tramite varie tecniche del _codice_ del programma, attività che prende il nome di __analisi statica__.
+
+L'analisi statica si basa cioè sull'esame di un __insieme finito di elementi__ (_le istruzioni del programma_), contrariamente all'analisi dinamica che invece considera un insieme infinito (_gli stati delle esecuzioni_).
+È un'attività perciò __meno costosa del testing__, poiché non soffre del problema dell'_"esplosione dello spazio degli stati"_.
+
+Considerando solamente il codice "statico" del programma, questa tecnica non ha la capacità di rilevare anomalie dipendenti da particolari valori assunti dalle variabili a runtime.
+Si tratta nondimeno di un'attività estremamente utile, che può aiutare a individuare numerosi errori e inaccortezze.
+
+- [**Compilatori**](./01_compilatori.md)
+- [**Analisi Data Flow**](./02_data-flow/00_index.md)
+- [**Testing**](./03_testing.md)
+- [**Criteri di copertura**](./04_criteri/00_index.md)
+- [**Oltre le variabili**](./05_oltre-variabili.md)
diff --git a/src/13_analisi-statica/01_compilatori.md b/src/13_analisi-statica/01_compilatori.md
new file mode 100644
index 0000000000000000000000000000000000000000..a7feaa3af2133b0e8ee0dd5e67f1bd73da6e6689
--- /dev/null
+++ b/src/13_analisi-statica/01_compilatori.md
@@ -0,0 +1,54 @@
+# Compilatori
+
+Prima di trasformare il codice sorgente in eseguibile, i compilatori fanno un'attività di analisi statica per identificare errori sintattici (o presunti tali) all'interno del codice.
+
+Il lavoro dei compilatori si può dividere solitamente in __quattro tipi di analisi__ (gli esempi sono presi dal compilatore di Rust, caratteristico per la quantità e qualità di analisi svolta durante la compilazione):
+
+- __analisi lessicale__: identifica i token appartenenti o meno al linguaggio, permettendo di individuare possibili errori di battitura;
+
+  ```txt
+  error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `ciao`
+  --> src/main.rs:2:9
+    |
+  2 |     BRO ciao = "mondo";
+    |           ^^^^ expected one of 8 possible tokens
+  ```
+
+- __analisi sintattica__: controlla che i token identificati siano in relazioni _sensate_ tra di loro in base alla grammatica del linguaggio, scovando così possibili errori di incomprensione del linguaggio;
+
+  ```txt
+  error: expected `{`, found keyword `for`
+  --> src/main.rs:2:14
+    |
+  2 |     if !expr for;
+    |              ^^^ expected `{`
+    |
+  ```
+
+- __controllo dei tipi__: nei linguaggi tipizzati, individua violazioni di regole d'uso dei tipi ed eventuali incompatibilità tra tipi di dati;
+
+  ```txt
+  error[E0308]: mismatched types
+  --> src/main.rs:2:24
+    |
+  2 |     let name: String = 42;
+    |               ------   ^^- help: try using a conversion method: `.to_string()`
+    |               |        |
+    |               |        expected struct `String`, found integer
+    |               expected due to this
+
+  For more information about this error, try `rustc --explain E0308`.
+  ```
+
+- __analisi flusso dei dati__: si cercano di rilevare problemi relativi all'_evoluzione dei valori_ associati alle variabili, come per esempio porzioni di codice non raggiungibili.
+
+  ```txt
+  error: equal expressions as operands to `!=`
+  --> src/main.rs:2:8
+    |
+  2 |     if 1 != 1 {
+    |        ^^^^^^
+    |
+  ```
+
+Se i primi tre tipi di analisi sono abbastanza facili da comprendere, l'ultimo merita una maggiore attenzione, motivo per cui gli dedichiamo il prossimo paragrafo.
diff --git a/src/13_analisi-statica/02_data-flow/00_index.md b/src/13_analisi-statica/02_data-flow/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..ebfe5aab8ff5b2a5c8a4fc66fa5a8aa51781e618
--- /dev/null
+++ b/src/13_analisi-statica/02_data-flow/00_index.md
@@ -0,0 +1,65 @@
+<!-- KaTeX op macro definitions -->
+<div style="display: none; margin: 0;">
+$$
+\require{color}
+% Regular operations
+\def\op#1{
+  \fcolorbox{black}{white}{$\vphantom{d} \sf{#1}$}
+}
+\def\d{\op{d} \\,}
+\def\a{\op{a} \\,}
+\def\u{\op{u} \\,}
+% Erroneous operations
+\def\opR#1{
+  \fcolorbox{black}{orangered}{$\vphantom{d} \color{white}{\sf{#1}}$}
+}
+\def\dR{\opR{d} \\,}
+\def\aR{\opR{a} \\,}
+\def\uR{\opR{u} \\,}
+% Subscript operations
+\def\Op#1#2{
+  \fcolorbox{black}{white}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\D#1{\Op{d}{#1} \\,}
+\def\A#1{\Op{a}{#1} \\,}
+\def\U#1{\Op{u}{#1} \\,}
+% Warning subscript operations
+\def\OpW#1#2{
+  \fcolorbox{black}{orange}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+% Green subscript operations
+\def\OpG#1#2{
+  \fcolorbox{black}{lightgreen}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\DG#1{\OpG{d}{#1} \\,}
+\def\AG#1{\OpG{a}{#1} \\,}
+\def\UG#1{\OpG{u}{#1} \\,}
+% Error
+\def\Err{
+  \color{red}{\sf{ERROR}}
+}
+\def\err{
+  \\, \Err
+}
+$$
+</div>
+
+# Analisi Data Flow
+
+Nata nell'ambito dell'__ottimizzazione dei compilatori__, che per migliorare le proprie performance ricercavano porzioni di codice non raggiungibile da non compilare, l'__analisi del flusso di dati__ è stata più avanti imbracciata dall'ingegneria del software per ricercare e prevenire le cause di errori simili. \
+Parlando di flusso dei dati si potrebbe pensare a un'analisi prettamente dinamica come il testing, ma l'insieme dei controlli statici che si possono fare sul codice per comprendere come vengono _utilizzati_ i valori presenti nel programma è invece particolarmente significativo.
+
+È possibile infatti analizzare staticamente il tipo delle operazioni eseguite su una variabile e l'__insieme dei legami di compatibilità__ tra di esse per determinare se il valore in questione viene usato in maniera _semanticamente sensata_ all'interno del codice. \
+Nello specifico, le operazioni che possono essere eseguite su un __dato__ sono solamente di tre tipi:
+
+- \\(\op{d}\\) (__definizione__): il comando __assegna un valore__ alla variabile; anche il passaggio del dato come parametro ad una funzione che lo modifica è considerata un'operazione di (ri)definizione;
+
+- \\(\op{u}\\) (__uso__): il comando __legge il contenuto__ di una variabile, come per esempio l'espressione sul lato destro di un assegnamento;
+
+- \\(\op{a}\\) (__annullamento__): al termine dell'esecuzione del comando il valore della variabile __non è significativo/affidabile__.
+  Per esempio, dopo la _dichiarazione senza inizializzazione_ di una variabile e al termine del suo _scope_ il valore è da considerarsi inaffidabile.
+
+Dal punto di vista di ciascuna variabile è possibile ridurre una qualsiasi sequenza d'istruzioni (_ovvero un cammino sul diagramma di flusso_) a una sequenza di definizioni, usi e annullamenti.
+
+- [**Regole**](./01_regole.md)
+- [**Sequenze**](02_sequenze.md)
diff --git a/src/13_analisi-statica/02_data-flow/01_regole.md b/src/13_analisi-statica/02_data-flow/01_regole.md
new file mode 100644
index 0000000000000000000000000000000000000000..3f419379a379902999d1e403e82e730170f7c06a
--- /dev/null
+++ b/src/13_analisi-statica/02_data-flow/01_regole.md
@@ -0,0 +1,152 @@
+<!-- KaTeX op macro definitions -->
+<div style="display: none; margin: 0;">
+$$
+\require{color}
+% Regular operations
+\def\op#1{
+  \fcolorbox{black}{white}{$\vphantom{d} \sf{#1}$}
+}
+\def\d{\op{d} \,}
+\def\a{\op{a} \,}
+\def\u{\op{u} \,}
+% Erroneous operations
+\def\opR#1{
+  \fcolorbox{black}{orangered}{$\vphantom{d} \color{white}{\sf{#1}}$}
+}
+\def\dR{\opR{d} \,}
+\def\aR{\opR{a} \,}
+\def\uR{\opR{u} \,}
+% Subscript operations
+\def\Op#1#2{
+  \fcolorbox{black}{white}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\D#1{\Op{d}{#1} \,}
+\def\A#1{\Op{a}{#1} \,}
+\def\U#1{\Op{u}{#1} \,}
+% Warning subscript operations
+\def\OpW#1#2{
+  \fcolorbox{black}{orange}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+% Green subscript operations
+\def\OpG#1#2{
+  \fcolorbox{black}{lightgreen}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\DG#1{\OpG{d}{#1} \,}
+\def\AG#1{\OpG{a}{#1} \,}
+\def\UG#1{\OpG{u}{#1} \,}
+% Error
+\def\Err{
+  \color{red}{\sf{ERROR}}
+}
+\def\err{
+  \, \Err
+}
+$$
+</div>
+
+# Regole
+
+Fatta questa semplificazione è allora possibile individuare la presenza di anomalie nell'uso delle variabili definendo alcune __regole di flusso__: alcune di queste devono essere necessariamente rispettate in un programma corretto (1 e 3), mentre altre hanno più a che fare con la semantica dell'uso di un valore (2).
+
+<ol>
+
+<li>
+
+  L'**uso di una variabile** deve essere **sempre preceduto** in ogni sequenza **da una definizione senza annullamenti intermedi**.
+
+  $$
+  \a\u\err
+  $$
+</li>
+<li>
+
+  La **definizione di una variabile** deve essere **sempre seguita** da **un uso**, **prima** di un suo **annullamento** o nuova **definizione**.
+
+  $$
+  \d\a\err \\\\
+  \d\d\err
+  $$
+
+</li>
+  <li>
+
+  L'**annullamento di una variabile** deve essere **sempre seguito** da **una definizione**, **prima** di un **uso** o **altro annullamento**.
+  
+  $$
+  \a\a\err
+  $$
+
+</li>
+</ol>
+
+Riassumendo, \\(\a\op{u}\\), \\(\d\op{a}\\), \\(\d\op{d}\\) e \\(\a\op{a}\\) sono sequenze che identificano __situazioni anomale__, anche se non necessariamente dannose: se per esempio usare una variabile subito dopo averla annullata rende l'esecuzione del programma non controllabile, un annullamento subito dopo una definizione non crea nessun problema a runtime, ma è altresì indice di un possibile errore concettuale.
+
+<table align="center" style="width: 50%">
+<tr>
+  <th></th>
+  <th>
+  
+  \\(\a\\)</th>
+  <th>
+  
+  \\(\d\\)</th>
+  <th>
+  
+  \\(\u\\)</th>
+</tr>
+<tr>
+  <th>
+  
+  \\(\a\\)</th>
+  <th>
+  
+  \\(\Err\\)</th>
+  <th></th>
+  <th>
+  
+  \\(\Err\\)</th>
+</tr>
+<tr>
+  <th>
+  
+  \\(\d\\)</th>
+  <th>
+  
+  \\(\Err\\)</th>
+  <th>
+  
+  \\(\Err\\)</th>
+  <th></th>
+</tr>
+<tr>
+  <th>
+  
+  \\(\u\\)</th>
+  <th></th>
+  <th></th>
+  <th></th>
+</tr>
+</table>
+
+#### Esempio
+
+Consideriamo la seguente funzione C con il compito di scambiare il valore di due variabili:
+
+```c
+void swap(int &x1, int &x2) {
+    int x1;
+    x3 = x1;
+    x3 = x2;
+    x2 = x1;
+}
+```
+
+Analizzando il codice, le sequenze per ogni variabile sono le seguenti:
+
+| Variabile | Sequenza | Anomalie |
+|-|-|-|
+| `x1` | \\(\aR\uR\u\a\\) | `x1` viene usata 2 volte senza essere stata prima definita |
+| `x2` | \\(\dots \d\u\op{d} \dots\\) | Nessuna |
+| `x3` | \\(\dots \d\dR\opR{d} \dots\\) | `x3` viene definita più volte senza nel frattempo essere stata usata |
+
+Come si vede, in un codice sintatticamente corretto l'analisi Data Flow ci permette quindi di scovare un errore semantico osservando le sequenze di operazioni sulle sue variabili.
diff --git a/src/13_analisi-statica/02_data-flow/02_sequenze.md b/src/13_analisi-statica/02_data-flow/02_sequenze.md
new file mode 100644
index 0000000000000000000000000000000000000000..2a73ffef645024b8eeb4781c5cf820d1d89d1ec5
--- /dev/null
+++ b/src/13_analisi-statica/02_data-flow/02_sequenze.md
@@ -0,0 +1,124 @@
+<!-- KaTeX op macro definitions -->
+<div style="display: none; margin: 0;">
+$$
+\require{color}
+% Regular operations
+\def\op#1{
+  \fcolorbox{black}{white}{$\vphantom{d} \sf{#1}$}
+}
+\def\d{\op{d} \,}
+\def\a{\op{a} \,}
+\def\u{\op{u} \,}
+% Erroneous operations
+\def\opR#1{
+  \fcolorbox{black}{orangered}{$\vphantom{d} \color{white}{\sf{#1}}$}
+}
+\def\dR{\opR{d} \,}
+\def\aR{\opR{a} \,}
+\def\uR{\opR{u} \,}
+% Subscript operations
+\def\Op#1#2{
+  \fcolorbox{black}{white}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\D#1{\Op{d}{#1} \,}
+\def\A#1{\Op{a}{#1} \,}
+\def\U#1{\Op{u}{#1} \,}
+% Warning subscript operations
+\def\OpW#1#2{
+  \fcolorbox{black}{orange}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+% Green subscript operations
+\def\OpG#1#2{
+  \fcolorbox{black}{lightgreen}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\DG#1{\OpG{d}{#1} \,}
+\def\AG#1{\OpG{a}{#1} \,}
+\def\UG#1{\OpG{u}{#1} \,}
+% Error
+\def\Err{
+  \color{red}{\sf{ERROR}}
+}
+\def\err{
+  \, \Err
+}
+$$
+</div>
+
+# Sequenze
+
+Abbiamo accennato più volte al concetto di "sequenza" di operazioni su una variabile.
+Più formalmente, definiamo __sequenza__ di operazioni per la variabile \\(\mathtt{a}\\) secondo il cammino \\(p\\) la concatenazione della tipologia delle istruzioni che coinvolgono tale variabile, e la indichiamo con \\(\operatorname{P}(p, \\, \mathtt{a})\\).
+
+Considerando per esempio il seguente programma C:
+
+```c
+01  void main() {
+02      float a, b, x, y;
+03      read(x);
+04      read(y);
+05      a = x;
+06      b = y;
+07      while (a != b)
+08          if (a > b)
+09              a = a - b;
+10          else
+11              b = b - a;
+12      write(a);
+13  }
+```
+
+possiamo dire che:
+
+$$
+\begin{align*}
+&\operatorname{P}([1, 2, 3, 4, 5, 6, 7, 8, 9, 7, 12, 13], \\, \mathtt{a}) \\\\
+&= \A{2} \D{5} \U{7} \U{8} \U{9} \D{9} \U{7} \U{12} \A{13}
+\end{align*}
+$$
+
+Eseguendo questo tipo di operazione su tutte le variabili e per tutti i cammini del programma si potrebbe verificare la presenza eventuali anomalie, ma come sappiamo __i cammini sono potenzialmente infiniti__ quando il programma contiene cicli e decisioni: per scoprire quali percorsi segue effettivamente l'esecuzione del programma dovremmo eseguirlo e quindi uscire dal campo dell'analisi statica.
+
+#### Espressioni regolari
+
+Tuttavia non tutto è perduto: un caso di cammini contenenti __cicli__ e __decisioni__ è possibile rappresentare un insieme di sequenze ottenute dal programma \\(P\\) utilizzando delle __espressioni regolari__.
+Con \\(\operatorname{P}([1 \rightarrow], \\, \mathtt{a})\\) si indica infatti l'espressione regolare che rappresenta __tutti i cammini__ che partono dall'istruzione \\(1\\) per la variabile \\(\mathtt{a}\\).
+
+Questo perché nelle espressioni regolari è possibile inserire, oltre che una serie di parentesi che isolano sotto-sequenze, anche due simboli molto particolari:
+
+- la __pipe__ (\|), che indica che i simboli (o le sotto-sequenze) alla propria destra e alla propria sinistra si _escludono_ a vicenda: _una e una sola_ delle due è presente;
+- l'__asterisco__ (\*), che indica che il simbolo (o la sotto-sequenza) precedente può essere _ripetuto da 0 a \\(n\\) volte_.
+
+Grazie a questi simboli è possibile rappresentare rispettivamente decisioni e cicli.
+Prendendo per esempio il codice precedente, è possibile costruire \\(\operatorname{P}([1 \rightarrow], \\, \mathtt{a})\\) come:
+
+$$
+\begin{align*}
+&\A{2} \D{5} & & &&&  && && & & \\\\
+&\A{2} \D{5} &\U{7} &\Big( &\phantom{\U8} &&\textit{while body} &&\phantom{\U{7}} &&\Big)* &\quad \quad \U{12} &\A{13} \\\\
+&\A{2} \D{5} &\U{7} &\Big( &\U{8} &&\textit{if body} &&\phantom{\U{7}} &&\Big)* &\quad \quad \U{12} &\A{13} \\\\
+&\A{2} \D{5} &\U{7} &\Big( &\U{8} &&\Big(\, \U{9} \D{9} \Big | \: \U{11} \Big) && &&\Big)* &\quad \quad \U{12} &\A{13} \\\\
+&\A{2} \D{5} &\OpW{u}{7} \\, &\Big( \\, &\U{8} &&\Big(\, \U{9} \D{9} \Big | \: \U{11} \Big)
+  &&\OpW{u}{7} \\,
+&&\Big)* &\quad \quad \U{12} &\A{13}
+\end{align*}
+$$
+
+Osserviamo come \\(\OpW{u}{7}\\) si ripeta due volte: questo può rendere _fastidioso_ ricercare errori, per via della difficoltà di considerare cammini multipli.
+Comunque sia, una volta ottenuta un'espressione regolare è facile verificare l'eventuale presenza di errori applicando le solite regole (nell'esempio non ce n'erano).
+
+Bisogna però fare attenzione a un'aspetto: le espressioni regolari così costruite rappresentano __tutti i cammini__ possibili del programma, ma __non tutti e i soli__!
+Trattandosi di oggetti puramente matematici, infatti, le espressioni regolari sono necessariamente _più generali_ di qualunque programma: esse non tengono infatti conto degli _effetti_ che le istruzioni hanno sui dati e delle relative proprietà che si possono inferire. \
+Riprendendo a esempio l'espressione regolare di cui sopra, essa contiene la sequenza nella quale il ciclo viene eseguito _infinite volte_, ma osservando il programma è facile indovinare che tale comportamento non sia in realtà possibile: diminuendo progressivamente \\(\mathtt{a}\\) e \\(\mathtt{b}\\) a seconda di chi sia il maggiore si può dimostrare che prima o poi i due convergeranno allo stesso valore permettendo così di uscire dal ciclo.
+
+In effetti, uno stesso programma può essere rappresentato tramite __un numero infinito di espressioni regolari__ valide.
+Si potrebbe addirittura argomentare che l'espressione regolare
+
+$$
+\Big ( \\, \u \Big | \: \d \Big | \: \a \Big)*
+$$
+
+possa rappresentare qualsiasi programma. \
+Allontanandosi però dai casi estremi, si dimostra essere impossibile scrivere un algoritmo che dato un qualsiasi programma riesca a generare un'espressione regolare che rappresenti __tutti e soli__ i suoi cammini possibili senza osservare i valori delle variabili.
+Bisogna dunque accontentarsi di trovare espressioni regolari che rappresentino __al meglio__ l'esecuzione del programma, ovvero con il minor numero di cammini impossibili rappresentati.
+
+Nell'analisi Data Flow tramite espressioni regolari è quindi necessario tenere conto che il modello generato è un'__astrazione pessimistica__: se viene notificata la presenza di un errore non si può essere certi che esso ci sia veramente, in quanto esso potrebbe derivare da un cammino non percorribile.
\ No newline at end of file
diff --git a/src/13_analisi-statica/03_testing.md b/src/13_analisi-statica/03_testing.md
new file mode 100644
index 0000000000000000000000000000000000000000..d988a6d1ed753b0a38c7c015977c183e230084d1
--- /dev/null
+++ b/src/13_analisi-statica/03_testing.md
@@ -0,0 +1,23 @@
+# Testing
+
+Oltre ad essere un processo utile di per sé per il rilevamento di potenziali errori, l'__analisi statica__ può anche contribuire a guidare l'attività di __testing__. \
+Per capire come, osserviamo che a partire dall'analisi statica è possibile fare le seguenti osservazioni:
+
+- perché si presenti un malfunzionamento dovuto a una anomalia in una _definizione_, deve esserci un _uso_ che si serva del valore assegnato;
+- un ciclo dovrebbe essere ripetuto (di nuovo) se verrà _usato_ un valore _definito_ alla iterazione precedente.
+
+L'analisi statica può quindi aiutare a __selezionare i casi di test__ basandosi sulle _sequenze definizione-uso_ delle variabili, costruendo cioè dei nuovi criteri di copertura.
+
+## Terminologia
+
+Per rendere più scorrevole la spiegazione dei prossimi argomenti introduciamo innanzitutto un po' di terminologia.
+
+Dato un nodo \\(i\\) del diagramma di flusso (_un comando/riga del programma_), chiamiamo \\(\operatorname{def}(i)\\) l'__insieme delle variabili definite in__ \\(\bf{i}\\).
+
+Data invece una variabile \\(x\\) e un nodo \\(i\\), chiamiamo \\(\operatorname{du}(x, \\, i)\\) l'insieme dei nodi \\(j\\) tali che:
+
+- \\(x \in \operatorname{def}(i)\\), ovvero la variabile \\(x\\) è __definita__ in \\(i\\);
+- \\(x\\) è __usata__ nel nodo \\(j\\);
+- __esiste un cammino__ da \\(i\\) a \\(j\\) __libero da definizioni__ di \\(x\\), ovvero che se seguito non sovrascrive il valore di \\(x\\).
+
+Si tratta cioè dell'__insieme di nodi \\(\bf{j}\\) che _potrebbero_ usare il valore di \\(\bf{x}\\) definito in \\(\bf{i}\\)__.
diff --git a/src/13_analisi-statica/04_criteri/00_index.md b/src/13_analisi-statica/04_criteri/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..7653015a60afd97b9374e29d404f4ac87a6eebe6
--- /dev/null
+++ b/src/13_analisi-statica/04_criteri/00_index.md
@@ -0,0 +1,5 @@
+# Criteri di copertura derivati dall'analisi statica
+
+- [**Criterio di copertura delle definizioni**](./01_definizioni.md)
+- [**Criterio di copertura degli usi**](./02_usi.md)
+- [**Criterio di copertura dei cammini DU**](./03_cammini-du.md)
diff --git a/src/13_analisi-statica/04_criteri/01_definizioni.md b/src/13_analisi-statica/04_criteri/01_definizioni.md
new file mode 100644
index 0000000000000000000000000000000000000000..c54f587eacf8f16d5bb286176e580337b374583e
--- /dev/null
+++ b/src/13_analisi-statica/04_criteri/01_definizioni.md
@@ -0,0 +1,98 @@
+<!-- KaTeX op macro definitions -->
+<div style="display: none; margin: 0;">
+$$
+\require{color}
+% Regular operations
+\def\op#1{
+  \fcolorbox{black}{white}{$\vphantom{d} \sf{#1}$}
+}
+\def\d{\op{d} \,}
+\def\a{\op{a} \,}
+\def\u{\op{u} \,}
+% Erroneous operations
+\def\opR#1{
+  \fcolorbox{black}{orangered}{$\vphantom{d} \color{white}{\sf{#1}}$}
+}
+\def\dR{\opR{d} \,}
+\def\aR{\opR{a} \,}
+\def\uR{\opR{u} \,}
+% Subscript operations
+\def\Op#1#2{
+  \fcolorbox{black}{white}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\D#1{\Op{d}{#1} \,}
+\def\A#1{\Op{a}{#1} \,}
+\def\U#1{\Op{u}{#1} \,}
+% Warning subscript operations
+\def\OpW#1#2{
+  \fcolorbox{black}{orange}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+% Green subscript operations
+\def\OpG#1#2{
+  \fcolorbox{black}{lightgreen}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\DG#1{\OpG{d}{#1} \,}
+\def\AG#1{\OpG{a}{#1} \,}
+\def\UG#1{\OpG{u}{#1} \,}
+% Error
+\def\Err{
+  \color{red}{\sf{ERROR}}
+}
+\def\err{
+  \, \Err
+}
+$$
+</div>
+
+# Criterio di copertura delle definizioni
+
+_Un test \\(\ T\\) soddisfa il __criterio di copertura delle definizioni__ se e solo se per ogni nodo \\(i\\) e ogni variabile \\(x \in \operatorname{def}(i)\\), \\(T\\) include un caso di test che esegue un cammino libero da definizioni da \\(i\\) ad __almeno uno__ degli elementi di \\(\operatorname{du}(i, x).\\)_
+
+Ci si vuole cioè assicurare di testare tutte le definizioni, assicurandosi che funzionino osservando almeno un uso del valore da loro assegnato.
+Matematicamente si può dire che:
+
+$$
+\begin{align*}
+T \in C_{def} \Longleftrightarrow& \forall i \in P, \  \forall x \in \operatorname{def}(i), \ \exists j \in \operatorname{du}(i, \\, x), \\\\
+& \exists t \in T \ \text{che esegue un cammino da $i$ a $j$ senza ulteriori definizioni di $x$}.
+\end{align*}
+$$
+
+Riconsideriamo l'esempio già visto in precedenza, considerando la variabile \\(\mathtt{a}\\):
+
+```c
+01  void main() {
+02      float a, b, x, y;
+03      read(x);
+04      read(y);
+05      a = x;
+06      b = y;
+07      while (a != b)
+08          if (a > b)
+09              a = a - b;
+10          else
+11              b = b - a;
+12      write(a);
+13  }
+```
+
+Partiamo definendo gli insiemi dei nodi degli usi \\(\operatorname{du}(i, \\, \mathtt a)\\):
+
+1. \\(\operatorname{du}(5, \\, \mathtt a)\\) = \\(\{7, \\, 8, \\, 9, \\, 11, \\, 12\}\\);
+2. \\(\operatorname{du}(9, \\, \mathtt a)\\) = \\(\{7, \\, 8, \\, 9, \\, 11, \\, 12\}\\).
+
+È solo __un caso__ il fatto che in questo esempio tali insiemi siano uguali. \
+Comunque sia, l'obiettivo è _per ognuna delle due definizioni_ ottenere un __uso__ di tale definizione:
+
+1. Per la prima definizione la soluzione è banale, a riga 7 la variabile \\(\mathtt a\\) viene letta sempre:
+\\(\D{5}\U{7}\\).
+2. Per la seconda, invece, è necessario scegliere un valore tale per cui il flusso di esecuzione entri almeno una volta nel ciclo ed esegua almeno una volta la riga 9:
+\\(\D{9}\U{7}\\).
+
+Un test che soddisfa totalmente il criterio può essere il seguente:
+
+$$
+T = \{ \langle 8, \\, 4 \rangle \}.
+$$
+
+Come si vede, il criterio di copertura delle definizioni non copre tutti i comandi e di conseguenza __non implica il criterio di copertura dei comandi__.
diff --git a/src/13_analisi-statica/04_criteri/02_usi.md b/src/13_analisi-statica/04_criteri/02_usi.md
new file mode 100644
index 0000000000000000000000000000000000000000..4f8e4f876a3ea84dfeaeb460dedf22dd7533a279
--- /dev/null
+++ b/src/13_analisi-statica/04_criteri/02_usi.md
@@ -0,0 +1,146 @@
+<!-- KaTeX op macro definitions -->
+<div style="display: none; margin: 0;">
+$$
+\require{color}
+% Regular operations
+\def\op#1{
+  \fcolorbox{black}{white}{$\vphantom{d} \sf{#1}$}
+}
+\def\d{\op{d} \,}
+\def\a{\op{a} \,}
+\def\u{\op{u} \,}
+% Erroneous operations
+\def\opR#1{
+  \fcolorbox{black}{orangered}{$\vphantom{d} \color{white}{\sf{#1}}$}
+}
+\def\dR{\opR{d} \,}
+\def\aR{\opR{a} \,}
+\def\uR{\opR{u} \,}
+% Subscript operations
+\def\Op#1#2{
+  \fcolorbox{black}{white}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\D#1{\Op{d}{#1} \,}
+\def\A#1{\Op{a}{#1} \,}
+\def\U#1{\Op{u}{#1} \,}
+% Warning subscript operations
+\def\OpW#1#2{
+  \fcolorbox{black}{orange}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+% Green subscript operations
+\def\OpG#1#2{
+  \fcolorbox{black}{lightgreen}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\DG#1{\OpG{d}{#1} \,}
+\def\AG#1{\OpG{a}{#1} \,}
+\def\UG#1{\OpG{u}{#1} \,}
+% Error
+\def\Err{
+  \color{red}{\sf{ERROR}}
+}
+\def\err{
+  \, \Err
+}
+$$
+</div>
+
+# Criterio di copertura degli usi
+
+_Un test \\(\ T\\) soddisfa il __criterio di copertura degli usi__ se e solo se per ogni nodo \\(i\\) e ogni variabile \\(x\\) appartenente a \\(\operatorname{def}(i)\\), \\(T\\) include un caso di test che esegue un cammino libero da definizioni da \\(i\\) ad __ogni elemento__ di \\(\operatorname{du}(i, \\, x).\\)_
+
+Sembra simile al precedente, con la differenza che ora bisogna coprire __tutti__ i potenziali usi di una variabile definita.
+Questo appare ancora più chiaro osservando la formula matematica:
+
+$$
+\begin{align*}
+T \in C_{path} \Longleftrightarrow& \forall i \in P, \  \forall x \in \operatorname{def}(i), \ \forall j \in \operatorname{du}(i, \\, x), \\\\
+& \exists t \in T \ \text{che esegue un cammino da $i$ a $j$ senza ulteriori definizioni di $x$}.
+\end{align*}
+$$
+
+Si noti però che il criterio di copertura degli usi __non implica il criterio di copertura delle definizioni__, perché nel caso in cui non esistano \\(j \in \operatorname{du}(i, \\, x)\\) l'uso del \\(\forall\\) è più _"permissivo"_ del \\(\exists\\) del criterio precedente: quest'ultimo richiedeva infatti che per ogni definizione esistesse almeno un uso, mentre il criterio di copertura degli usi non pone tale clausola (_se non ci sono usi il \\(\forall\\) è sempre vero_).
+Viene quindi da sé che questo criterio non copre neanche il criterio di copertura dei comandi.
+
+Riconsideriamo nuovamente il programma in C visto in precedenza come esempio:
+
+```c
+01  void main() {
+02      float a, b, x, y;
+03      read(x);
+04      read(y);
+05      a = x;
+06      b = y;
+07      while (a != b)
+08          if (a > b)
+09              a = a - b;
+10          else
+11              b = b - a;
+12      write(a);
+13  }
+```
+
+Come prima, consideriamo la variabile \\(\mathtt a\\) e i relativi insieme dei nodi degli usi per ogni sua definizione:
+
+1. \\(\operatorname{du}(5, \\, \mathtt a)\\) = \\(\{7, \\, 8, \\, 9, \\, 11, \\, 12\}\\);
+2. \\(\operatorname{du}(9, \\, \mathtt a)\\) = \\(\{7, \\, 8, \\, 9, \\, 11, \\, 12\}\\).
+
+Per ogni definizione occorre coprire __tutti gli usi__:
+
+<style>
+  #criterio-usi-tabella {
+    text-align: center;
+  }
+  #criterio-usi-tabella p {
+    margin-bottom: 0;
+  }
+</style>
+
+<table id="criterio-usi-tabella" style="text-align: center;">
+<tr>
+  <th style="width: 50%">
+
+\\(\operatorname{du}(5, \\, \mathtt a)\\)
+  </th>
+  <th>
+  
+  \\(\operatorname{du}(9, \\, \mathtt a)\\)</th>
+</tr>
+<tr>
+  <td>
+  
+  \\(\D{5}\UG{7}\UG{8}\UG{11}\U{7}\UG{12}\\)
+  </td>
+  <td>
+  
+  \\(\dots \\, \D{9} \UG7 \UG8 \UG9 \dots\\)
+  </td>
+</tr>
+<tr>
+  <td>
+  
+  \\(\dots \\, \D5 \U7 \U8 \UG9 \dots\\)
+  </td>
+  <td>
+  
+  \\(\dots \\, \D9 \U7 \U8 \UG{12} \dots\\)
+  </td>
+</tr>
+<tr>
+  <td></td>
+  <td>
+  
+  \\(\dots \\, \D9 \U7 \U8 \UG{11} \dots\\)
+  </td>
+</tr>
+</table>
+
+Un test che soddisfa totalmente il criterio può essere il seguente:
+
+$$
+T = \{ \langle 4, \\,  8 \rangle, \\, \langle 12, \\, 8 \rangle, \\, \langle 12, \\, 4 \rangle \}.
+$$
+
+Questo esempio permette di notare qualcosa sulla natura dei cicli: dovendo testare ogni percorso al loro interno è necessario fare almeno due iterazioni.
+Può quindi sorgere un dubbio: è meglio che le due iterazioni siano fatte nello stesso caso di test o in casi test separati? Ovvero, è meglio __minimizzare__ i __casi di test__ o le __iterazioni per caso__? \
+Opinione diffusa è quella secondo cui è preferibile __minimizzare le iterazioni__: partizionando le casistiche in diversi casi di test è possibile rilevare con più precisione gli errori, riducendo il tempo di debug.
+In alcune situazioni però aumentare il numero di iterazioni può diminuire il tempo di esecuzione totale dei test, in quanto dovendo riavviare il programma per ciascun caso di test la somma dei tempi di startup può diventare significativa per software molto massicci.
diff --git a/src/13_analisi-statica/04_criteri/03_cammini-du.md b/src/13_analisi-statica/04_criteri/03_cammini-du.md
new file mode 100644
index 0000000000000000000000000000000000000000..57d8510ea9acb06dd4f7a9479c1d180c56490354
--- /dev/null
+++ b/src/13_analisi-statica/04_criteri/03_cammini-du.md
@@ -0,0 +1,14 @@
+# Criterio di copertura dei cammini DU
+
+Nel criterio precedente si richiedeva di testare _un_ cammino da ogni definizione ad ogni suo uso, ma come sappiamo i cammini tra due istruzioni di un programma possono essere molteplici.
+Potrebbe dunque sorgere l'idea di testarli _tutti_: da questa intuizione nasce il __criterio di copertura dei cammini DU__.
+
+$$
+\begin{align*}
+T \in C_{pathDU} \Longleftrightarrow& \forall i \in P, \  \forall x \in \operatorname{def}(i), \ \forall j \in \operatorname{du}(i, \\, x), \\\\
+&\forall \text{ cammino da $i$ a $j$ senza ulteriori definizioni di $x$} \\\\
+& \exists t \in T \ \text{che lo esegue}.
+\end{align*}
+$$
+
+Questo criterio può essere __utile da ipotizzare__, ma a causa dell'esplosione combinatoria del numero dei cammini è considerato __impraticabile__ (_"sopra la barra rossa"_).
diff --git a/src/13_analisi-statica/05_oltre-variabili.md b/src/13_analisi-statica/05_oltre-variabili.md
new file mode 100644
index 0000000000000000000000000000000000000000..819d8eb8231cfecfc3945c5a7f1a394e2130e8fa
--- /dev/null
+++ b/src/13_analisi-statica/05_oltre-variabili.md
@@ -0,0 +1,69 @@
+<!-- KaTeX op macro definitions -->
+<div style="display: none; margin: 0;">
+$$
+\require{color}
+% Regular operations
+\def\op#1{
+  \fcolorbox{black}{white}{$\vphantom{d} \sf{#1}$}
+}
+\def\d{\op{d} \,}
+\def\a{\op{a} \,}
+\def\u{\op{u} \,}
+% Erroneous operations
+\def\opR#1{
+  \fcolorbox{black}{orangered}{$\vphantom{d} \color{white}{\sf{#1}}$}
+}
+\def\dR{\opR{d} \,}
+\def\aR{\opR{a} \,}
+\def\uR{\opR{u} \,}
+% Subscript operations
+\def\Op#1#2{
+  \fcolorbox{black}{white}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\D#1{\Op{d}{#1} \,}
+\def\A#1{\Op{a}{#1} \,}
+\def\U#1{\Op{u}{#1} \,}
+% Warning subscript operations
+\def\OpW#1#2{
+  \fcolorbox{black}{orange}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+% Green subscript operations
+\def\OpG#1#2{
+  \fcolorbox{black}{lightgreen}{$\vphantom{d_6} \sf{#1}_{#2}$}
+}
+\def\DG#1{\OpG{d}{#1} \,}
+\def\AG#1{\OpG{a}{#1} \,}
+\def\UG#1{\OpG{u}{#1} \,}
+% Error
+\def\Err{
+  \color{red}{\sf{ERROR}}
+}
+\def\err{
+  \, \Err
+}
+$$
+</div>
+
+# Oltre le variabili
+
+L'analisi del flusso dati si può estendere anche su altri _"oggetti"_, non solo variabili. \
+Per esempio, è possibile prevedere le seguenti operazioni su un __file__:
+
+- \\(\op{a}\\) (__apertura__): specializzata in _per lettura_ o _per scrittura_;
+- \\(\op{c}\\) (__chiusura__);
+- \\(\op{l}\\) (__lettura__);
+- \\(\op{s}\\) (__scrittura__).
+
+Date queste operazioni si possono individuare una serie di regole, come per esempio:
+
+1. \\(\op{l}\\), \\(\op{s}\\) e \\(\op{c}\\) devono essere precedute da \\(\op{a}\\) senza \\(\op{c}\\) intermedie;
+2. \\(\op{a}\\) deve essere seguita da \\(\op{c}\\) prima di un'altra \\(\op{a}\\);
+3. legami tra tipo di apertura (per lettura o per scrittura) e relative operazioni.
+
+È interessante notare il __legame__ tra l'attività di analisi del flusso di dati e i diagrammi UML a stati finiti: un _oggetto_ risponde a una certa _tipologia di eventi_, può essere in diversi _stati_ e in certi _stati_ non sono ammesse alcune _operazioni_.
+Si noti come nessuna delle due discipline entra comunque nel merito del valore delle variabili, relegato ad un'analisi a runtime.
+
+## Criterio di _copertura del budget_
+
+Molto spesso nei contesti reali l'unico criterio applicato è quello di __copertura del budget__: si continuano a creare casi di test finché non sono finite le risorse (tempo e soldi).
+Questa tecnica ovviamente non fornisce alcuna garanzia sull'efficacia dei test, ed è sicuramente sconsigliata.
diff --git a/src/14_review/00_index.md b/src/14_review/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..f7859269b3e949891e85a498c4fe8a83e72d1547
--- /dev/null
+++ b/src/14_review/00_index.md
@@ -0,0 +1,18 @@
+# Tecniche di review
+
+Finora abbiamo esplorato tecniche più o meno _automatiche_ per la ricerca di errori, che stimolavano il programma con specifici input o ne analizzavano il codice per individuare potenziali anomalie. \
+Tuttavia, alcuni tipi di errori non possono essere rilevati con questi metodi: si tratta soprattutto errori legati a _incomprensione delle specifiche_.
+Del resto, attività come il testing richiedono che il programmatore fornisca l'output "corretto" che si aspetta dal programma che viene confrontato con quello effettivo per scovare eventuali differenze: se chi scrive il codice non comprende in primo luogo _cosa_ dovrebbe fare il suo software non c'è modo di individuare l'errore.
+
+Per questo motivo molto spesso il codice viene sottoposto ad un'attività di __review__, in cui un operatore umano ne analizza la struttura e il funzionamento: egli sarà chiaramente in grado di cogliere una serie di __errori semantici__ che sfuggono alla comprensione dei tool automatici di test.
+Spesso questa mansione viene svolta da un __team di testing__ separato dal team di sviluppo: non solo questo promuove l'effettiva ricerca di errori (_mentre gli sviluppatori avrebbero tutto l'interesse di non trovarne nessuno_), ma sottopone il software a uno sguardo esterno più critico e imparziale.
+
+Anche per la review esistono una serie di tecniche: vediamone quindi le principali.
+
+- [**Bebugging**](./01_bebugging.md)
+- [**Analisi mutazionale**](./02_analisi-mutazionale/00_index.md)
+- [**Object oriented testing**](./03_object-oriented.md)
+- [**Testing funzionale**](./04_testing-funzionale/00_index.md)
+- [**Software inspection**](./05_software-inspection/00_index.md)
+- [**Modelli statistici**](./06_modelli-statistici.md)
+- [**Debugging**](./07_debugging.md)
diff --git a/src/14_review/01_bebugging.md b/src/14_review/01_bebugging.md
new file mode 100644
index 0000000000000000000000000000000000000000..96d1806b4249394757782db805a677a63aacb0fa
--- /dev/null
+++ b/src/14_review/01_bebugging.md
@@ -0,0 +1,11 @@
+# Bebugging
+
+Talvolta può capitare che il team di testing __non trovi errori__ nel programma sotto osservazione.
+Oltre ad essere scoraggiante per chi esegue la review questo è spesso indice del fatto che tale attività non viene svolta in maniera corretta, poiché raramente un programma è effettivamente corretto al 100% dopo la prima scrittura.
+
+Un metodo efficace per risolvere questo problema è il cosiddetto __bebugging__, una tecnica secondo la quale gli sviluppatori __inseriscono deliberatamente \\(\bf{n}\\) errori__ nel codice prima di mandarlo in analisi al team di testing, a cui viene comunicato il numero \\(n\\) di errori da trovare.
+L'ovvio vantaggio di questa tecnica è l'__incentivo__ per il team di testing a continuare a cercare errori, facendo sì che durante la ricerca ne vengano scovati molti altri non ancora noti.
+
+La metrica utilizzata per valutare l'efficacia del testing tramite questa tecnica è dunque la __percentuale di errori trovati__ tra quelli inseriti artificialmente, che può fornire un'indicazione della frazione di errori che il team di testing è in grado di trovare.
+Se per esempio il team di sviluppo ha aggiunto 10 bug _"artificiali"_ e durante il testing ne vengono trovati 8 più 2 non noti, si può supporre che il team di review riesce a trovare l'_80% degli errori_ e che quindi ce ne è ancora un altra porzione di errori _reali_ da scovare. \
+Bisogna però essere molto cauti nel fare considerazioni di questo tipo: è possibile che gli errori immessi artificialmente siano __troppo facili__ o __troppo difficili__ da trovare, per cui conviene sempre prendere tutto con le pinze.
diff --git a/src/14_review/02_analisi-mutazionale/00_index.md b/src/14_review/02_analisi-mutazionale/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..d275327ddb520dab851e879e68885d8a4044f6eb
--- /dev/null
+++ b/src/14_review/02_analisi-mutazionale/00_index.md
@@ -0,0 +1,13 @@
+# Analisi mutazionale
+
+Una evoluzione del bebugging è l'__analisi mutazionale__.
+Dato un programma \\(P\\) e un test \\(T\\) (_insieme di casi di test_), viene generato un insieme di programmi \\(\Pi\\) _simili_ al programma \\(P\\) in esame: tali programmi prendono il nome di __mutanti__. \
+Si esegue poi il test \\(T\\) su ciascun mutante: se \\(P\\) era corretto i programmi in \\(\Pi\\) __devono essere sbagliati__, ovvero devono produrre un __risultato diverso__ per almeno un caso di test \\(t \in T\\).
+Se così non fosse, infatti, vorrebbe dire che il programma \\(P\\) non viene opportunamente testato nell'aspetto in cui si discosta dal mutante che non ha sollevato errori, per cui non si può essere sicuri della sua correttezza.
+Non viene cioè testata la correttezza del programma, ma piuttosto __quanto il test è approfondito__.
+
+Si può quindi valutare la capacità di un test di rilevare le differenze introdotte nei mutanti tramite un nuovo criterio di copertura, che prende il nome di __criterio di copertura dei mutanti__.
+
+- [**Criterio di copertura dei mutanti**](./01_copertura-mutanti.md)
+- [**Generazione dei mutanti**](./02_generazione.md)
+- [**Automazione**](./03_automazione.md)
diff --git a/src/14_review/02_analisi-mutazionale/01_copertura-mutanti.md b/src/14_review/02_analisi-mutazionale/01_copertura-mutanti.md
new file mode 100644
index 0000000000000000000000000000000000000000..2bc30769a8e1b6fa4be119196fbc46e59b4db955
--- /dev/null
+++ b/src/14_review/02_analisi-mutazionale/01_copertura-mutanti.md
@@ -0,0 +1,14 @@
+# Criterio di copertura dei mutanti
+
+_Un test \\(\ T\\) soddisfa il __criterio di copertura dei mutanti__ se e solo se per ogni mutante \\(\pi \in \Pi\\) esiste almeno un caso di test \\(t \in T\\) la cui esecuzione produca per \\(\pi\\) un risultato diverso da quello prodotto da \\(P\\)_.
+
+La metrica di valutazione di questo criterio è la __frazione di mutanti \\(\pi\\) riconosciuta come diversa__ da \\(P\\) sul totale di mutanti generati.
+Se non tutti i mutanti vengono scovati sarà necessario aggiungere dei casi di test che li riconoscano.
+
+I tre passi da seguire per costruire un test tramite l'analisi mutazionale sono quindi:
+
+1. __analisi__ delle classi e generazione dei mutanti;
+2. __selezionare__ dei casi di test da aggiungere a \\(T\\), in base alla metrica di cui sopra;
+3. __esecuzione__ dei casi di test sui mutanti, pensando anche alle performance;
+
+Analizziamo ciascuno di tali step in maggior dettaglio.
diff --git a/src/14_review/02_analisi-mutazionale/02_generazione.md b/src/14_review/02_analisi-mutazionale/02_generazione.md
new file mode 100644
index 0000000000000000000000000000000000000000..6726393a262e69cd6ca75f0a00255bbb50f1721e
--- /dev/null
+++ b/src/14_review/02_analisi-mutazionale/02_generazione.md
@@ -0,0 +1,36 @@
+# Generazione dei mutanti
+
+Idealmente i mutanti generati dovrebbero essere il __meno differenti possibile__ dal programma di partenza, ovvero dovrebbe esserci __un mutante per ogni singola anomalia__ che sarebbe possibile inserire nel programma.
+
+Questo richiederebbe però di generare __infiniti__ mutanti, mentre per mantenere la suite di test _eseguibile in tempi ragionevoli_ il numero di mutanti non dovrebbe essere troppo elevato: un centinaio è una buona stima, ma un migliaio sarebbe auspicabile. \
+Visto il numero limitato è necessario dunque concentrarsi sulla "__qualità__" dei mutanti generati, dove i mutanti sono tanto più buoni quanto più permettono di scovare degli errori.
+Per questo motivo vengono creati degli specifici _operatori_ che dato un programma restituiscono dei mutanti _utili_.
+
+## Operatori mutanti
+
+Come già accennato, gli __operatori mutanti__ sono delle funzioni (_o piccoli programmi_) che dato un programma \\(P\\) generano un insieme di mutanti \\(\Pi\\).
+Essi operano eseguendo piccole __modifiche sintattiche__ che modifichino la __semantica del programma__ senza però causare errori di compilazione.
+
+Tali operatori si distinguono in __classi__ in base agli oggetti su cui operano:
+
+- __costanti__ e __variabili__, per esempio scambiando l'occorrenza di una con l'altra;
+- __operatori__ ed __espressioni__, per esempio trasformando `<` in `<=`, oppure `true` in `false`;
+- __comandi__, per esempio trasformando un `while` in `if`, facendo così eseguire il ciclo una sola volta.
+
+Alcuni operatori possono essere anche specifici su alcuni tipi di applicazioni, come nel caso di:
+
+- operatori per __sistemi concorrenti__: operano principalmente sulle primitive di sincronizzazione – come eseguire una `notify()` invece che una `notifyAll()`;
+- operatori per __sistemi object-oriented__: operano principalmente sulle interfacce dei moduli.
+
+Poiché la generazione dei mutanti è un'attività tediosa, il compito di applicare questi operatori viene spesso affidato a tool automatici.
+Esistono però numerosi __problemi di prestazioni__, in quanto per ogni mutante occorre modificare il codice, ricompilarlo, controllare che non si sovrapponga allo spazio di compilazione delle classi di altri mutanti e fare una serie di altre operazioni che comportano un pesante overhead.
+Per questo motivo i tool moderni lavorano spesso sull'__eseguibile__ in sé (_sul bytecode nel caso di Java_): sebbene questo diminuisca il lavoro da fare per ogni mutante è possibile che il codice eseguibile così ottenuto sia un programma che non sarebbe possibile generare tramite compilazione.
+Si espande quindi l'universo delle possibili anomalie anche a quelle _non ottenibili_, un aspetto che bisognerà tenere in considerazione nella valutazione della metrica di copertura.
+
+## High Order Mutation
+
+Normalmente i mutanti vengono generati introducendo una _singola modifica_ al programma originale.
+Nella variante __HOM__ (__High Order Mutation__) si applicano invece modifiche a __codice già modificato__.
+
+La giustificazione per tale tecnica è che esistono alcuni casi in cui trovare errori dopo aver applicato più modifiche è _più difficile_ rispetto ad applicarne solo una.
+Può essere che un errore mascheri parzialmente lo stato inconsistente dell'altro rendendo più difficile il rilevamento di malfunzionamenti, cosa che porta a generare test ancora più approfonditi.
diff --git a/src/14_review/02_analisi-mutazionale/03_automazione.md b/src/14_review/02_analisi-mutazionale/03_automazione.md
new file mode 100644
index 0000000000000000000000000000000000000000..b60099faf32cfbf76ba342b32d47b340a222d7ad
--- /dev/null
+++ b/src/14_review/02_analisi-mutazionale/03_automazione.md
@@ -0,0 +1,25 @@
+# Automazione
+
+Generalmente nel testing gli unici due _outcomes_ sono _risultato corretto_ o _non corretto_ e la metrica è una misura della correttezza del programma.
+Il discriminante delle tecniche di analisi mutazionale è invece il numero di casi di test che forniscono un risultato ___diverso___ da quello di \\(P\\), indipendentemente dalla correttezza (di entrambi).
+
+Come già detto, trovare errori con queste tecniche (specialmente l'HOM) misura quindi il __livello di approfondimento__ dei casi di test e __non__ la __correttezza__ del programma di partenza. \
+Prescindere dalla _correttezza_ dei risultati ha però un aspetto positivo: per eseguire l'analisi mutazionale non è necessario conoscere il comportamento corretto del programma, eliminando la necessità di un _oracolo_ che ce lo fornisca.
+Si può quindi misurare la bontà di un insieme casi di test __automatizzando la loro creazione__: come già detto precedentemente, occorre però vigilare sulla __proliferazione del numero di esecuzioni__ da effettuare per completare il test – un caso di test dà infatti origine a \\(n+1\\) esecuzioni, dove \\(n\\) è il numero di mutanti.
+
+Il seguente diagramma di flusso visualizza quindi l'attività __facilmente automatizzabile__ di analisi mutazionale:
+
+![Schema analisi mutazionale](/assets/13_analisi-mutazionale-schema.png)
+
+Benché semplice, questo algoritmo __non garantisce la terminazione__ per una serie di motivi:
+
+- quando si estrae un caso di test casuale, c'è sempre il rischio di __estrarre sempre lo stesso__;
+- si potrebbe essere _particolarmente sfortunati_ e __non trovare un caso di test utile__ in tempo breve;
+- __esistono infinite varianti__ di programmi __funzionalmente identici__ ma __sintatticamente diversi__, ovvero che svolgono la stessa funzione anche se sono diversi: una modifica sintattica potrebbe non avere alcun effetto sul funzionamento del programma, come per esempio scambiare `<` con `<=` in un algoritmo di ordinamento.
+  In tal caso, nessun nuovo caso di test permetterebbe di coprire il mutante, in quanto esso restituirebbe sempre lo stesso output del programma originale.
+
+Spesso viene quindi posto un timeout sull'algoritmo dipendente sia dal tempo totale di esecuzione, sia dal numero di casi di test estratti.
+
+Per verificare la validità del test, è necessario controllare il __numero di mutanti generati__: se questo numero è elevato, il test non era affidabile.
+In alternativa, è possibile _"nascondere"_ i mutanti, a patto che non sia richiesta una copertura totale.
+In questo modo, è possibile __analizzare programmi__ che sono __funzionalmente uguali ma sintatticamente diversi__, al fine di dimostrarne l'equivalenza o scoprire casi in cui essa non è valida.
diff --git a/src/14_review/03_object-oriented.md b/src/14_review/03_object-oriented.md
new file mode 100644
index 0000000000000000000000000000000000000000..69fefff576b4cf8e0173860512768228014d3992
--- /dev/null
+++ b/src/14_review/03_object-oriented.md
@@ -0,0 +1,63 @@
+# Object-oriented testing
+
+Finora abbiamo trattato i programmi come funzioni matematiche da un dominio di input a un dominio di output.
+Questo è tutto sommato vero per quanto riguarda i __linguaggi procedurali__: un programma in tale paradigma è composto da un insieme di funzioni e procedure che preso un dato in ingresso ne restituiscono uno in uscita.
+A meno di eventuali variabili globali condivise (il cui uso è comunque sconsigliato), tali funzioni sono indipendenti l'una dall'altra, e possono quindi essere _testate indipendentemente_ come fossero dei piccoli sotto-programmi.
+
+La situazione cambia per quanto riguarda invece i __linguaggi object oriented__ (__OO__), che introducono i concetti di classe e istanza: in tali linguaggi gli oggetti sono l'__unione di metodi e stato__.
+Le tecniche di testing viste finora smettono quindi di funzionare: la maggior parte delle volte testare i metodi isolatamente come funzioni da input ad output perde di senso, in quanto non si considera il contesto (lo _stato_ dell'oggetto associato) in cui essi operano.
+
+Bisogna dunque sviluppare una serie di tecniche di test specifiche per i linguaggi orientati agli oggetti, in cui l'__unità testabile__ si sposti dalle procedure alle __classi__.
+
+## Ereditarietà e collegamento dinamico
+
+Prima di capire _come_ è possibile testare un'intera classe, affrontiamo due punti critici che derivano dal funzionamento intrinseco dei linguaggi a oggetti: l'__ereditarietà__ e il __collegamento dinamico__.
+
+Partiamo dal primo e immaginiamo di avere una classe già completamente testata.
+Creando ora una sottoclasse di tale classe originale può sorgere un dubbio: _visto che i metodi ereditati sono già stati testati nella classe genitore ha senso testarli nella classe figlia?_
+Un quesito simile sorge nel caso di metodi di default appartenenti a un'interfaccia: _ha senso testare i metodi di default direttamente nell'interfaccia o è meglio testarli nelle classi concrete che implementano tale interfaccia?_ \
+Il consenso degli esperti è di __testare nuovamente tutti i metodi ereditati__: nelle sottoclassi e nelle classi che implementano delle interfacce con metodi di default tali metodi opereranno infatti in __nuovi contesti__, per cui non vi è alcuna certezza che funzionino ancora a dovere.
+Inoltre, a causa del collegamento dinamico non è nemmeno sicuro che eseguire lo stesso metodo nella classe base significa eseguire le stesse istruzioni nella classe ereditata. \
+In generale dunque non si eredita l'attività di testing, ma si possono invece ereditare i casi di test e i relativi valori attesi (_l'oracolo_): è perciò opportuno __rieseguire__ i casi di test anche nelle sottoclassi.
+
+Un altro motivo per cui il testing object-oriented differisce fortemente da quello per linguaggi funzionali è la preponderanza del __collegamento dinamico__, attraverso il quale le chiamate ai metodi vengono collegate a runtime in base al tipo effettivo degli oggetti.
+Dal punto di vista teorico, infatti, tale meccanismo rende difficile stabilire staticamente tutti i possibili cammini di esecuzione, complicando la determinazione dei criteri di copertura.
+
+## Testare una classe
+
+Entriamo ora nel vivo della questione.
+Per __testare una classe__:
+
+- la __si isola__ utilizzando più _classi stub_ possibili per renderla eseguibile indipendentemente dal contesto;
+- si implementano eventuali __metodi astratti__ o non ancora implementati (stub);
+- si aggiunge una funzione per permettere di estrarre ed esaminare lo stato dell'oggetto e quindi __bypassare l'incapsulamento__;
+- si costruisce una classe driver che permetta di istanziare oggetti e chiamare i metodi secondo il __criterio di copertura__ scelto.
+
+Ebbene sì, sono stati progettati dei criteri di copertura specifici per il testing delle classi.
+Vediamo dunque di cosa si tratta.
+
+### Copertura della classe
+
+I __criteri classici__ visti precedentemente (comandi, decisioni, ...) continuano a valere ma __non sono sufficienti__.
+Per testare completamente una classe occorre considerare lo __stato dell'oggetto__: in particolare, è comodo utilizzare una __macchina a stati__ che rappresenti gli _stati possibili_ della classe e le relative _transazioni_, ovvero le chiamate di metodi che cambiano lo stato.
+
+Tale rappresentazione potrebbe esistere nella documentazione o essere creato specificatamente per l'attività di testing.
+Il seguente diagramma rappresenta per esempio una macchina a stati di una classe avente due metodi, \\(\mathtt{m1}\\) e \\(\mathtt{m2}\\).
+
+![Grafo criteri di copertura](/assets/13_criteri-copertura-grafo.png)
+
+Ottenuta una rappresentazione di questo tipo, alcuni criteri di copertura che si possono ipotizzare sono:
+
+- __coprire tutti i nodi__: per ogni __stato__ dell'oggetto deve esistere almeno un caso di test che lo raggiunge;
+- __coprire tutti gli archi__: per ogni stato e per ogni metodo deve esistere almeno un caso di test che esegue tale metodo trovandosi in tale stato;
+- __coprire tutte le coppie di archi input/output__: per ogni stato e per ogni coppia di archi entranti e uscenti deve esistere almeno un caso di test che arriva nello stato tramite l'arco entrante e lo lascia tramite l'arco uscente (consideriamo anche _come_ siamo arrivati nello stato);
+- __coprire tutti i cammini identificabili nel grafo__: spesso i cammini in questione sono infiniti, cosa che rende l'applicazione di questo criterio infattibile (_"sopra la linea rossa"_).
+
+### Tipo di testing: white o black box?
+
+Abbiamo assunto che il diagramma degli stati facesse parte delle specifiche del progetto.
+Se così fosse, allora il testing appena descritto assume una connotazione __black box__:  il diagramma rappresenta sì la classe ma è ancora una sua __astrazione__, che non considera il codice effettivo che rappresenta lo stato o che implementa uno specifico metodo ma solo le relazioni tra i vari stati.
+
+In caso il diagramma degli stati non sia però fornito, il testing delle classi è comunque possibile!
+Attraverso tecniche di __reverse engineering__ guidate da certe euristiche (che operano ad un livello di astrazione variabile) è possibile ad __estrarre informazioni sugli stati__ di una _classe già scritta_; spesso tali informazioni non sono comprensibili per un essere umano, motivo per cui esse vengono piuttosto utilizzate da vari tool di testing automatico.
+In questo caso, però, il testing assume caratteristiche __white box__, in quanto il codice che implementava la classe era già noto prima di iniziare a testarlo.
diff --git a/src/14_review/04_testing-funzionale/00_index.md b/src/14_review/04_testing-funzionale/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..45431195a61f5c15092bbe3c7e49ca7159ca9625
--- /dev/null
+++ b/src/14_review/04_testing-funzionale/00_index.md
@@ -0,0 +1,26 @@
+# Testing funzionale
+
+Introduciamo ora una nuova attività di testing che parte da presupposti completamente diversi rispetto a quelli del test strutturale.
+
+Il __test funzionale__ è infatti un tipo di test che si concentra sulla verifica del comportamento del programma dal punto di vista dell'__utente finale__, senza considerare il suo funzionamento interno.
+In altre parole, il test funzionale è un approccio __black box__ in cui non si ha (o non comunque non si sfrutta) la conoscenza del codice sorgente.
+
+Talvolta questo può essere l'__unico approccio possibile__ al testing, come nel caso di validazione del lavoro di un committente esterno; altre volte invece si decide volontariamente di farlo, concentrandosi sul __dominio delle informazioni__ invece che sulla struttura di controllo. \
+Il test funzionale, che prende in considerazione le __specifiche__ (e non i requisiti) del progetto per discriminare un comportamento corretto da uno scorretto, permette infatti di identificare errori che __non possono essere individuati__ con criteri strutturali, come per esempio funzionalità non implementate, flussi di esecuzione dimenticati o errori di interfaccia e di prestazioni.
+
+Le tecniche di test funzionale si possono raggruppare in:
+
+- __metodi basati su grafi__: oltre alle tecniche già viste in precedenza, si può per esempio lavorare anche sui diagrammi di sequenza;
+- __suddivisioni del dominio in classi di equivalenza__: si possono raggruppare i valori del dominio che causano lo stesso comportamento in classi d'equivalenza, così da testare tutti i _comportamenti distinti_ piuttosto che tutti i possibili valori del dominio.
+Occorre fare attenzione a non fare l'inverso, ovvero a concentrarsi sui soli valori appartenenti ad una classe di equivalenza ignorando il resto;
+- __analisi dei valori limite (test di frontiera)__: si testano, tra tutti i possibili valori del dominio, quelli _"a cavallo"_ tra una categoria e l'altra, in quanto essi possono più facilmente causare malfunzionamenti;
+- __collaudo per confronto__: si confronta la nuova versione del programma con la vecchia, assicurandosi che non siano presenti regressioni.
+Non solo si possono confrontare gli eseguibili, ma anche _specifiche formali eseguibili_ che rappresentino le caratteristiche importanti del software;
+
+Non tutte le metodologie di testing funzionale ricadono però in una di queste categorie, e la più notevole è sicuramente il __testing delle interfacce__, di cui diamo un assaggio prima di passare a parlare di classi di equivalenza.
+
+- [**Testing delle interfacce**](./01_interfacce.md)
+- [**Classi di equivalenza**](./02_classi-equivalenza.md)
+- [**Test di frontiera**](./03_test-frontiera.md)
+- [**Category partition**](./04_category-partition.md)
+- [**Object orientation e testing funzionale**](./05_object-orientation.md)
diff --git a/src/14_review/04_testing-funzionale/01_interfacce.md b/src/14_review/04_testing-funzionale/01_interfacce.md
new file mode 100644
index 0000000000000000000000000000000000000000..46bd13d3d7c373ddccebb19328a26e364bc1a2a9
--- /dev/null
+++ b/src/14_review/04_testing-funzionale/01_interfacce.md
@@ -0,0 +1,12 @@
+# Testing delle interfacce
+
+Questa tecnica mira a testare come i vari sotto-sistemi del programma dialoghino e __collaborino__ tra loro: per "interfacce" non si intendono infatti le `interface` Java o le _signature_, ma l'insieme di funzionalità che permettono l'interoperabilità dei componenti. \
+Esistono in particolare diversi tipi di interfacce:
+
+- a __invocazione di parametri__;
+- a __condivisione di memoria__;
+- a __metodi sincroni__;
+- a __passaggio di messaggi__.
+
+Le interfacce aderenti a ciascuna categoria possono essere analizzate in modi diversi alla ricerca di anomalie.
+Gli sbagli più comuni sono per esempio __errori nell'uso dell'interfaccia__, come il passaggio di parametri in ordine o tipo errato oppure assunzioni sbagliate circa ciò che le funzionalità richiedono (_precondizioni_), ed __errori di tempistica o di sincronizzazione__ tra componenti.
diff --git a/src/14_review/04_testing-funzionale/02_classi-equivalenza.md b/src/14_review/04_testing-funzionale/02_classi-equivalenza.md
new file mode 100644
index 0000000000000000000000000000000000000000..29d39b7a50c304015826247d095916bfaf85069e
--- /dev/null
+++ b/src/14_review/04_testing-funzionale/02_classi-equivalenza.md
@@ -0,0 +1,31 @@
+# Classi di equivalenza
+
+La tecnica delle classi di equivalenza si pone l'obiettivo di dividere il dominio del programma in __classi di dati__, ovvero gruppi di valori di input che _dovrebbero_ __stimolare il programma nella stessa maniera__.
+Non si tratta quindi di classi di equivalenza degli output, ovvero valori che dati in pasto al programma forniscono lo stesso risultato, quanto piuttosto valori che dati in pasto al programma forniscono un risultato diverso ma _prodotto nello stesso modo_.
+
+Una volta individuate le classi di dati l'obiettivo sarebbe quindi di estrarre da esse casi di test in modo da testare il funzionamento del programma in tutti suoi funzionamenti standard. \
+In realtà, dunque, si cercano di individuare casi di test che rivelino eventuali __classi di equivalenza di errori__, ovvero insiemi di valori che generano malfunzionamenti per lo stesso motivo.
+Classi di equivalenza di questo tipo sono solitamente più _"stabili"_ rispetto alle normali classi di equivalenza in quanto il risultato ottenuto, ovvero l'errore, è spesso lo stesso o molto simile.
+
+Volendo dare una definizione più formale, _una __classe di equivalenza__ rappresenta un insieme di __stati validi o non validi__ per i dati in input e un insieme di stati validi per i dati di output_, dove per dato si intendono valori, intervalli o insiemi di valori correlati. \
+È importante comprendere anche i possibili _stati non validi_ in quanto bisogna testare che il programma reagisca bene all'input mal formattato.
+Ogni dominio avrà quindi almeno due classi di equivalenza:
+
+- la classe degli __input validi__
+- la classe degli __input non validi__
+
+Per fare un esempio si può considerare un programma che chiede in input un __codice PIN di 4 cifre__.
+Il suo dominio può quindi essere suddiviso in due semplici classi di equivalenza:
+
+1. PIN corretto;
+2. tutti i numeri di 4 cifre diversi dal PIN.
+
+Volendo fare un altro esempio, se ci si aspetta che i valori in input ricadano in un intervallo, per esempio \\([100, \\, 700]\\)), si possono definire la classe di equivalenza valida \\(x \in [100, 700]\\) e la classe di equivalenza non valida \\(x \notin [100, 700]\\).
+Per voler aumentare la granularità si può però spezzare la classe degli input non validi in due, ottenendo una classe valida e due non valide:
+
+1. \\(x \in [100, 700]\\);
+2. \\(x < 100\\);
+3. \\(x > 700\\).
+
+Come si vede, la scelta delle classi di equivalenza da considerare non è univoca, e richiede un minimo di conoscenza di dominio.
+Alternativamente esistono delle tecniche standard di individuazione delle classi di equivalenza a partire dalle specifiche che prendono il nome di __category partition__.
diff --git a/src/14_review/04_testing-funzionale/03_test-frontiera.md b/src/14_review/04_testing-funzionale/03_test-frontiera.md
new file mode 100644
index 0000000000000000000000000000000000000000..b312269177980ca1eeff2302693d3f5d3ff1c1af
--- /dev/null
+++ b/src/14_review/04_testing-funzionale/03_test-frontiera.md
@@ -0,0 +1,4 @@
+# Test di frontiera
+
+La tecnica dei __test di frontiera__ è _complementare_ a quella delle classi di equivalenza.
+Partendo dal presupposto che gli errori tendono ad accumularsi sui __casi limite__, ovvero quelli la cui gestione è più particolare, questa tecnica suggerisce di selezionare come casi di test non valori a caso all'interno delle classi di equivalenza, ma i valori presenti __al confine__ tra di loro.
diff --git a/src/14_review/04_testing-funzionale/04_category-partition.md b/src/14_review/04_testing-funzionale/04_category-partition.md
new file mode 100644
index 0000000000000000000000000000000000000000..2413444f24919d8d0c16a2f43e43f42870cdba53
--- /dev/null
+++ b/src/14_review/04_testing-funzionale/04_category-partition.md
@@ -0,0 +1,166 @@
+# Category partition
+
+La tecnica di __category partition__ è un metodologia che permette di caratterizzare e identificare le classi di equivalenza del dominio di un problema a partire dalle sue specifiche.
+Può essere utilizzata a __vari livelli__ a seconda che si debbano realizzare test di unità, test di integrazione e o test funzionali.
+
+Il metodo è composto da una serie di passi in sequenza:
+
+1. __analisi delle specifiche__: in questa fase vengono identificate le _unità funzionali individuali_ che possono essere verificate singolarmente; non necessariamente sono un'unica classe, è sufficiente che siano componenti facilmente separabili dal resto, sia a livello di testing che concettuale.
+Per ogni unità vengono quindi identificate delle caratteristiche (__categorie__) dei parametri e dell'ambiente in cui opera;
+2. __scegliere dei valori__: per ogni categoria, occorre scegliere quali sono i _valori sensati_ su cui fare riferimento;
+3. __determinare eventuali vincoli tra le scelte__, che non sono sempre indipendenti;
+4. __scrivere test e documentazione__.
+
+Per capire meglio ciascuna di tali fasi vediamo un'esempio di utilizzo della tecnica di _category partition_ prendendo come soggetto il comando `find` della shell Linux.
+
+## PASSO 1 – analizzare le specifiche
+
+Per prima cosa analizziamo le specifiche del comando:
+
+> __Syntax__: `find <pattern> <file>`
+>
+> The find command is used to locate one or more instances of a given pattern in a file.
+> All lines in the file that contain the pattern are written to standard output.
+> A line containing the pattern is written only once, regardless of the number of times the pattern occur in it.
+>
+> The pattern is any sequence of characters whose length does not exceed the maximum length of a line in the file.
+> To include a blank in the pattern, the entire pattern must be enclosed in quotes (`"`).
+> To include a quotation mark in the pattern, two quotes (`""`) in a row must be used.
+
+Vista la relativa semplicità, `find` è un'unità funzionale individuale che può essere verificata separatamente.
+Bisogna dunque individuarne i parametri: come è chiaro dalla sintassi essi sono due, il `pattern` da cercare e il `file` in cui farlo.
+
+Ora, ciascuno di tali parametri può possedere determinate caratteristiche, ed è nostro compito in questa fase comprenderle ed estrarle. \
+Tali caratteristiche possono essere di due tipi: __esplicite__, ovvero quelle ricavabili direttamente dalla lettura specifiche, e __implicite__, ovvero quelle che provengono dalla nostra conoscenza del dominio di applicazione e che quindi non vengono specificate.
+
+Tornando al nostro caso di studio possiamo per esempio ottenere la seguente tabella:
+
+<style>
+  #category-partition-find-table ul {
+    margin-bottom: 0;
+  }
+</style>
+
+<table id="category-partition-find-table">
+<tr>
+  <th>Oggetto</th>
+  <th>Caratteristiche esplicite</th>
+  <th>Caratteristiche implicite</th>
+</tr>
+<tr>
+  <th>
+  
+`pattern`
+  </th>
+  <td>
+
+- lunghezza del pattern;
+- pattern tra doppi apici;
+- pattern contenente spazi;
+- pattern contenente apici.
+</td>
+  <td>
+
+- pattern tra apici con/senza spazi;
+- più apici successivi inclusi nel pattern.
+</td>
+</tr>
+  <th>
+  
+`file` \
+(nome)
+  </th>
+  <td style="text-align: center;"><i>(nessuna)</i></td>
+  <td>
+
+- caratteri nel nome ammissibili o meno;
+- file esistente (con permessi di lettura) o meno.
+</td>
+<tr>
+  <th>
+  
+`file` \
+(contenuto)
+  </th>
+  <td>
+
+- numero occorrenze del pattern nel file;
+- massimo numero di occorrenze del pattern in una linea;
+- massima lunghezza linea.
+</td>
+  <td>
+
+- pattern sovrapposti;
+- tipo del file.
+</td>
+</tr>
+</table>
+
+È importante _esplicitare le caratteristiche implicite_ dei parametri dell'unità funzionale perché __le specifiche non sono mai complete__ e solo così possiamo disporre di tutti gli elementi su cui ragionare nelle fasi successive.
+
+Si presti poi attenzione alla distinzione fatta tra il _nome_ del file e il suo _contenuto_: il primo infatti è un __parametro__ che viene passato al comando per iniziarne l'esecuzione, mentre il secondo fa parte dell'__ambiente__ in cui il comando opera ed è dunque soggetto ad una sottile distinzione concettuale.
+
+### <big>ALPHA E BETA TESTING</big>
+
+Spesso, però, analizzare le specifiche non basta per comprendere tutte le variabili che entrano in gioco durante l'esecuzione di un programma.
+Bisogna infatti ricordare che ci sono moltissime altre caratteristiche d'ambiente che ancora __non sono state considerate__: la versione del sistema operativo, del browser, il tipo di architettura della macchina su cui gira il programma eccetera.
+
+Spesso, quindi, la fase di testing funzionale si divide in due fasi:
+
+- __alpha testing__: l'unità funzionale viene testata in-house, ovvero su una macchina all'interno dello studio di sviluppo.
+In questa fase si considerano soprattutto le caratteristiche legate alle specifiche di cui sopra;
+- __beta testing__: per testare varie _configurazioni d'ambiente_ una versione preliminare del programma viene distribuito in un _ambiente variegato_ per osservare come esso si comporta sulle macchine di diversi utenti.
+
+Per il momento, però, consideriamo solo la fase di alpha testing e le categorie ad essa relative.
+
+## PASSO 2 – scegliere dei valori
+
+Individuate le caratteristiche dei parametri e delle variabili d'ambiente da cui l'unità funzionale dipende, che prendono il nome di __categorie__, si passa quindi alla seconda fase.
+
+In questa fase si devono identificati __tutti e i soli__ _casi significativi_ per ogni categoria, ovvero quei valori della stessa che si ritiene abbia senso testare; poiché si tratta di un compito molto soggettivo è importante in questa fase avere __esperienza__ (_know-how_) nel dominio d'applicazione.
+
+Per capire meglio di cosa stiamo parlando ritorniamo al nostro esempio e consideriamo il parametro `pattern`.
+Per ciascuna delle sue categorie possono essere individuati vari casi significativi:
+
+- __lunghezza del pattern__: vuoto, un solo carattere, più caratteri, più lungo di almeno una linea del file;
+- __presenza di apici__: pattern tra apici, pattern non tra apici, pattern tra apici errati;
+- __presenza di spazi__: nessuno spazio nel pattern, uno spazio nel pattern, molti spazi nel pattern;
+- __presenza di apici interni__: nessun apice nel pattern, un apice nel pattern, molti apici nel pattern.
+
+È interessante notare il _mantra_ già visto del "__nessuno__, __uno__, __molti__", spesso molto utile in questa fase.
+
+## PASSO 3 – determinare i vincoli tra le scelte
+
+Trovati tutti i valori significativi delle categorie dell'unità funzionale come possiamo costruire i casi di test da utilizzare per verificarne la correttezza?
+
+Si potrebbe pensare di testare __tutte le combinazioni__ di valori significativi, facendo cioè il prodotto cartesiano tra le categorie.
+Nella pratica, però, ciò risulterebbe in un numero esagerato di casi di test: già solo nel nostro semplice esempio questi sarebbero ben 1944, decisamente __troppi__.
+
+Nel tentativo di evitare quest'esplosione combinatoria ci si accorge però che spesso le anomalie sorgono dall'interazione di __coppie__ di caratteristiche indipendentemente dal valore assunto da tutte le altre: per esempio, un problema potrebbe presentarsi se si usa il browser _Edge_ sul sistema operativo _Linux_, indipendentemente da caratteristiche quali la dimensione dello schermo, l'architettura del processore eccetera. \
+Per ridurre il numero di casi di test si sviluppa quindi la tecnica del ___pairwise testing___, che riduce l'insieme delle configurazioni da testare a tutte le combinazioni di coppie di valori.
+È quindi presente almeno un caso di test _per ogni coppia ipotizzabile_ di valori: in rete e in Java sono presenti diversi [__strumenti__](https://www.pairwise.org/tools.html) che permettono di creare casi di test combinati con il metodo _pairwise_.
+
+Un'ulteriore tentativo di ridurre il numero di casi di test prevede di definire una serie di ___vincoli___ per la generazione delle coppie, escludendo particolari combinazioni di caratteristiche: così, per esempio si potrebbe escludere la coppia "OS == MacOs" e "browser == Edge" perché sfruttando la conoscenza di dominio sappiamo che tale browser non è disponibile sul suddetto sistema operativo. \
+Volendo essere più precisi, la creazione di vincoli prevede un passaggio intermedio: vengono definite una serie di __proprietà__ (es. _NotEmpty_ o _Quoted_ per l'esempio su `find`) e si creano dei vincoli logici a partire da esse.
+I vincoli seguono poi una struttura tra le seguenti:
+
+- __se__: si può limitare l'uso di un valore solo ai casi in cui è definita una proprietà. Per esempio, è inutile testare il caso _"il file non esiste"_ se la proprietà _NotEmpty_ si manifesta;
+- __single__: alcune caratteristiche prese singolarmente anche se combinate con altre generano lo stesso risultato. Per esempio, se il file non contiene occorrenze del pattern cercato il risultato del programma è indipendente dal tipo di pattern cercato;
+- __error__: alcune caratteristiche generano semplicemente errore, come per esempio se si omette un parametro.
+
+## PASSO 4 – scrivere i test
+
+Fissati i vincoli e fatti i calcoli combinatori si procede ad enumerare iterativamente tutti i casi di test generati continuando ad aggiungere vincoli fino ad arrivare ad un __numero ragionevole__.
+
+Ovviamente, i casi di test avranno poi bisogno di __valori specifici__ per le caratteristiche: non basta dire "pattern con apici all'interno", bisogna creare un pattern aderente a questa descrizione!
+Fortunatamente questa operazione è solitamente molto facile, anche con tool automatici.
+
+## Conclusioni
+
+Per quanto intuitiva e utile, la tecnica di category partition presenta due criticità:
+
+- individuare i __casi significativi__ delle varie caratteristiche può essere difficile e si può sbagliare, anche utilizzando mantra come "_zero_, _uno_, _molti_";
+- una volta generati i casi di test serve comunque un "__oracolo__" che fornisca la risposta giusta, ovvero quella che ci si attende dall'esecuzione sul caso di test.
+L'attività non è dunque _completamente_ automatizzabile.
+
+Va però detto che esistono delle tecniche di __property-based testing__ che cercano di eliminare la necessità di un oracolo considerando particolari proprietà che dovrebbero sempre valere durante l'esecuzione (invarianti) piuttosto che analizzare il risultato dell'esecuzione dei casi di test per determinare la correttezza del programma.
diff --git a/src/14_review/04_testing-funzionale/05_object-orientation.md b/src/14_review/04_testing-funzionale/05_object-orientation.md
new file mode 100644
index 0000000000000000000000000000000000000000..f1043566e906e357b7272d8e35f898cf407b4f9e
--- /dev/null
+++ b/src/14_review/04_testing-funzionale/05_object-orientation.md
@@ -0,0 +1,14 @@
+# Object orientation e testing funzionale
+
+Trattandosi di un approccio __black box__ che ragiona sulle __funzionalità__ e non sui dettagli implementativi, l'introduzione del paradigma a oggetti __non dovrebbe cambiare nulla__ per quanto riguarda il testing funzionale.
+Se questa affermazione è vera per quanto riguarda la verifica di singole unità funzionali, lo stesso non si può dire nel caso di __test di integrazione__.
+
+Nei linguaggi procedurali i test di integrazione sono infatti scritti secondo logiche alternativamente __bottom-up__ o __top-down__: esiste cioè un punto di partenza dal quale partire ad aggregare le componenti, seguendo cioè una qualche forma di __albero di decomposizione__ del programma. \
+Per quanto riguarda la programmazione a oggetti, invece, la situazione è __molto più caotica__: le relazioni tra le classi sono spesso cicliche e non gerarchiche (tranne per l'ereditarietà &mdash; la relazione meno interessante), in una serie di _inter-dipendenze_ che rendono difficoltoso individuare un punto da cui partire a integrare.
+
+Relazioni interessanti in questa fase sono infatti _associazioni_,_aggregazioni_ o _dipendenze_, ma rendono complicato identificare il __sottoinsieme di classi da testare__.
+Per fare ciò si possono comunque utilizzare alcuni strumenti già visti:
+
+- si può partire dai diagrammi degli __use cases e scenari__ per testare i componenti citati;
+- si possono osservare i __sequence diagram__ per testare le classi protagoniste delle interazioni a scambio di messaggi descritte;
+- si possono infine usare gli __state diagram__ nella modalità che abbiamo già descritto.
diff --git a/src/14_review/05_software-inspection/00_index.md b/src/14_review/05_software-inspection/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..c680b3b69c6e808515cefa3ce2e336f90309a01f
--- /dev/null
+++ b/src/14_review/05_software-inspection/00_index.md
@@ -0,0 +1,14 @@
+# Software inspection
+
+Un'altra classe di tecniche di verifica e convalida è la __software inspection__, ovvero tecniche manuali per individuare e correggere gli errori basati su una attività di gruppo in cui si analizza il codice insieme passo passo: si pensi per esempio alla tecnica di _pair programming_ già ampiamente citata parlando di XP.
+
+Le tecniche di software inspection sono molto interessanti in quanto hanno __pochi requisiti__ e l'unico __tool__ richiesto è un essere __umano__ che si prenda la briga ispezionare il codice, spesso in riunioni di gruppo da 5-6 persone.
+
+Trattandosi di una tecnica umana essa è molto __flessibile__: l'__oggetto sotto ispezione__ può essere una porzione di codice non funzionante, una serie di specifiche formali o informali o direttamente l'eseguibile compilato.
+La software inspection può quindi essere eseguita durante tutte le fasi del ciclo di vita di un programma.
+
+- [**Fagan code inspection**](./01_fagan.md)
+- [**Automazione**](./02_automazione.md)
+- [**Pro e contro**](./03_pro-contro.md)
+- [**Confronto tra tecniche di verifica e convalida**](./04_verifica-convalida.md)
+- [**Gruppi di test autonomi**](./05_gruppi-test.md)
diff --git a/src/14_review/05_software-inspection/01_fagan.md b/src/14_review/05_software-inspection/01_fagan.md
new file mode 100644
index 0000000000000000000000000000000000000000..abf53b7ab307b8f3e06c2dc6c56272b78acb1a7f
--- /dev/null
+++ b/src/14_review/05_software-inspection/01_fagan.md
@@ -0,0 +1,87 @@
+# Fagan code inspection
+
+La __Fagan code inspection__ è una metodologia sviluppata da Michael Fagan alla IBM negli anni '70.
+La metodologia prevede che un __gruppo di esperti__ esegua una serie di passi per verificare la correttezza del codice sorgente al fine di individuare eventuali errori, incongruenze o altri problemi. \
+È __la più diffusa__ tra le tecniche di ispezione, nonché la più rigorosa e definita.
+
+## Ruoli
+
+Essendo un'attività di gruppo, nella Fagan code inspection vengono identificati diversi ruoli:
+
+- __Moderatore__: è colui che coordina i meeting, sceglie i partecipanti e ha la responsabilità di far rispettare le regole di cui parleremo tra poco.
+  È di solito una persona che lavora ad un progetto diverso da quello in esame in modo da evitare conflitti di interessi.
+- __Readers e Testers__: non sono persone diverse, semplicemente a seconda dei momenti i partecipanti possono coprire uno di questi due ruoli: i primi leggono il codice al gruppo, mentre i secondi cercano difetti al suo interno.
+  La lettura del codice è una vera e propria _parafrasi_ di esso, ovvero un'interpretazione del codice nella quale si spiega quello che fa ma seguendo comunque la sua struttura.
+- __Autore__: è colui che ha scritto il codice sotto ispezione; è un partecipante passivo che risponde solo a eventuali domande.
+  È simile al ruolo del _cliente_ nell'eXtreme Programming: pronto a rispondere a qualsiasi domanda per accelerare il lavoro degli altri.
+
+## Processo
+
+Definiti i ruoli, secondo la tecnica __Fagan__ di ispezione del codice il processo si articola come segue:
+
+1. __Planning__: in questa prima fase il moderatore sceglie i partecipanti, si definiscono i loro ruoli e il tempo da dedicare alla ispezione, pianificando anche i vari incontri.
+2. __Overview__: viene fornito a tutti i partecipanti materiale sul progetto per permettere loro di farsi un'idea del contesto in cui l'oggetto dell'ispezione si inserisce in ottica della riunione vera e propria.
+3. __Preparation__: i partecipanti _"offline"_ comprendono il codice e la sua struttura autonomamente sulla base anche del materiale distribuito nella fase precedente;
+4. __Inspection__: la vera e propria fase di ispezione.
+  In questa fase si verifica che il codice soddisfi le regole definite in precedenza e si segnalano eventuali problemi o anomalie.
+  Durante l'ispezione, il gruppo di esperti esamina il codice riga per riga, confrontandolo con le specifiche e cercando di individuare errori, incongruenze o altri problemi.
+5. __Rework__: una volta individuati i problemi, l'autore del codice si occupa di correggere i difetti individuati.
+6. __Follow-up__: possibile re-ispezione del nuovo codice ottenuto dopo la fase precedente.
+
+Se la maggior parte delle fasi è abbastanza autoesplicativa, è bene dare uno sguardo più approfondito all'attività di ispezione vera e propria.
+
+### Ispezione
+
+Durante la fase di ispezione, l'obiettivo è __trovare e registrare__ i difetti __senza correggerli__: la tentazione di correggere i difetti è sicuramente fortissima ma non è compito dei partecipanti alla riunione farlo.
+Ciascuno di loro potrebbe infatti avere le proprie idee e preferenze e metterli d'accordo potrebbe non essere facile: si preferisce quindi che sia l'autore del codice a correggere successivamente i problemi trovati.
+
+Per evitare ulteriormente di perdere tempo sono previste _al massimo_ 2 sessioni di ispezione di 2 ore al giorno, durante le quali lavorare approssimativamente a __150 linee di codice all'ora__.
+Quest'ultimo vincolo è __molto variable__ in quanto cambia in base al linguaggio, al progetto, all'attenzione ai dettagli richiesta e alla complessità.
+
+Una possibilità prevista in questa fase è anche quella di fare _"test a mano"_: si analizza
+il flusso di controllo del programma su una serie di casi di test così da verificarne il funzionamento.
+
+Ancora più prominente è però l'uso di una serie di __checklist__, di cui parliamo nel prossimo paragrafo.
+
+### Checklist
+
+Rispetto all'attività di testing, che a partire dai malfunzionamenti permetteva di risalire ai difetti e dunque agli sbagli commessi, il _thought-process_ per le __checklist__ è inverso: __si parte dagli _sbagli___ che più frequentemente hanno portato ad inserire determinate anomalie nel codice e si controlla che nessuno di questi sia stato commesso nuovamente.
+
+In letteratura è reperibile la __conoscenza__ di tutto ciò che è meglio evitare poiché in passato ha portato più volte ad avere anomalie nel codice.
+Tale conoscenza è raccolta in __checklist comuni__ per i vari linguaggi.
+
+Inoltre, l'ispezione del codice funziona così bene anche perché tali checklist possono essere __redatte internamente__ all'azienda, in base all'esperienza passata e alla storia di un determinato progetto. \\
+Man mano che il progetto va avanti, l'__individuazione di un nuovo sbaglio__ si traduce in un'__evoluzione della checklist__: dalla prossima ispezione si controllerà di non aver commesso lo stesso errore.
+
+#### <big>Esempio NASA</big>
+
+La NASA nel suo <a href="../assets/13_nasa-software-inspection.pdf"><i>Software Formal Inspections Guidebook</i></a> (1993) ha formalizzato circa __2.5 pagine di checklist__ per C e 4 per FORTRAN.
+
+Sono divise in _functionality_, _data usage_, _control_, _linkage_, _computation_, _maintenance_ e _clarity_.
+
+Di seguito sono elencati alcuni esempi dei punti di tali checklist:
+
+> - [ ] Does each module have a single function?
+> - [ ] Does the code match the Detailed Design?
+> - [ ] Are all constant names upper case?
+> - [ ] Are pointers not `typecast` (except assignment of `NULL`)?
+> - [ ] Are nested `#include` files avoided?
+> - [ ] Are non-standard usage isolated in subroutines and well documented?
+> - [ ] Are there sufficient comments to understand the code?
+
+## Struttura di incentivi
+
+Perché l'ispezione del codice come è stata descritta funzioni bene, occorre prevedere una serie di __dinamiche positive__ di incentivi al suo interno.
+
+In particolare, è importante sottolineare che i difetti trovati __non devono essere utilizzati__ per la valutazione del personale: questo evita che i programmatori nascondano i difetti nel proprio codice, minando la qualità del prodotto.
+
+Dall'altro canto si possono invece considerare per la __valutazione di readers e tester__ i difetti trovati durante l'ispezione, in modo che questi siano incentivati a fare l'ispezione più accurata possibile.
+
+## Variante: _active_ design reviews
+
+Purché il processo di ispezione funzioni al meglio __le persone__ coinvolte __devono partecipare__, ma per come abbiamo descritto l'attività di Fagan Code Inspection nulla vieterebbe ai revisori non preparati di essere presenti ma non partecipare, rimanendo in silenzio e pensando ad altro.
+
+Innanzitutto, per sopperire a questo problema i partecipanti andrebbero __scelti__ tra persone di adeguata esperienza e sopratutto assicurando che nel team vi siano revisori per diversi aspetti nel progetto.
+
+Qualora questo non bastasse, una variante del processo che prende il nome di __active design review__ suggerisce che sia l'__autore__ a leggere le checklist e sollevare questioni all'attenzione dei revisori, chiedendo diverse domande.
+Essendo presi direttamente in causa, i revisori saranno quindi costretti a partecipare.
diff --git a/src/14_review/05_software-inspection/02_automazione.md b/src/14_review/05_software-inspection/02_automazione.md
new file mode 100644
index 0000000000000000000000000000000000000000..b49cffc34eee3f73fe6ebcd86d24792ff74e452e
--- /dev/null
+++ b/src/14_review/05_software-inspection/02_automazione.md
@@ -0,0 +1,10 @@
+# Automazione
+
+Sebbene l'ispezione del codice sia una tecnica manuale esistono diversi __strumenti di supporto automatici__ in grado di velocizzare notevolmente il lavoro.
+Alcuni di questi tool possono aiutare con:
+
+- __controlli banali__, come la formattazione; in fase di ispezione manuale si controllerà poi il risultato del controllo automatico;
+- __riferimenti__: checklist e standard in formati elettronici facilmente consultabili e compilabili;
+- __aiuti alla comprensione del codice__: tool che permettono di navigare e leggere il codice con maggiore facilità e spesso utilizzati durante attività di _re-engineering_;
+- __annotazione e comunicazioni__ del team, come l'email;
+- __guida al processo e rinforzo__: non permettere di chiudere il processo se non sono stati soddisfatti alcuni requisiti (_come la necessità di approvazione prima del merge di una pull request_).
diff --git a/src/14_review/05_software-inspection/03_pro-contro.md b/src/14_review/05_software-inspection/03_pro-contro.md
new file mode 100644
index 0000000000000000000000000000000000000000..37869f33a117017f30b0ba588be8e5baeb152a3a
--- /dev/null
+++ b/src/14_review/05_software-inspection/03_pro-contro.md
@@ -0,0 +1,21 @@
+# Pro e contro
+
+Finito di descrivere il processo di ispezione del software possiamo chiederci: funziona?
+Prove empiriche parrebbero suggerire che la risposta sia __sì__ e evidenziano anche che tale tecnica è particolarmente _cost-effective_.
+
+I vantaggi dell'uso di questa tecnica di verifica e convalida sono infatti numerosi:
+
+- Esiste un __processo rigoroso e dettagliato__;
+- Si basa sull'__accumulo dell'esperienza__, auto-migliorandosi con il tempo (vd. _checklist_);
+- Il processo integra una serie di __incentivi sociali__ che spingono l'autore del codice ad analizzarlo in modo critico;
+- A differenza del testing è possibile per la mente umana __astrarre il dominio completo__ dei dati, considerando quindi in un certo senso tutti i casi di test;
+- È applicabile anche a __programmi incompleti__.
+
+La software inspection funziona così bene che è spesso utilizzata come _baseline_ per valutare altre tecniche di verifica e convalida.
+
+Questo non significa però che essa sia esente da __limiti__. \
+Innanzitutto il test può essere fatto __solo a livello di unità__ in quanto la mente umana ha difficoltà a lavorare in situazioni in cui sono presenti molte informazioni contemporaneamente in assenza di astrazioni e indirettezze.
+Inoltre la software inspection __non è incrementale__: spesso infatti la fase di follow-up non è così efficace, in quanto il codice è cambiato talmente tanto che è necessario ricominciare l'ispezione da capo.
+
+Ciò non toglie però che, come afferma la __Legge di Fagan (L17)__:
+> _Le ispezioni aumentano in maniera significativa la produttività, qualità e la stabilità del progetto._
diff --git a/src/14_review/05_software-inspection/04_verifica-convalida.md b/src/14_review/05_software-inspection/04_verifica-convalida.md
new file mode 100644
index 0000000000000000000000000000000000000000..9a492b110cd4bfb63f1a3efaa98c2de37c31964d
--- /dev/null
+++ b/src/14_review/05_software-inspection/04_verifica-convalida.md
@@ -0,0 +1,29 @@
+# Confronto tra tecniche di verifica e convalida
+
+Numerosi studi hanno provato a confrontare l'efficacia di varie tecniche di testing, con particolare riferimento a testing strutturale, testing funzionale e software inspection.
+Un [articolo](https://web.archive.org/web/20060920113729/http:/www2.umassd.edu/SWPI/ISERN/ISERN-98-10.pdf) del 2004 riporta in una __tabella di confronto__ i risultati di alcuni di questi studi, considerando come metrica di valutazione delle tecniche di verifica e convalida la _percentuale media di difetti individuati_.
+
+![Confronto tecniche verifica e convalida](/assets/13_tabella-confronto-tecniche-vc.png)
+
+Come si può notare, a seconda dello studio appare più efficace l'una o l'altra tecnica; inoltre, la somma delle percentuali per ogni riga non è 100%, il che significa che alcuni difetti non possono essere rilevati da nessuna delle tre tecniche. \
+Nonostante ciò, si possono fare una serie di osservazioni: innanzitutto, l'efficacia di una  o dell'altra tecnica dipende dalla __tipologia del progetto__ su cui viene esercitata.
+Inoltre, __non è detto__ che tecniche diverse trovino __gli stessi errori__: l'ispezione potrebbe aver trovato una certa tipologia di errore mentre il testing funzionale un'altra; le diverse tecniche controllano infatti diversamente aspetti differenti del programma, osservandolo da __diversi punti di vista__.
+
+Confrontare le varie tecniche non è dunque necessariamente una perdita di tempo, mentre lo è sicuramente __confrontare solo i numeri__, come la varietà di risultati diversi ottenuti dai parte di studi diversi.
+Tra l'altro, dal riassunto della tabella si __perdono__ informazioni sulle __modalità di rilevazione__ dei dati, attribuendole ad espressioni generiche (come _comunemente_, _in media_, _progetti junior_, ...).
+
+In conclusione, non c'è una risposta semplice al confronto e __non esiste una tecnica _sempre_ migliore__ rispetto alle altre.
+
+## Combinare le tecniche
+
+Una domanda che sorge spontanea è chiedersi quindi cosa può succedere se si __combinano insieme__ diverse tecniche di verifica e convalida.
+
+Diversi [studi](https://web.archive.org/web/20070221162909/http://www2.umassd.edu/SWPI/TechnicalReview/r4094.pdf) mostrano che applicando tutte e quattro le tecniche qui descritte &mdash; anche se solo in modo superficiale &mdash; il risultato è sicuramente __più performante__ delle tecniche applicate singolarmente.
+
+![Tabella tecniche verifica convalida insieme](/assets/13_tabella-tecniche-vc-insieme.png)
+
+Anche se una certa percentuale di errori può essere rilevata senza alcuna tecnica formale di verifica e convalida, semplicemente usando il software, si può infatti notare ciascuna tecnica presa singolarmente migliora tale percentuale e che la __combinazione__ di tecniche diverse la incrementa ulteriormente.
+Questo perché tendenzialmente __ogni tecnica controlla aspetti differenti__ e le rispettive aree di controllo si sovrappongono poco: è dunque conveniente applicare superficialmente ciascuna tecnica piuttosto che una sola tecnica in modo molto approfondito.
+
+In conclusione, come afferma la __Legge di Hetzel-Meyer (L20)__:
+> _Una combinazione di diversi metodi di V/C supera qualsiasi metodo singolo._
diff --git a/src/14_review/05_software-inspection/05_gruppi-test.md b/src/14_review/05_software-inspection/05_gruppi-test.md
new file mode 100644
index 0000000000000000000000000000000000000000..f0e281f352ad38f50f2b2eb19920b1ef89802051
--- /dev/null
+++ b/src/14_review/05_software-inspection/05_gruppi-test.md
@@ -0,0 +1,40 @@
+# Gruppi di test autonomi
+
+È convinzione comune che colui che ha sviluppato un pezzo di codice sia la persona meno adatta a testarlo, come afferma la __Legge di Weinberg (L23)__:
+
+> _Uno sviluppatore non è adatto a testare il suo codice._
+
+Di conseguenza, si preferisce spesso che il testing sia affidato ad un __gruppo di tester autonomi__.
+Questo implica infatti una serie di vantaggi, sia __tecnici__ che e __psicologici__:
+
+- __Aspetti tecnici__:
+  - __maggiore specializzazione__: si evita così di richiedere che i propri sviluppatori siano anche esperti di testing;
+  - __maggiore conoscenze delle tecniche di verifica e convalida__ e dei relativi tool: chi fa il _tester_ di lavoro acquisisce competenze specifiche sui tool e sugli strumenti di testing (spesso complessi), oltre che sui concetti di copertura e mutazioni.
+- __Aspetti psicologici__:
+  - __distacco dal codice__: a causa dell'assenza di modelli mentali precedenti su come il software dovrebbe operare, un tester esterno pone maggiore attenzione agli aspetti spesso trascurati o dimenticati;
+  - __indipendenza nella valutazione__: una persona che testa il proprio codice è incentivata a _non_ trovare molti errori in quanto potrebbe suggerire un lavoro di dubbia qualità in fase di sviluppo.
+  Un gruppo specializzato nel testing è invece incentivato a trovarne il più possibile per giustificare il loro impiego.
+
+Ci sono tuttavia anche una serie di __svantaggi__ legati all'avere un gruppo di tester autonomo.
+Innanzitutto, i problemi più ovvi sono legati all'__aspetto tecnico__: il fatto che i tester diventino specializzati nel testing significa che __perderanno__ con il tempo la __capacità di progettare__ e __codificare__, oltre a possedere una __minore conoscenza dei requisiti__ del progetto.
+
+Nell'analisi di Elisabeth Hendrickson denominata "[__Better testing &mdash; worse quality?__](https://web.archive.org/web/20220526084408/http:/testobsessed.com/wp-content/uploads/2011/04/btwq.pdf)" viene analizzata poi la tecnica sotto un __punto di vista psicologico__: come è possibile che un maggior investimento nel team di testing porti a un calo delle prestazioni in termini di numero di errori nel codice?
+
+La risposta pare dipendere dal concetto di ___responsabilità___: seppur vero che l'attività di testing è compito del tester, è anche vero che è lo sviluppatore stesso che ha il compito di fare __test di unità__ del proprio codice &mdash; il team di testing dovrebbe occuparsi solo di quello funzionale o di integrazione.
+Spesso però, a fronte di un aumento del personale nel team di testing e specialmente quando una deadline è vicina, il team di sviluppo tende a __spostare la responsabilità__ di trovare gli errori ai tester, __abbassando la qualità del codice__. \
+Il team di testing troverà sì gli errori, riconsegnando il codice agli sviluppatori per correggerli, ma questo passaggio ulteriore implica una notevole __perdita di tempo__ e risorse.
+
+Inoltre, la presenza di un team di testing dedicato può generare __pressioni negative__ sul team di sviluppo: ogni sviluppatore potrebbe sentirsi sotto costante valutazione da parte del team di testing.
+
+## Possibili alternative
+
+Una possibile soluzione alle criticità appena evidenziate consisterebbe nella __rotazione del personale__: una stessa persona potrebbe ricoprire il ruolo di sviluppatore per un progetto e di tester per un altro.
+Questo approccio mostra diversi vantaggi, tra cui:
+
+- __evitare pressioni negative__: ricoprendo diversi ruoli in diversi progetti, il personale non si dovrebbe sentire _giudicato_ o _giudicante_;
+- __evitare il progressivo depauperamento tecnico__ dovuto ad all'eccessiva specializzazione;
+- __evitare lo svuotamento dei ruoli__.
+
+C'è però da considerare un certo __aumento dei costi di formazione__ per via del raddoppio delle responsabilità individuali e un parallelo __aumento della difficoltà di pianificazione__: potrebbe succedere che la stessa persona debba lavorare a più progetti contemporaneamente, dovendo quindi dividere il proprio tempo e le proprie competenze.
+
+Un'altra possibile alternativa consiste nella __condivisione del personale__, che prevede che siano gli stessi sviluppatori a occuparsi del testing: ciò permette di __sopperire__ al problema di __scarsa conoscenza del software__ in esame e del relativo dominio applicativo ma, oltre a far riemergere le __criticità__ individuate precedentemente, aumenta le __difficoltà nella gestione dei ruoli__.
diff --git a/src/14_review/06_modelli-statistici.md b/src/14_review/06_modelli-statistici.md
new file mode 100644
index 0000000000000000000000000000000000000000..7cdf54035b6e25c054085134996dcc2d27cb3e19
--- /dev/null
+++ b/src/14_review/06_modelli-statistici.md
@@ -0,0 +1,19 @@
+# Modelli statistici
+
+Negli ultimi tempi si stanno sviluppando una serie di __modelli statistici__ sulla distribuzione degli errori nel codice che dovrebbero teoricamente aiutare l'attività di testing guidandola verso le porzioni di sorgente che _più probabilmente_ potrebbero presentare difetti.
+
+Tali modelli propongono infatti una __correlazione statistica__ tra una serie di __metriche__ quali la lunghezza del codice, il tipo di linguaggio, il grado massimo di indentamento etc. e:
+
+- __la presenza di errori__ per categoria di errore;
+- __il numero di errori__ per categoria di errore.
+
+L'idea sarebbe quindi di __predire la distribuzione e il numero di errori__ all'interno di uno specifico modulo del software in esame.
+
+Occorre però __fare attenzione__ alle conclusioni di queste statistiche.
+Utilizzare i risultati di tali modelli statistici come indicazioni sul fatto che su determinati moduli vada fatta più attività di testing rispetto ad altri potrebbe inizialmente sembrare la __soluzione più logica__.
+Tuttavia, tali risultati non considerano l'attività di testing già effettuata e le correzioni successive e quindi __non cambiano__: codice inizialmente _"scritto male"_ secondo il modello rimarrà per sempre scritto male, anche se testato estensivamente.
+
+Con ciò in mente, si cita spesso la __Legge di Pareto/Zipf (L24)__:
+> _Circa l'80% dei difetti proviene dal 20% dei moduli._
+
+Sebbene tale affermazione è indubbiamente probabilmente vera, è difficile sfruttare questa nozione in quanto non sono conosciuti in principio i __moduli particolarmente problematici__, e il testing è comunque necessario anche in tutti gli altri.
diff --git a/src/14_review/07_debugging.md b/src/14_review/07_debugging.md
new file mode 100644
index 0000000000000000000000000000000000000000..46eb50cda0fc2eaf3c00eb776c177892d81a7321
--- /dev/null
+++ b/src/14_review/07_debugging.md
@@ -0,0 +1,102 @@
+# Debugging
+
+Il debugging è l'insieme di tecniche che mirano a __localizzare__ e __rimuovere__ le anomalie che sono la causa di malfunzionamenti riscontrati nel programma.
+Come già detto, esso non è invece utilizzato per _rilevare_ tali malfunzionamenti.
+
+Il debugging richiede una __comprensione approfondita del codice__ e del funzionamento del programma e può essere un processo complesso e articolato.
+Tuttavia, può contribuire in modo significativo a migliorare la qualità e la stabilità del codice, oltre che a _risolvere_ malfunzionamenti.
+
+Trattandosi di ricerca delle anomalie che generano malfunzionamenti noti, l'attività è definita per un __programma__ e __un insieme di dati che causano malfunzionamenti__.
+Essa si basa infatti sulla __riproducibilità__ del malfunzionamento, verificando prima che non sia dovuto in realtà a specifiche in errate.
+
+Si tratta di un'attività molto complicata, come fa notare Brian W. Kernighan nella sua famosa citazione:
+
+> _"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it"._
+
+È dunque importante scrivere codice __più semplice possibile__ in modo tale da poterne fare un altrettanto semplice debugging laddove necessario.
+
+## Perché è così difficile?
+
+L'attività di debugging è particolarmente complessa soprattutto perché non è sempre possibile individuare con precisione la __relazione anomalia-malfunzionamento__.
+Non è un legame banale, in quanto potrebbero esserci anomalie che prima di manifestarsi sotto forma di malfunzionamenti abbiano avuto molte evoluzioni.
+
+Inoltre, __non esiste una relazione biunivoca__ tra anomalie e malfunzionamenti: non è detto che un'anomalia causi un unico  malfunzionamento, ma nemmeno che un malfunzionamento sia causato da un'unica anomalia.
+
+Un altro problema è dovuto al fatto che la __correzione di anomalie__ non garantisce affatto un software migliore o con meno errori: per correggere un'anomalia è necessario per forza di cose anche modificare il codice sorgente, ma ogni volta che viene fatto si apre la possibilità di introdurre __nuove anomalie__ nel codice stesso.
+
+## Tecnica _naïve_
+
+La tecnica di debugging maggiormente utilizzata dai programmatori consiste nell'introdurre nel modulo in esame una serie di __comandi di output__ (es. _print_) che stampino su console il valore intermedio assunto dalle sue variabili.
+Questo permetterebbe di osservare l'evoluzione dei dati e, si spera, di comprendere la causa del malfunzionamento a partire da tale storia.
+
+Nonostante sia __facile da applicare__, si tratta in realtà di una tecnica __molto debole__: non solo essa __richiede la modifica del codice__ (e quindi una _rimozione_ di tali modifiche al termine), ma è __poco flessibile__ in quanto richiede una nuova compilazione per ogni stato esaminato. \
+Bisogna inoltre considerare che questa tecnica testa un __programma diverso__ da quello originale che presenta delle _print_ aggiuntive solo _apparentemente_ innocue e senza effetti collaterali.
+
+L'unico scenario (irrealistico) in cui la tecnica potrebbe essere considerata sufficiente
+sarebbe nel caso in cui il codice sia progettato talmente bene e il modulo così ben isolato che basterebbe scrivere un'unica _print_ per risalire all'anomalia.
+
+### Tecnica _naïve_ avanzata
+
+Un miglioramento parziale alla tecnica appena descritta si può ottenere sfruttando le __funzionalità del linguaggio__ oppure alcuni tool specifici per il debug, come per esempio:
+
+- `#ifdef` e `gcc -D` per il linguaggio C;
+- __librerie di logging__ (con diverso livello), che permettono peraltro di rimuovere i messaggi di log in fase di produzione del codice;
+- __asserzioni__, ovvero check interni al codice di specifiche proprietà: possono essere visti anche come _"oracoli" interni_ al codice che permettono di segnalare facilmente stati illegali.
+
+Ciò non toglie che la tecnica sia comunque __naïve__, in quanto si sta ancora modificando il codice in modo che fornisca informazioni aggiuntive.
+
+## Dump di memoria
+
+Una tecnica lievemente più interessante è quella dei __dump di memoria__, che consiste nel produrre un'__immagine esatta__ della __memoria__ del programma dopo un passo di esecuzione: si scrive cioè su un file l'intero contenuto della memoria a livello di linguaggio macchina (_nei sistemi a 32 bit, la dimensione dei dump può arrivare fino a 4GB_).
+
+```txt
+Segmentation fault (core dumped)
+```
+
+Sebbene questa tecnica non richieda la modifica del codice, essa è spesso __difficile da applicare__ a causa della differenza tra la rappresentazione astratta dello stato (legata alle strutture dati del linguaggio utilizzato) e la rappresentazione a livello di memoria di tale stato.
+Viene inoltre prodotta una __enorme mole di dati__ per la maggior parte inutili.
+
+## Debugging simbolico
+
+Il prossimo passo è invece il cosiddetto __debugging simbolico__, un'attività che utilizza __tool__ specifici di debugging per semplificare la ricerca delle anomalie che causano il malfunzionamento in esame.
+Tali strumenti permettono di __osservare in tempo reale l'esecuzione del programma__, sollevando una cortina e rendendo possibile analizzare l'evoluzione del valore delle variabili passo per passo: questi tool non alterano il codice ma _come_ esso è eseguito.
+
+A tal proposito, i debugger simbolici forniscono informazioni sullo stato delle variabili utilizzando lo __stesso livello di astrazione__ del linguaggio utilizzato per scrivere il codice: gli stati sono cioè rappresentati con __stessi simboli__ per cui le locazioni di memoria sono state definite (_stesse strutture dati_), rendendo quindi utile e semplice l'attività di __ispezione dello stato__.
+
+In aggiunta, i debugger simbolici forniscono __ulteriori strumenti__ che permettono di visualizzare il comportamento del programma in maniera selettiva, come per esempio _watch_ e _spy monitor_.
+
+Per regolare il flusso del programma è poi possibile inserire __breakpoint__ e __watchpoint__ su certe linee di codice che ne arrestino l'esecuzione in uno specifico punto, eventualmente rendendoli dipendenti dal valore di variabili.
+Volendo poi riprendere l'esecuzione si può invece scegliere la granularità del successivo passo:
+
+- __singolo__: si procede alla linea successiva;
+- __dentro una funzione__: si salta al codice eseguito dalle funzioni richiamate sulla riga corrente;
+- __drop/reset del frame__: vengono scartate le variabili nel frame d'esecuzione ritornando ad una situazione precedente.
+
+### Debugging per prova
+
+Molti debugging simbolici permettono non solo di visualizzare gli stati ottenuti, ma anche di  __esaminarli automaticamente__ in modo da verificarne la correttezza.
+
+In particolare, utilizzando __watch condizionali__ è possibile aggiungere __asserzioni a livello di monitor__, verificando così che certe proprietà continuino a valere durante l'intera esecuzione. \
+Così, per esempio, è possibile chiedere al _monitor_ (l'_esecutore_ del programma) di controllare che gli indici di un array siano sempre interni all'intervallo di definizione.
+
+### Altre funzionalità dei debugger
+
+Ma non finisce qui! I debugger moderni sono strumenti veramente molto interessanti, che permettono per esempio anche di:
+
+- __modificare il contenuto di una variabile__ (o zona di memoria) a runtime;
+- __modificare il codice__: nonostante non sia sempre possibile, può essere comodo per esempio dopo tante iterazioni di un ciclo;
+- ottenere __rappresentazioni grafiche__ dei dati: strutture dinamiche come puntatori, alberi e grafi possono essere rappresentate graficamente per migliorare la comprensione dello stato.
+
+## Automazione
+
+Visti tutti questi fantastici tool può sorgere una domanda: __l'attività di debugging può essere automatizzata?__
+
+Andreas Zeller tratta questo argomento in maniera approfondita nel suo [Debugging Book](http://debuggingbook.org/), proponendo alcune direzioni di sviluppo di ipotetici strumenti di debugging automatico. \
+I due concetti principali della trattazione sono i seguenti:
+
+- __shrinking input__: dato un __input molto grande__ e complesso che causa un malfunzionamento, strumenti automatici possono aiutare a ridurlo il più possibile in modo da semplificare il debugging;
+- __differential debugging__: dato lo stesso input, in maniera automatica vengono esplorati gli stati del programma mutando ad ogni iterazione piccole porzioni di codice per individuare dove è più probabile che si trovi l'anomalia.
+
+Purtroppo per il momento la prospettiva di debugger automatici è ancora lontana. \
+Tuttavia, esiste già qualcosa di simile, vale a dire il comando __`git bisect`__ di Git: data una versione vecchia in cui il bug non è presente, una versione nuova in cui esso si è manifestato e un oracolo che stabilisce se il bug è presente o meno, Git esegue una __ricerca dicotomica__ per trovare la versione che ha introdotto il problema.
+Sebbene non sia proprio la stessa cosa, si tratta sicuramente di uno strumento utile.
diff --git a/src/15_reti-petri/00_index.md b/src/15_reti-petri/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..1e67dcd83c2637173d8d5ac55a72b571513c293e
--- /dev/null
+++ b/src/15_reti-petri/00_index.md
@@ -0,0 +1,29 @@
+# [Reti di Petri](https://www2.informatik.uni-hamburg.de/TGI/PetriNets/index.php)
+
+In questa lezione verranno mostrate le reti di Petri come esempio di **linguaggio formale**: fin dall'inizio del corso è stato possibile apprendere come l'ingegneria del software si occupi di linguaggi e comunicazione.
+
+Partendo infatti dai processi che sfruttano un linguaggio poco formale e con poca terminologia tecnica (ad esempio le _user story_) e passando per la progettazione in cui è stato utilizzato un linguaggio più rigoroso, si arriva infine a un vero linguaggio formale utile a **raccogliere delle specifiche**.
+
+Esiste un modello standard di rete di Petri e delle possibili estensioni di quest'ultimo: ad esempio nelle prossime lezioni saranno illustrati alcuni possibili dialetti come le **reti temporizzate**, utili a descrivere sistemi real time che necessitano di requisiti formali per ridurne le criticità.
+
+In generale utilizzare linguaggi complessi e formali per descrivere le specifiche può essere costoso: vengono infatti utilizzati perlopiù in **contesti critici** dove i fallimenti provocano conseguenze molto gravi e in cui la **sicurezza deve essere garantita** prima di mettere in funzione il software.
+
+Le reti di Petri sono in parte simili agli __automi a stati finiti__ (FSM), ma nascono specificatamente per descrivere sistemi concorrenti.
+Tra gli altri aspetti, i concetti di _stato_ e _transizione_ per le reti di Petri differiscono rispetto a quelli già conosciuti per le FSM:
+- lo __stato__ nelle reti di Petri non è più un'informazione atomica osservata a livello di sistema ma è frammentata in __parti diverse__ la cui composizione avviene tramite la loro visione generale;
+- di conseguenza le __transizioni__ non operano sullo stato globale ma si limitano a variarne una parte.
+
+Nelle FSM esiste un unico stato attivo e gli stati disponibili sono dati dal prodotto cartesiano di tutti i possibili valori delle diverse entità.
+Per contro nelle reti di Petri ci sono __diversi stati attivi__ in un dato momento, cosa che permette di semplificarne notevolmente la rappresentazione e l'analisi.
+
+- [**Definizioni**](./01_definizioni.md)
+- [**Macchine a stati finiti**](./02_fsm.md)
+- [**Relazioni tra transizioni**](./03_relazioni/00_index.md)
+- [**Insieme di raggiungibilità**](./04_insieme-raggiungibilita.md)
+- [**Limitatezza**](./05_limitatezza.md)
+- [**Vitalità di una transizione**](./06_vitalita.md)
+- [**Capacità dei posti**](./07_capacita.md)
+- [**Archi inibitori**](./08_archi-inibitori.md)
+- [**Eliminazione pesi sugli archi**](./09_pesi-archi.md)
+- [**Conservatività**](./10_conservativita.md)
+- [**Stato base e rete revertibile**](./11_stato-base.md)
diff --git a/src/15_reti-petri/01_definizioni.md b/src/15_reti-petri/01_definizioni.md
new file mode 100644
index 0000000000000000000000000000000000000000..3677a65de302f91a45bb146188a6b44d8e3e3126
--- /dev/null
+++ b/src/15_reti-petri/01_definizioni.md
@@ -0,0 +1,102 @@
+# Definizioni
+
+## Definizione informale
+
+Un vantaggio delle reti di Petri è che possono essere viste in maniera informale dal cliente.
+È infatti facile rappresentare una rete di Petri come un grafo in cui ogni nodo rappresenta o un __posto__ o una __transizione__ e gli archi i collegamenti presenti tra le transizioni e i posti.
+Il grafo è __bipartito__, ovvero un grafo in cui nodi di un tipo sono messi in relazione __solo__ con nodi dell'altro tipo: in questo caso _i posti possono essere collegati soltano a transizioni e viceversa_.
+
+Ad ogni posto è assegnato un certo numero di ___gettoni___ (o __token__) – sarà successivamente approfondito il senso dell'assegnamento di un numero infinito di gettoni a un posto.
+
+La **disposizione dei gettoni nei posti** in un dato momento all'interno della rete di Petri ne determina il suo __stato complessivo__.
+
+![Rete Petri informale](/assets/14_rete-petri-informale.png)
+
+Per far evolvere lo stato della rete, l'__assegnamento dei gettoni deve poter variare__. \
+La trasformazione dello stato è effettuata dallo scatto di una transizione:
+- una transizione si dice ___abilitata___ (_enabled_) quando la somma dei gettoni dei posti collegati ingresso è maggiore di un certo numero;
+- una transizione ___scatta___ (_fire_) quando, dopo essere stata abilitata, consuma i gettoni dei posti collegati in ingresso e ne genera altri all'interno dei posti collegati in uscita.
+È importante notare come i gettoni __non si spostano__ da un posto a un altro conseguentemente a uno scatto, ma vengono __distrutti__ nei posti in ingresso alla transizione e __generati__ nei posti in uscita.
+Quest'ultima considerazione è importante per capire che i gettoni _non sono necessariamente sempre dello stesso numero in ingresso e in uscita_.
+
+Tramite questo __modello operativo__ è facile mostrare al cliente quando qualcosa cambia all'interno del sistema, perché risulta più intuitivo rispetto a un linguaggio logico e descrittivo.
+
+Lo **svantaggio** è che fornisce informazioni parziali su _come_ il sistema compie le azioni che dovrebbe eseguire, rischiando di essere una via di mezzo tra _specifica_ e _documento di design_.
+Si può comunque chiamare specifica perché _viene definito totalmente e inequivocabilmente il comportamento del sistema_.
+
+La rete descritta è quindi una **macchina di riferimento** da utilizzare come confronto per stabilire la validità del sistema sotto esame, come se fosse un _oracolo_.
+
+## Definizione matematica
+
+Come già detto, esistono numerosi dialetti di reti di Petri. 
+In questo caso vediamo le __PT net__ (reti con posti e transizioni) che sono le più classiche, successivamente verranno descritte delle estensioni e riduzioni di queste reti.
+
+Una rete di Petri classicamente è una 5-tupla \\([P, \\, T; \\; F, \\, W, \\, M_0]\\) in cui:
+- \\(P\\) è l'insieme degli **identificatori dei posti**;
+- \\(T\\) è l'insieme degli **identificatori delle transizioni**;
+- \\(F\\) è l'insieme delle **relazioni di flusso**;
+- \\(W\\) è una funzione che associa un **peso ad ogni flusso**; 
+- \\(M_0\\) è la **marcatura iniziale**, ovvero l'assegnamento iniziale dei _gettoni_.
+
+In generale definiamo come __marcatura__ una _particolare configurazione dell'assegnamento dei gettoni all'interno della rete di Petri_, sia essa _iniziale_ o una sua  _evoluzione_.
+
+Da notare che \\(P\\) e \\(T\\) a livello matematico sono degli insiemi di __identificatori__ che non si sovrappongono (dato che si tratta di entità differenti) a cui poi verrà assegnato un significato, quindi precedentemente sono stati associati a posti e transizioni, ma di fatto sono tutti __identificatori__.
+
+Data la 5-tupla appena descritta esistono le seguenti proprietà:
+- \\(P \cap T = \varnothing\\); 
+- \\(P \cup T \neq \varnothing\\) (una rete in cui non c'è nulla non è una rete: almeno un posto o una transizione ci devono essere);
+- \\(F \subseteq (P \times T) \cup (T \times P)\\);
+- \\(W: \: F \rightarrow \mathbb N \setminus \{ 0 \}\\);
+- \\(M_0: P \rightarrow \mathbb N\\).
+
+Utilizziamo inoltre alcune _scorciatoie_:
+
+- \\(\operatorname{Pre}(a) = \{ d \in (P \cup T) \quad \langle d, \\, a \rangle \in F \}\\). \
+Il **preset** di un nodo \\(a\\) è l'insieme degli elementi \\(d\\) appartenenti all'unione degli insiemi degli identificatori di posti e transizioni tali che esiste una relazione di flusso tra \\(d\\) e \\(a\\) appartenente a \\(F\\). \\
+In sostanza questo insieme rappresenta l'insieme degli **identificatori antecedenti** ad \\(a\\);
+- \\(\operatorname{Post}(a) = \{ d \in (P \cup T) \quad \langle a,\, d \rangle \in F \}\\). \
+Il **postset** di un nodo \\(a\\) è l'insieme degli elementi \\(d\\) appartenenti all'unione degli insiemi degli identificatori di posti e transizioni tali che esiste una relazione di flusso tra \\(a\\) e \\(d\\) appartenente a \\(F\\). \\
+In sostanza questo insieme rappresenta l'insieme degli **identificatori successivi** ad \\(a\\).
+
+Tutto questo rappresenta la parte statica delle reti di Petri, ovvero quando vengono osservate in un preciso istante di tempo, senza considerare i cambiamenti che potrebbero avvenire al suo interno.
+
+### Comportamento dinamico
+
+Una transizione \\(t \in T\\) è __abilitata__ in una particolare marcatura \\(M\\) se e solo se
+
+$$
+\boxed{
+    \forall p \in \operatorname{Pre}(t) \quad M(p) \geq W( \langle p, \\, t \rangle )
+}.
+$$
+
+In notazione, \\(\boxed{M \\ [ \\ t >}\\) significa che _\\(t\\) è abilitata in \\(M\\)._ 
+
+Significa che per ogni elemento collegato in ingresso a \\(t\\) esiste un numero di gettoni maggiore del peso dell'arco che collega \\(p\\) a \\(t\\).
+Un aspetto interessante di questa definizione è che non si sta ragionando su tutti i posti della rete, ma solo su quelli collegati in ingresso a \\(t\\). 
+Di conseguenza, non è necessario conoscere l'intera rete per poter affermare che una transizione sia abilitata o meno, ma è sufficiente controllare la zona che comprende i posti appartenenti a \\( \operatorname{Pre}(a) \\). 
+Questa proprietà è chiamata __località dell'analisi__.
+
+Lo __scatto__ di una transizione \\(t \in T\\) in una particolare marcatura \\(M\\) produce nel momento successivo una nuova marcatura \\(M'\\) tale per cui
+
+$$
+\begin{aligned}
+\forall p \in \operatorname{Pre}(t) \setminus \operatorname{Post}(t) &\quad  M'(p) = M(p) - W(\langle p, \\, t \rangle); \\\\
+\forall p \in \operatorname{Post}(t) \setminus \operatorname{Pre}(t) &\quad M'(p) = M(p) + W(\langle t, \\, p \rangle); \\\\
+\forall p \in \operatorname{Post}(t) \cap \operatorname{Pre}(t) &\quad M'(p) = M(p) - W(\langle p, \\, t \rangle) + W(\langle t, \\, p \rangle); \\\\
+\forall p \in P - \left ( \operatorname{Post}(t) \cup \operatorname{Pre}(t) \right ) &\quad M'(p) = M(p).
+\end{aligned}
+$$
+
+Specificando in modo descrittivo le notazioni precedenti:
+- per ogni identificatore \\(p\\) appartenente al preset ma non al postset della transizione \\(t\\), il numero di gettoni della nuova marcatura \\(M'\\) sarà uguale al numero di gettoni della marcatura precedente \\(M\\)
+meno il peso dell'arco che collega \\(p\\) a \\(t\\);
+- per ogni identificatore \\(p\\) appartenente al postset ma non al preset della transizione \\(t\\), il numero di gettoni della nuova marcatura \\(M'\\) sarà uguale al numero di gettoni della marcatura precedente \\(M\\)
+più il peso dell'arco che collega \\(t\\) a \\(p\\);
+- per ogni identificatore \\(p\\) appartenente sia al preset sia al postset della transizione \\(t\\), il numero di gettoni della nuova marcatura \\(M'\\) sarà uguale al numero di gettoni della marcatura precedente \\(M\\)
+meno il peso dell'arco che collega \\(p\\) a \\(t\\) più il peso dell'arco che collega \\(t\\) a \\(p\\);
+- per ogni identificatore \\(p\\) appartenente all'insieme dei posti meno l'unione tra preset e postset di \\(p\\) la marcatura non cambia.
+
+In notazione, \\(\boxed{\boxed{M \\ [ \\ t >} \\, M'}\\) significa che lo scatto di \\(t\\) in \\(M\\) produce \\(M'\\).
+
+È importante notare come una transizione può scattare nel caso in cui non abbia alcun elemento nel suo preset; questo significa che la transizione in questione **non possiede prerequisiti** per scattare.
diff --git a/src/15_reti-petri/02_fsm.md b/src/15_reti-petri/02_fsm.md
new file mode 100644
index 0000000000000000000000000000000000000000..198adfcfdfdb23e467fc0e8b4f6f6f3ec9b24d94
--- /dev/null
+++ b/src/15_reti-petri/02_fsm.md
@@ -0,0 +1,77 @@
+# Macchine a stati finiti
+
+È _meccanicamente_ possibile trasformare una macchina a stati finiti in una rete di Petri.
+
+![Produttore](/assets/14_produttore.png)
+
+Riferendosi all'esempio del produttore, l'unico problema è l'**esistenza di collegamenti diretti tra posti**: come è stato detto in precedenza questo non è possibile in una rete di Petri. 
+Sarà quindi necessario interporre tra i posti delle transizioni per avere una rete di Petri valida. \
+Immaginando di mettere un solo gettone in uno dei due posti della rete appena creata, questo indicherà lo **stato attivo** presente nella macchina a stati finiti. \
+Seguendo questi passaggi diventa banale mappare una macchina a stati finiti su una rete di Petri: di seguito è possibile osservare l'operazione analoga eseguita sulle FSM di un consumatore e di un buffer.
+
+![Consumatore buffer](/assets/14_consumatore-buffer.png)
+
+Componendo le reti di Petri di _produttore_, _consumatore_ e _buffer_ appena create, si crea la seguente.
+
+![Produttore consumatore buffer](/assets/14_produttore-consumatore-buffer.png)
+
+In termini di automi a stati finiti, per trovare gli **stati raggiungibili** da questa composizione sarebbe stato necessario eseguire il prodotto cartesiano tra gli stati delle tre macchine a stati finiti combinate tra loro.
+Trattandosi invece di una rete di Petri, è sufficiente unire tutti gli identificatori uguali in un unico identificatore (ad esempio la transizione deposita della rete _produttore_ e della rete _buffer_) e aggiungere a quest'ultimo tutti collegamenti posseduti dagli identificatori uniti.
+
+<span style="color: red">__ATTENZIONE__</span>: nell'esempio della rete composta le coppie di transizioni _"preleva"_ e _"deposita"_ dovrebbero avere due nomi differenti, ma siccome sono indicate con due rettangoli diversi è stato omesso questo particolare. 
+In termini matematici **devono avere nomi differenti**.
+
+Precedentemente è stato detto che, _nel caso di una rappresentazione di una FSM in termini di una rete di Petri_, si rappresenta lo stato attivo nella FSM con un gettone: di conseguenza, portando all'interno della rete composta tutti i gettoni delle varie reti si arriva ad ottenere il risultato descritto dall'immagine precedente, in cui tutte le "entità" (_consumatore_, _produttore_ e _buffer_) mantengono la propria individualità (è infatti presente un gettone per ogni entità).
+
+In questo caso si può quindi notare che il produttore è pronto a produrre, il buffer è vuoto e il consumatore è pronto a consumare una volta che il buffer avrà al suo interno qualcosa.
+
+## Come evolve questa rete?
+Per rispondere a questa domanda la prima cosa da considerare è quali sono le **transizioni abilitate**: in questo caso si tratta solo della transizione _produci_ sotto a \\(p_0\\), in quanto è l'unica ad avere tutti gli elementi del suo preset con un numero di gettoni sufficienti a farla scattare; \\(p_0\\) possiede infatti un gettone e l'arco ha peso 1 (_quando non è specificato il peso è 1_). \
+Una rete di Petri _non forza lo scatto di alcuna transizione_, quindi volendo si potrebbe rimanere nello stato corrente all'infinito senza far mai scattare _produci_. 
+Se però _produci_ scatta, il gettone in \\(p_0\\) viene distrutto e in \\(p_1\\) viene generato un nuovo gettone.
+
+![Primo scatto](/assets/14_primo-scatto.png)
+
+Dopo questo scatto la rete di Petri si trova in una situazione in cui il produttore ha prodotto qualcosa ed è pronto a depositarlo nel buffer: a questo punto non resta che porsi nuovamente la domanda _"quali transizioni sono abilitate?"_ per capire come può procedere l'evoluzione della rete. \
+È facile notare come la transizione _deposita_ sotto \\(b_0\\) sia l'unica abilitata e di conseguenza, _se dovesse scattare_, il risultato sarebbe il seguente:
+
+![Secondo scatto](/assets/14_secondo-scatto.png)
+
+Ora è possibile identificare una situazione particolare, ovvero quella in cui le transizioni pronte a scattare sono due. Sorge spontanea la domanda: _"quale delle due transizioni scatta prima?"_.
+Nelle reti di Petri descritte fino ad ora non è stato presentato lo **scatto simultaneo** delle transizioni, ma nulla vieta che possa avvenire in un contesto reale.
+In tal caso si tratterebbe di un'istanza di __non determinismo__, ovvero _non si può dire quale transizione deve scattare_. 
+Sono quindi 3 le situazioni che si possono verificare:
+- scatta la prima transizione;
+- scatta la seconda transizione;
+- non scatta nessuna transizione (la _non evoluzione_ è comunque un'evoluzione).
+
+Nel caso in cui fosse stato necessario definire che una delle due transizioni scatti prima dell'altra, ci si troverebbe di fronte ad una rete **non corretta**: è infatti possibile modificare la rete in modo tale che imponga un ordine di scatto alle transizioni.
+
+## Sfruttare le reti di Petri
+A questo punto è possibile chiedersi se si stiano sfruttando realmente tutte le potenzialità delle reti di Petri, siccome la rete dell'esempio precedente è stata ricavata da un automa a stati finiti.
+Per capire ciò è possibile osservare un secondo esempio in cui è presentata una rete alternativa alla precedente, ma con lo stesso scopo.
+
+![Rete alternativa](/assets/14_rete-alternativa.png)
+
+La differenza che salta subito all'occhio è il numero di gettoni presenti all'interno di \\(b_0\\) che indicano il numero di posizioni libere nel buffer.
+Questo è un vantaggio perchè se il buffer dovesse cambiare la sua capienza, sfruttando questa rete è sufficiente modificare la marcatura di \\(b_0\\) e il problema sarebbe risolto; la rete precedente avrebbe invece bisogno di una pesante modifica per essere adattata. \
+Di conseguenza si può applicare lo stesso concetto per il consumatore e per il produttore: aumentandone il numero dei gettoni (rispettivamente in \\(p_0\\) e \\(c_0\\)) aumenterebbe il numero di entità in grado di produrre e consumare.
+
+![Rete alternativa diverse entità](/assets/14_rete-alternativa-diverse-entita.png)
+
+È possibile affermare quindi che cambiando il **numero di gettoni** è possibile moltiplicare gli elementi del sistema di cui si vuole tracciare l'evoluzione. Si sottolinea ancora che questo risulterebbe molto oneroso in termini di dimensioni se fosse stato riadattato in una macchina a stati finiti.
+
+Per definizione le macchine a stati finiti __non__ possono rappresentare **situazioni infinite**, se si volesse quindi modificare ulteriormente l'esempio appena visto imponendo una capienza illimitata al buffer, non sarebbe possibile utilizzando una macchina a stati finiti.
+Sfruttando le reti di Petri invece è sufficiente eliminare l'identificatore del posto \\(b_0\\): in questo modo abbiamo una situazione in cui i produttori possono depositare senza limiti all'interno del buffer, mentre i consumatori non possono prelevare più elementi di quelli presenti nel buffer.
+Questo vincolo è imposto dalla marcatura di \\(b_1\\), infatti la transizione "preleva" può scattare al massimo \\(n\\) volte consecutivamente, dove \\(n\\) è la marcatura di \\(b_1\\) &mdash; assumendo che nel mentre non avvengano depositi da parte dei produttori.
+
+Un altra modifica applicabile all'esempio sfrutta i pesi degli archi: ponendo un peso di 3 all'arco che collega _deposita_ a \\(b_1\\) si può dire che il produttore crea e deposita tre prodotti, occupando tre posizioni nel buffer.
+Ponendo invece un peso di 2 all'arco che collega \\(b_1\\) a _preleva_ si specifica che è possibile prelevare dal buffer due elementi alla volta.
+Questo esempio, in parte forzato, è utile per chiarire il fatto che nelle reti di Petri _gli archi non sono semplici collegamenti, ma è possibile attribuirgli un significato_.
+
+Vengono infatti informalmente chiamati _archi_, rifacendosi alla terminologia dei grafi, ma in realtà indicano una relazione più profonda che coinvolge due identificatori: in questo esempio esiste infatti una relazione per cui ogni elemento prodotto occupa tre posizioni all'interno del buffer e un'altra relazione in cui ogni consumatore può prelevare obbligatoriamente due elementi alla volta.
+Tramite il peso degli archi è possibile creare delle situazioni ambigue: ad esempio se la relazione che coinvolge _deposita_ e \\(p_0\\) avesse un peso di 2, ogni volta che viene prodotto qualcosa i produttori si moltiplicherebbero e ovviamente questa situazione indicherebbe che la rete è sbagliata, quindi è necessario fare attenzione ad evitare questo tipo di situazioni.
+
+![Archi con pesi](/assets/14_archi-con-pesi.png)
+
+È da sottolineare che è possibile ridurre una rete P/T avente pesi sugli archi in una rete P/T senza pesi sugli archi: successivamente verrà illustrato come ciò è possibile.
diff --git a/src/15_reti-petri/03_relazioni/00_index.md b/src/15_reti-petri/03_relazioni/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..6ba99b1775993cf84d9bc91991bb93bcd151d223
--- /dev/null
+++ b/src/15_reti-petri/03_relazioni/00_index.md
@@ -0,0 +1,7 @@
+# Relazioni
+
+Di seguito verranno elencati le tipologie di relazioni che possono coinvolgere i diversi identificatori e cosa comporta la loro presenza.
+
+- [**Sequenza**](./01_sequenza.md)
+- [**Conflitto**](./02_conflitto.md)
+- [**Concorrenza**](./03_concorrenza.md)
diff --git a/src/15_reti-petri/03_relazioni/01_sequenza.md b/src/15_reti-petri/03_relazioni/01_sequenza.md
new file mode 100644
index 0000000000000000000000000000000000000000..a5b0eae12276d92a088e2ee98a3942b7addb0089
--- /dev/null
+++ b/src/15_reti-petri/03_relazioni/01_sequenza.md
@@ -0,0 +1,15 @@
+# Sequenza
+
+Una transizione \\(t_1\\) è __in sequenza__ con una transizione \\(t_2\\) in una marcatura \\(M\\) se e solo se
+
+$$\boxed{M \\ [ \\ t_1 >} \\: \land \\: \lnot \\, \boxed{ M \\ [ \\ t_2 > } \\: \land \\: \boxed{ M \\ [ \\ t_1 t_2 > } \\, .$$
+
+Questa formula indica che:
+- \\(t_1\\) è abilitata in \\(M\\);
+- \\(t_2\\) NON è abilitata in \\(M\\);
+- \\(t_2\\) viene abilitata dallo scatto di \\(t_1\\) in \\(M\\).
+
+Si può notare una **relazione d'ordine non simmetrica** in cui _lo scatto di \\(t_1\\)_ è una condizione sufficiente per cui \\(t_2\\) possa scattare: questo tipo di relazione permette quindi di creare un **ordine di scatto** delle transizioni.
+È condizione sufficiente e non necessaria perchè osservando l'esempio sottostante è facile capire che lo sacatto di \\(t_0\\) non è necessario per far si che \\(t_2\\) scatti: infatti anche se dovesse avvenire lo scatto di \\(t_2\\), la transizione \\(t_1\\) diventerebbe comunque abilitata.
+
+![Sequenza](/assets/14_sequenza.png)
diff --git a/src/15_reti-petri/03_relazioni/02_conflitto.md b/src/15_reti-petri/03_relazioni/02_conflitto.md
new file mode 100644
index 0000000000000000000000000000000000000000..149c6fa17ea320ec87b8ed2da8ce196b0a129583
--- /dev/null
+++ b/src/15_reti-petri/03_relazioni/02_conflitto.md
@@ -0,0 +1,38 @@
+# Conflitto
+
+Due transizioni \\((t_1, \\, t_2)\\) sono in:
+- __conflitto strutturale__ \\(\Longleftrightarrow \operatorname{Pre}(t_1) \cap \operatorname{Pre}(t_2) \neq \varnothing \\);
+- __conflitto effettivo__ in una marcatura \\(M\\) \\(\Longleftrightarrow\\):
+    - \\(\boxed{M \\ [ \\ t_1 >} \land \boxed{M \\ [ \\ t_2 >}\\);
+    - \\(\exists p \in \operatorname{Pre}(t_1) \cap \operatorname{Pre}(t_2) \mid M(p) < W(\langle p, \\, t_1 \rangle) + W(\langle  p, \\, t_2\rangle)\\).
+
+Analizzando i due tipi di conflitto è possibile notare che:
+- due transizioni sono in __conflitto strutturale__ se l'intersezione dei due preset non è vuota e quindi hanno posti in ingresso in comune: possono quindi interferire tra loro.
+Il conflitto strutturale dipende solo dalla topologia dela rete, infatti non vengono citate le marcature;
+- due transizioni sono in __conflitto effettivo__ se sono entrambe abilitate in una marcatura \\(M\\) ed esiste un posto in ingresso in comune ai due preset tale per cui il numero di gettoni in quel posto è minore della somma dei pesi dei due flussi che vanno dal posto alla transizione (quindi il posto in ingresso non ha abbastanza gettoni per far scattare entrambe le transizioni).
+Entrano quindi in conflitto sulla disponibilità di gettoni nel preset.
+
+Esiste una versione __rilassata__ della definizione di conflitto esplicitata dalla seguente formula:
+
+$$
+\boxed{M \\ [ \\ t_1 >} \\: \land \\: \boxed{M \\ [ \\ t_2 >} \\: \land \\: \lnot \\, \boxed{M \\ [ \\ t_1 t_2 >}.
+$$
+
+Questa proposizione indica che il conflitto è presente se \\(t_1\\) e \\(t_2\\) sono abilitate in una marcatura \\(M\\) e non è possibile la sequenza \\(t_1\\) \\(t_2\\) a partire da \\(M\\).
+Ma cosa vuol dire che è una _versione rilassata_? 
+Per capirlo si osservi questo l'esempio sottostante:
+
+![Esempio conflitto](/assets/14_esempio-conflitto1.png)
+
+Secondo le definizioni di conflitto che sono state date, in questa rete di Petri è presente un conflitto sia per la prima definizione che per la seconda.
+È possibile però fare in modo che rimanga in conflitto per la prima definizione data ma non più per la definizione rilassata introducendo una piccola modifica:
+
+![Esempio conflitto differenza](/assets/14_esempio-conflitto1-differenza.png)
+
+Aggiungendo una relazione tra \\(t_1\\) a \\(p_1\\) si può notare che dopo lo scatto di \\(t_1\\) quest'ultima è ancora abilitata e quindi non rientra più sotto la definizione rilassata di conflitto.
+
+Lasciando da parte la definizione rilassata, è facile osservare a questo punto che la definizione per il conflitto strutturale si basa solo sui preset, ignorando quindi qualsiasi arco in uscita, mentre la quella per il conflitto effettivo ragiona anche sugli effetti dello scatto delle transizioni. Si noti che la presenza di un conflitto strutturale __non implica__ obbligatoriamente la presenza di un conflitto effettivo in quanto quest'ultimo per esistere necessita che venga soddisfatta una condizione in più.
+Al contrario invece un conflitto effettivo __implica__ la presenza di un conflitto strutturale in qunato le condizioni di quest'ultimo sono comprese in quelle del conflitto effettivo. \
+Di seguito viene mostrato un esempio di conflitto _effettivo_ e _strutturale_.
+
+![Esempio conflitto effettivo e strutturale](/assets/14_conflitto-effettivo-e-strutturale.png)
diff --git a/src/15_reti-petri/03_relazioni/03_concorrenza.md b/src/15_reti-petri/03_relazioni/03_concorrenza.md
new file mode 100644
index 0000000000000000000000000000000000000000..0f962afa3545563e4a1bb82a0c80a460a5fc43b8
--- /dev/null
+++ b/src/15_reti-petri/03_relazioni/03_concorrenza.md
@@ -0,0 +1,18 @@
+# Concorrenza
+
+È in qualche modo intuitivo considerare la relazione di concorrenza come la relazione opposta alla relazione di conflitto: due transizioni \\((t_1, \\, t_2)\\) sono in:
+- __concorrenza strutturale__ \\(\Longleftrightarrow \operatorname{Pre}(t_1) \cap \operatorname{Pre}(t_2) = \varnothing\\);
+- __concorrenza effettiva__ in una marcatura \\(M \Longleftrightarrow\\)
+    - \\(\boxed{M \\ [ \\ t_1 >} \cap \boxed{M \\ [ \\ t_2 >}\\);
+    - \\(\forall p \in \operatorname{Pre}(t_1) \cap \operatorname{Pre}(t_2) \quad M(p) \geq W(\langle p, \\, t_1 \rangle) + W(\langle  p, \\, t_2\rangle)\\).
+
+Quest'ultima formula indica che due identificatori delle transizioni sono in concorrenza effettiva se e solo se per tutti i posti che hanno in comune c'è un numero di gettoni sufficienti per farle scattare entrambe.
+
+In questo caso non esiste alcun legame tra concorrenza strutturale ed effettiva, diversamente da quanto abbiamo visto in precedenza per le relazioni di conflitto.
+Se si verificano le condizioni per avere una concorrenza strutturale è __possibile__ che le due transizioni non siano abilitate, oppure se si verificano le condizioni per avere concorrenza effettiva è __possibile__ che \\(t_1\\) e \\(t_2\\) abbiano posti in comune che posseggano abbastanza gettoni per entrambe.
+
+Questo però non esclude il fatto che sia possibile avere concorrenza strutturale ed effettiva contemporaneamente, infatti di seguito sono riportati degli esempi che confermano ciò:
+
+![Esempio concorrenza](/assets/14_esempio-concorrenza.png)
+
+Ovviamente è anche possibile che non ci sia alcun tipo di concorrenza: è sufficiente che due transizioni abbiano in comune un posto e una delle due non sia abilitata.
diff --git a/src/15_reti-petri/04_insieme-raggiungibilita.md b/src/15_reti-petri/04_insieme-raggiungibilita.md
new file mode 100644
index 0000000000000000000000000000000000000000..e47ff36392c3565bca947345e497c01bbf8bbcd2
--- /dev/null
+++ b/src/15_reti-petri/04_insieme-raggiungibilita.md
@@ -0,0 +1,14 @@
+<span style="display: none;">\\(\def\pt{\mathcal{P/T}}\\)</span>
+
+# Insieme di raggiungibilità 
+
+L'insieme di raggiungibilità \\(R\\) di una rete \\(\pt\\) a partire da una marcatura \\(M\\) è il più piccolo insieme di marcature tale che:
+- \\(M \in R(\pt, \\, M)\\);
+- \\(M' \in R(\pt, \\, M) \land \exists t \in T \quad \boxed{\boxed{M' \\ [\\ t >} \\, M''} \Longrightarrow M'' \in R(\pt, \\, M)\\).
+
+Questa definizione induttiva viene interpretata nel seguente modo:
+- __passo base__: la marcatura \\(M\\) appartiene all'insieme di raggiungibilità \\(R(\pt, \\, M)\\) \\
+(\\(M\\) indica la marcatura iniziale mentre \\(\pt\\) indica la rete posti-transizioni);
+- __passo induttivo__: se \\(M'\\) appartiene all'insieme di raggiungibilità (quindi si dice che _è raggiungibile_) ed esiste una transizione della rete tale per cui è abilitata in \\(M'\\) e porta in \\(M''\\) &mdash; per cui con uno scatto è possibile passare dalla marcatura \\(M'\\) alla marcatura \\(M''\\) &mdash; _allora_ anche quest'ultima è __raggiungibile__.
+
+Procedendo ricorsivamente con questa definizione è possibile ottenere tutte le marcature raggiungibili.
diff --git a/src/15_reti-petri/05_limitatezza.md b/src/15_reti-petri/05_limitatezza.md
new file mode 100644
index 0000000000000000000000000000000000000000..7a09d775cf3f3025271a28f921698e486448a38a
--- /dev/null
+++ b/src/15_reti-petri/05_limitatezza.md
@@ -0,0 +1,27 @@
+<span style="display: none;">\\(\def\pt{\mathcal{P/T}}\\)</span>
+
+# Limitatezza
+<span id="limitatezza"></span>
+Una proprietà importante delle reti di Petri è la __limitatezza__, che indica se le possibili evoluzioni della rete possono essere limitate o illimitate, quindi se gli stati raggiungibili sono in numero finito oppure infiniti.
+Volendo dare una definizione più formale è possibile dire che una rete posti-transizioni (\\(\pt\\)) con marcatura \\(M\\) si dice __limitata__ se e solo se:
+
+$$
+\exists k \in \mathbb N, \\: \forall M' \! \in R(\pt, \\, M), \\: \forall p \in P \quad M'(p) \leq k
+$$
+
+cioè se esiste un numero naturale \\(k\\) tale per cui per ogni marcatura \\(M'\\) raggiungibile da \\(M\\), per ogni posto \\(p\\) all'interno della rete il numero di gettoni in quella marcatura _raggiungibile_ è minore o uguale di \\(k\\) &mdash; ovvero se è possibile porre un numero finito tale per cui dopo qualsiasi evoluzione non esista alcun posto che possiede un numero di gettoni maggiore di \\(k\\) &mdash; allora è possibile affermare che **la rete è limitata**. \
+Se ciò non si verifica esiste almeno un posto in cui è possibile aumentare tendenzialmente all'infinito il numero di gettoni, tramite una certa evoluzione della rete.
+È importante sottolineare che la limitatezza di una rete può dipendere dalla sua **marcatura iniziale**.
+
+![Esempio rete illimitata](/assets/14_esempio-rete-illimitata.png)
+
+## Da reti di Petri a automi
+
+Precedentemente è stato mostrato come a partire da un automa a stati finiti sia possibile ricavare una rete di Petri, ma è possibile fare **il contrario**?
+Se la rete è limitata allora l'insieme di raggiungibilità è finito, di conseguenza è possibile definire un corrispondente automa a stati finiti che prende ogni marcatura raggiungibile come un proprio _stato_ e ne traccia le transizioni di stato dell'automa conseguenti alla transizione scattata nella rete di Petri.
+Due considerazioni:
+- gli **stati** sono le possibili marcature dell'insieme di raggiungibilità;
+- le **transizioni** sono gli eventi che permettono il passaggio da una configurazione alla successiva.
+
+Riuscire a passare dalle reti di Petri agli automi ci permette di modellare un problema in modo più sintetico, ma allo stesso tempo rimane possibile utilizzare i **tool di analisi** che sfruttano proprietà già consolidate per gli automi.
+L'unico problema è che questo approccio vale solo per **reti limitate**.
diff --git a/src/15_reti-petri/06_vitalita.md b/src/15_reti-petri/06_vitalita.md
new file mode 100644
index 0000000000000000000000000000000000000000..f856958d12e719a2bd26250a9e64b57e9036d5e0
--- /dev/null
+++ b/src/15_reti-petri/06_vitalita.md
@@ -0,0 +1,32 @@
+# Vitalità di una transizione
+
+Una transizione \\(t\\) in una marcatura \\(M\\) si può dire _viva_ con un certo __grado__:
+- __grado 0__ (o __morta__): non è abilitata in nessuna marcatura appartanente all'insieme di raggiungibilità, quindi qualunque evoluzione avvenga nella rete, la transizione non portà mai scattare (non è sempre un aspetto negativo);
+- __grado 1__: esiste almeno una marcatura raggiungibile a partire da \\(M\\) in cui la transizione è abilitata;
+- __grado 2__: per ogni numero \\(n\\) naturale escluso lo zero esiste almeno una sequenza di scatti ammissibile a partire da \\(M\\) in cui la transizione scatta \\(n\\) volte, ovvero è possibile far scattare la transizione un numero \\(n\\) grande a piacere di volte;
+- __grado 3__: esiste una sequenza di scatti ammissibile a partire da \\(M\\) per cui la transizione scatta _infinite_ volte;
+- __grado 4__ in _qualunque marcatura raggiungibile_ esiste una sequenza ammissibile in cui è possibile far scattare la transizione almeno una volta, di conseguenza può scattare infinite volte in qualunque situazione ci si trovi (ovvero in qualunque marcatura).  \
+In questo caso si dice che la transizione è __viva__ _in maniera assoluta_.
+
+Si noti come il concetto di _\\(n\\) grande a piacere_ presente nel grado 2 sia differente dal concetto di _infinite volte_ nel grado.
+
+Gli esempi seguenti rappresentano delle situazioni verosimili riguardanti la vitalità delle transizioni:
+- __grado 0__: qualunque cosa accada _la centrale nucleare non può esplodere_;
+- __grado 1__: in un certo momento se si assume il controllo di tutto ciò che avverrà _è possibile portare la centrale nucleare allo spegnimento_;
+- __grado 2__: Duccio, ingegnere della centrale nucleare che si trova in coffee break, è in grado di interagire con la macchinetta del caffé appena accesa in modo da avere un numero di caffé _grande a piacere_, almeno finchè qualcuno non inserisce una moneta nella macchinetta;
+- __grado 3__: Biascica, guardia giurata della centrale, è in grado di fare alzare la sbarra per il parcheggio _un numero infinito di volte_;
+- __grado 4__: se succede qualcosa fuori dal controllo all'interno della centrale si può comunque riuscire ad eseguire lo spegnimento (René urla _"chiudi tutto, Duccio!"_).
+
+Una rete viene chiamata __viva__ quando tutte le sue transizioni sono vive.
+
+## Esempio
+
+![Esempio vitalità tranisizioni](/assets/14_esempio-vitalita-transizioni.png)
+
+- Da questo esempio pratico è possibile notare come la transizione \\(t_0\\) è di **grado 0** in quanto non potrà mai scattare, perchè è impossibile che abbia i gettoni necessari nel preset per scattare (al massimo o in \\(p_0\\) o in \\(p_1\\)).
+- La transizione \\(t_1\\) è di **grado 1** perchè esiste almeno una marcatura raggiungibile per cui essa scatti, infatti la marcatura corrente è quella che ne _permette_ lo scatto (ricordando ancora che se una transizione è abilitata allo scatto non significa che debba scattare).
+- Osservando la transizione \\(t_3\\) è possibile notare che essa scatti infinite volte (e non \\(n\\) grande a piacere, quindi non si tratta di una transizione di grado 2), ma nel caso avvenga lo scatto di \\(t_1\\) la transizione \\(t_3\\) non potrà mai più essere abilitata (quindi esiste una marcatura in cui non sarà possibile il suo scatto) garantendo che non si tratta di una transizione di grado 4, ma bensì di **grado 3**.
+- Il caso più particolare è quello della transizione \\(t_2\\): è noto che \\(t_3\\) può scattare infinite volte e quindi in \\(p_2\\) possono esserci infiniti gettoni; inoltre, conseguentemente allo scatto di \\(t_1\\) il posto \\(p_1\\) conterrà un gettone, ma comunque la transizione \\(t_2\\) non può scattare infinite volte.
+Questo perchè è vero che all'infinito posso generare gettoni in \\(p_2\\), ma dal momento che scatta \\(t_1\\) si perde questa possibilità, permettendo a \\(t_2\\) di scattare tante volte quanti sono i gettoni in \\(p_2\\). 
+La transazione è quindi di **grado 2**.
+- Infine \\(t_4\\) è una transizione viva (di **grado 4**), perchè qualunque sia la marcatura raggiungibile dalla marcatura corrente è possibile prendere il controllo e sicuramente esiste una sequenza di scatti tale per cui \\(t_4\\) diventi abilitata.
diff --git a/src/15_reti-petri/07_capacita.md b/src/15_reti-petri/07_capacita.md
new file mode 100644
index 0000000000000000000000000000000000000000..a592daedef1fbf0de8f8b174ddf27eab3766a78b
--- /dev/null
+++ b/src/15_reti-petri/07_capacita.md
@@ -0,0 +1,75 @@
+<span style="display: none;">\\(\def\pt{\mathcal{P/T}}\\)</span>
+
+# Capacità dei posti 
+
+Inizialmente è stato detto che esistono diversi dialetti riguardanti le reti di Petri.
+Una possibile estensione consiste infatti nel fissare una **capacità massima** rispetto al numero di gettoni ammissibili in un posto.
+Un esempio potrebbe essere quello in cui in un sistema possono essere presenti \\(k\\) lettori contemporaneamente e non più di \\(k\\).
+Avendo la possibilità di definire una capacità dei posti, è facile intuire che diventa possibile _forzare la limitatezza della rete_.
+
+Tale estensione aumenta la potenza espressiva oppure è semplicemente una scorciatoia?
+Tramite l'esempio sottostate si può notare che questa estensione non è altro che una tecnica per facilitare la scrittura della rete.
+
+![Simulazione capacità posti](/assets/14_simulazione-capacita-posti.png)
+
+Nella rete con capacità dei posti limitata per far sì che ad esempio la transizione \\(t_0\\) scatti, è necessario sia che i posti nel suo preset abbiano gettoni sufficienti sia che dopo il suo scatto il posto \\(p_0\\) non superi il limite assegnatogli.
+Volendo scrivere la stessa rete utilizzando il metodo classico visto fino ad ora basta aggiungere un __posto complementare__ che quindi rende le reti __equipollenti__, ossia aventi lo stesso valore espressivo.
+
+Fino a che nel posto complementare esistono dei gettoni, la transizione \\(t_0\\) può infatti scattare; dal momento però che tutti i gettoni di \\(p_0(\text{compl})\\) vengono bruciati, \\(t_0\\) non sarà più abilitata e nel posto \\(p_0\\) ci sarà il numero massimo di gettoni possibili.
+Notare come la somma dei gettoni del posto considerato sia esattamente la capacità massima scelta in precedenza.
+
+Questa proprietà vale solo per le reti __pure__, ovvero _le reti che_ ___per ogni transizione___ _hanno preset e postset disgiunti_.
+
+## Posto complementare
+Un posto complementare è un posto avente in uscita verso ognuna delle transizioni del posto considerato un **arco di ugual peso** ma di **direzione opposta**.
+
+Matematicamente è possibile scivere questa definizione nel seguente modo: \
+un posto \\(pc\\) è _complementare_ di \\(p\\) se e solo se
+
+$$
+\begin{align}
+\forall t \in T \\: \Big [ \exists \langle p, \\, t \rangle \in F &\Longleftrightarrow \exists \langle t, \\, pc \rangle \in F \quad W(\langle p,\, t \rangle) = W(\langle t, \\, pc \rangle) \Big ] \\\\
+\land \
+\forall t \in T \\: \Big [ \exists \langle t, \\, p \rangle \in F
+&\Longleftrightarrow \exists \langle pc, \\, t \rangle \in F \quad W(\langle pc, \\, t \rangle) = W(\langle t, \\, p \rangle) \Big ] .
+\end{align}
+$$
+
+Per ogni transizione appartenente a \\(T\\) in uscita da \\(p\\), quindi tale per cui esiste una relazione di flusso dal posto \\(p\\) alla transizione \\(t\\) deve esistere un flusso che va dalla transizione \\(t\\) al posto complementare \\(pc\\) avente lo stesso peso. \
+Inoltre, per le transizioni in ingresso al posto \\(p\\) (quindi per ogni transizione \\(t\\) appartenente a \\(T\\) in ingresso a \\(p\\)) tali per cui esista un flusso da \\(t\\) al posto \\(p\\), deve esistere un flusso che va dal posto complementare \\(pc\\) a \\(t\\) di direzione opposta e avente lo stesso peso.
+
+Questa formula garantisce che la **somma** del numero di gettoni tra il posto e il suo complementare sia costante, permettendo quindi di formulare la **condizione di abilitazione** (lavorando sul preset della transizione) in modo da dipendere anche dal numero di gettoni presenti nel posto in arrivo.
+
+## Abilitazione con capacità
+Come è possibile definire la condizione di abilitazione nel caso di **reti con capacità sui posti**?
+
+La definizione di _abilitazione_ per reti con capacità sui posti è la seguente: \
+\\(t \in T\\) è __abilitata__ in \\(M\\) se solo se:
+
+$$
+\begin{align*}
+\forall p \in \operatorname{Pre}(t) &\quad M(p) \geq W(\langle p, t \rangle) \\\\
+\forall p \in \operatorname{Post}(t) \setminus \operatorname{Pre}(t) &\quad M(p) + W(\langle t, p \rangle) \leq C(p) \\\\
+\forall p \in \operatorname{Post}(t) \cap \operatorname{Pre}(t) &\quad M(p) - W(\langle p, t \rangle) + W(\langle t, p \rangle) \leq C(p).
+\end{align*}
+$$
+
+Considerando l'immagine seguente, infatti, possiamo notare come la rete di sinistra abbia ancora una transazione abilitata, mentre quella di destra no.
+Nella seconda rete è come se **lo scatto venisse spezzato in due fasi**: la prima in cui vengono generati i gettoni nel posto (in questo caso \\(p_3\\)), la seconda invece in cui vengono tolti tanti gettoni quanto è il peso dell flusso da \\(p_3\\) a \\(t_1\\). \
+Nella prima rete invece questo non accade, è come se si verificasse tutto nello stesso istante.
+
+![Esempio abilitazione reti con capacità](/assets/14_esempio-abilitazione-reti-capacita.png)
+
+A questo punto, ci si potrebbe chiedere se fosse possibile generare la situazione equivalente nel caso di una rete \\(\pt\\) classica: la risposta è **no**, ad eccezione del caso in cui si usano delle reti con posti complementari.
+Utilizzando i __posti complementari__ è infatti possibile rappresentare **solo le _reti pure equivalenti_**, ma _non tutte le reti in generale_: finché non sono presenti archi in entrata e uscita allo stesso posto dalla stessa transizione non sorge alcun tipo di problema.
+
+Come è possibile superare questa limitazione? 
+Si possono pensare due approcci:
+- si trova un altro approccio diverso dai posti complementari;
+- si cerca di dimostrare che una rete non pura ha sempre una equivalente rete pura; \
+quindi, si procede a rimuovere la capacità utilizzando i posti complementari.
+
+Entrambe le soluzioni non sono così immediate.
+
+<!-- aggiungere esempio / marcature pure / pure-equivalenti / ecc .. -->
+<!-- Si è fermato a questo punto durante la lezione, nella lezione 20 non ha spiegato ancora quale approccio utilizzare -->
diff --git a/src/15_reti-petri/08_archi-inibitori.md b/src/15_reti-petri/08_archi-inibitori.md
new file mode 100644
index 0000000000000000000000000000000000000000..b3dcc4524304ea7dc595827f0b44cf36127634f1
--- /dev/null
+++ b/src/15_reti-petri/08_archi-inibitori.md
@@ -0,0 +1,14 @@
+# Archi inibitori
+
+Esiste un'altra estenzione delle reti di petri in cui si utilizzano gli __archi inibitori__, ovvero degli archi che indicano la situazione in cui una transizione ha bisogno che **non siano presenti gettoni nel posto** in modo che possa essere abilitata.
+Un _arco inibitore di peso \\(n\\)_ indica che la transazione collegata è abilitata se nel posto collegato sono presenti **meno di** \\(n\\) gettoni.
+
+In caso di **rete limitata** la **potenza espressiva** di una rete che sfrutta gli archi inibitori **non cambia**, perché esistendo un limite massimo \\(k\\) di gettoni all'interno della rete sarà sufficiente creare un posto complementare contente un numero di gettoni tali per cui la somma tra quest'ultimi e i gettoni presenti nel posto considerato sia minore di \\(k\\). \
+A questo punto è necessario che siano presenti due archi (uno in ingresso e uno in uscita) di peso \\(k\\), in modo da permettere lo scatto della transizione solo nel caso in cui tutti i gettoni siano all'interno del posto complementare. \
+In realtà **non è necessario** che tutta la rete sia limitata, è sufficiente che il singolo posto lo sia: è necessario garantire che qualunque sia lo _stato generale_ della rete, in quel preciso posto non ci siano più di \\(k\\) gettoni.
+
+Nel caso di una rete **non limitata** invece non è sempre possibile avere una traduzione equivalente della rete di Petri: la **potenza espressiva** delle reti con gli archi inibitori **aumenta**.
+
+Il problema degli archi inibitori è che rendono **inutilizzabili** alcune **tecniche di analisi** che verranno affrontate successivamente.
+
+<!-- *nessuno* ne sentiva la mancanza :) -->
diff --git a/src/15_reti-petri/09_pesi-archi.md b/src/15_reti-petri/09_pesi-archi.md
new file mode 100644
index 0000000000000000000000000000000000000000..7feb32ac5c00f503d182f33356dee1a09a8c72d3
--- /dev/null
+++ b/src/15_reti-petri/09_pesi-archi.md
@@ -0,0 +1,39 @@
+<span style="display: none;">\\(\def\pt{\mathcal{P/T}}\\)</span>
+
+# Eliminazione pesi sugli archi
+
+In precedenza è stato accennato che per ogni rete avente dei pesi sugli archi è possibile crearne una **equivalente** senza pesi sugli archi (ovvero avente tutti gli archi di peso 1). \
+Per fare ciò è necessario considerare due casi distinti, ovvero quello con peso sugli archi in **ingresso ad una transizione** e quello con peso sugli archi **in uscita** ad una traniszione.
+
+## Pesi su archi in ingresso
+
+Per poter effettuare questa modifica è necessario avere lo **scatto di una nuova transizione** (in quanto ovviamente non è possibile collegare due archi dallo stesso posto alla stessa transizione), ma non basta.
+Dopo lo scatto di \\(t_0\\) è infatti possibile che \\(t_0^\text{BIS}\\) non scatti e la rete evolva senza che in \\(p_1\\) ci sia il giusto numero di gettoni (problema di concorrenza).
+
+Per risolvere questo problema si sfrutta una sorta di __lock__, ovvero un posto collegato bidirezionalmente con tutte le transizioni della rete tranne per \\(t_0\\), a cui è collegato solo in ingresso, e per \\(t_0^\text{BIS}\\), a cui è collegato solo in uscita.
+In questo modo è come se lo scatto di \\(t_0\\) sia scomposto logicamente in due parti: quando \\(t_0\\) scatta viene attivato il lock in modo tale che nessun'altra transizione sia abilitata e, successivamente, lo scatto di \\(t_0^\text{BIS}\\) lo rilascia.
+Questo ovviamente non obbliga \\(t_0^\text{BIS}\\) a scattare immediatamente, però è certo che la rete non potrà evolvere in alcun altro modo e quindi non si creeranno marcature non esistenti nella rete originale.
+Questa soluzione non è molto elegante perchè esiste un posto avente in ingresso un arco per ogni transizione della rete.
+
+![Eliminazione pesi archi ingresso](/assets/14_eliminazione-archi-ingresso.png)
+
+## Pesi su archi in uscita
+
+In questo caso il peso da rimuovere è su un arco che esce da un posto ed entra in una transizione, quindi è necessario che vengano **distrutti due gettoni** dallo stesso scatto. \
+L'approccio da utilizzare è simile: è infatti presente un **posto globale** che fa da **lock** in modo da risolvere il problema di concorrenza tra \\(t_8\\) e \\(t_1\\).
+In questo caso però è presente un ulteriore problema, ovvero al momento dello scatto di \\(t_8\\) il gettone in \\(p_0\\) viene consumato, di conseguenza \\(t_1\\) non può scattare. Inoltre il resto della rete rimane bloccata, in quanto all'interno del posto globale non è più presente il gettone che è stato consumato sempre dallo scatto di \\(t_8\\). \
+Questo **deadlock** può essere risolto aggiungendo un controllo sul posto \\(p_0\\), in modo tale che possa scattare solo quando possiede due o più gettoni: in questo modo non può verificarsi la situazione in cui \\(t_8\\) scatti senza un successivo scatto di \\(t_1\\).
+
+Il meccanismo della rete inizia ad essere **molto complesso**; nell'esempio viene mostrato solo il caso in cui devono essere consumati due gettoni.
+In altri casi con più gettoni, o con situazioni differenti, la rete aumenterebbe ulteriormente di complessità. 
+Risulta quindi più facile pensare la rete in modo differente.
+
+La tenica descritta sopra non è infatti l'unica esistente per modellare il sistema: nonostante possa essere adatta per questo particolare esempio, è comunque possibile trovarne un'altra per modellare una rete senza fruttare i pesi o una loro **traduzione meccanica**.
+
+![Eliminazione pesi archi uscita](/assets/14_eliminazione-archi-uscita.png)
+
+## Reti \\(\mathcal{C/E}\\)
+Le reti \\(\mathcal{C/E}\\) (condizioni eventi) sono delle particolari reti **più semplici**, in cui tutti gli archi hanno **peso uno** e tutti i posti hanno capacità massima uno.
+A prima vista, questo tipo di rete può risultare poco modellabile, ma è in realtà più semplice ed immediata da capire: infatti _i posti rappresentano delle condizioni_ che possono essere __vere__ o __false__ ed in base ad esse è possibile il verificarsi di certi eventi, rappresentati dalle transizioni.
+Ogni rete \\(\pt\\) __limitata__ è **traducibile** in un'equivalente rete \\(\mathcal{C/E}\\). \
+Per le reti illimitate non è invece possibile trovare una traduzione, siccome non si possono rappresentare infiniti stati con un tipo di rete che per definizione è limitata.
diff --git a/src/15_reti-petri/10_conservativita.md b/src/15_reti-petri/10_conservativita.md
new file mode 100644
index 0000000000000000000000000000000000000000..282cb823ab0a8d61662947ac97aff8d506a0462b
--- /dev/null
+++ b/src/15_reti-petri/10_conservativita.md
@@ -0,0 +1,53 @@
+<span style="display: none;">\\(\def\pt{\mathcal{P/T}}\\)</span>
+
+# Conservatività 
+La conservatività è una proprietà di una rete rispetto ad una funzione \\(H\\) che assegna un peso ad ogni posto della rete, e ognuno di questi pesi è positivo. \
+Esiste quindi una **funzione di pesi** \\(H: P \rightarrow \mathbb N \setminus \{ 0 \}\\) tale per cui una rete \\(\pt\\) con una marcatura \\(M\\) si dice __conservativa rispetto ad \\(H\\)__ se e solo se:
+
+$$
+\forall M' \in R(\pt, \\, M) \quad \sum_{p \in P} H(p) M'(p) = \sum_{p \in P} H(p) M(p).
+$$
+
+Ovvero, per ogni marcatura \\(M'\\) raggiungibile dalla marcatura iniziale data una certa marcatura e una funzione \\(H\\), si dice che la rete è conservativa se la sommatoria dei gettoni di ogni posto (quest'ultimi pesati attraverso la funzione \\(H\\)) è _**costante** per qualunque marcatura raggiungibile_.
+
+## Conservatività \\(\Rightarrow\\) limitatezza
+
+Esiste inoltre un **legame** tra **conservatività** e **limitatezza**, ovvero _una rete che garantisce la conservatività è limitata, ma non è detto il viceversa_ (quindi _la limitatezza è una condizione necesaria ma non sufficiente per la conservatività_).
+
+### Dimostrazione
+
+Assumendo che \\(\sum_{p \in P} H(p) M(p)=k\\), allora
+
+$$
+\forall M' \in R(\pt, \\, M) \quad \sum_{p \in P} H(p) M'(p) = k.
+$$
+
+Sapendo inoltre che \\(\forall p \in P \quad H(p) > 0\\), allora ogni elemento della sommatoria ha un **contributo nullo o positivo**.
+Infatti, se non ci sono gettoni all'interno del posto il contributo della sommatoria sarà un numero positivo (\\(H(p)\\)) moltiplicato per 0, quindi nullo. 
+
+Quindi, se esiste almeno una marcatura di \\(p\\) cui numero di gettoni è diverso da 0, il suo contributo è positivo ma limitato da \\(k\\).
+Questo vale per ogni posto all'interno della rete, riconducendosi di conseguenza alla definizione di <a href="#limitatezza">limitatezza</a>. 
+<span style="float: right"> \\(\blacksquare\\) </span>
+
+## Rete strettamente conservativa
+La _conservatività stretta_ è un particolare caso di conservatività definibile come segue: una rete \\(\pt\\) conservativa rispetto alla funzione \\(H\\) che assegna pesi tutti uguali a 1 si dice _strettamente conservativa_.
+
+$$
+\forall M' \in R(\pt, \\, M) \quad \sum_{p \in P} M'(p) = \sum_{p \in P} M(p).
+$$
+
+La sommatoria del numero di gettoni per ogni posto in una _qualsiasi marcatura_ è **costante**, ovvero è uguale alla sommatoria dei gettoni della marcatura iniziale per ogni posto. 
+In altre parole, dopo lo scatto di una transazione viene forzata la **distruzione del gettone in ingresso** e la **generazione di un'altro in uscita**. 
+
+Matematicamente questo concetto si può esprimere anche tramite questa espressione:
+
+$$
+\forall t \in T \quad \sum_{p \in \operatorname{Pre}(t)} W(\langle p, \\,  t \rangle) = \\! \sum_{p \in \operatorname{Post}(t)} \\! W(\langle t, \\, p \rangle)
+$$
+
+Per ogni transizione \\(t\\) la somma dei pesi degli archi che collegano ogni elemento del preset di \\(t\\) alla transizione \\(t\\) deve essere uguale alla sommatoria dei pesi degli archi che collegano la transizione \\(t\\) con ogni posto nel postset di \\(t\\).
+
+Le due espressioni sopra esprimono lo stesso concetto, ma la prima si riferisce alle **marcature** (stati) analizzando dinamicamente calcolando gli stati raggiungibili mentre l'altra all'**aspetto topologico** della rete (ovvero i pesi degli archi).
+
+Si precisa che per quanto riguarda la seconda formula, le espressioni da considerare sono quelle __non morte__ (di grado \\(\geq 1\\)).
+La seconda è anche più generale rispetto alla prima, ma potrebbe erroneamente considerare **non** strettamente conservative reti che **invece lo sono**.
diff --git a/src/15_reti-petri/11_stato-base.md b/src/15_reti-petri/11_stato-base.md
new file mode 100644
index 0000000000000000000000000000000000000000..ca9cf5c1e65ab3d3df99ab3ea70bb2fa673e35e0
--- /dev/null
+++ b/src/15_reti-petri/11_stato-base.md
@@ -0,0 +1,5 @@
+## Stato base e rete revertibile
+
+Una marcatura \\(M'\\) si dice __stato base__ di una rete se per ogni marcatura \\(M\\) in \\(R(M_0)\\), \\(M'\\) è raggiungibile da \\(M\\), ovvero _qualunque_ sia lo stato attuale della rete è **sempre possibile** raggiungere la marcatura \\(M'\\).
+
+Quando la marcatura iniziale \\(M_0\\) è lo stato base della rete per ogni marcatura \\(M\\) in \\(R(M_0)\\) allora la rete si dice __reversibile__, ovvero lo stato iniziale è uno stato base.
diff --git a/src/16_reti-petri-analisi/00_index.md b/src/16_reti-petri-analisi/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..acf8b968594f9e45259c517e89af5b5f7f9cc189
--- /dev/null
+++ b/src/16_reti-petri-analisi/00_index.md
@@ -0,0 +1,27 @@
+# Analisi delle reti di Petri
+
+Le reti di Petri sono state introdotte per poter **analizzare un sistema** ancora prima di avere il codice.
+Alcune domande da porsi sono:
+
+- può essere raggiunta una determinata marcatura?
+- è possibile una certa sequenza di scatti?
+- esiste uno stato di deadlock all'interno della rete?
+- la rete (o una certa transizione) è viva? E di che grado?
+
+Per rispondere a queste domande esistono diverse tecniche, suddivise in:
+
+- **tecniche dinamiche**:
+  - albero (grafo) delle marcature raggiungibili (chiamato anche **grafo di raggiungibilità**);
+  - albero (grafo) di copertura delle marcatura raggiungibili (chiamato anche **grafo di copertura**);
+- **tecniche statiche**:
+  - identificazione delle **\\(P\\)-invarianti** (caratteristiche invarianti riguardanti i posti);
+  - identificazione delle **\\(T\\)-invarianti** (caratteristiche invarianti riguardanti alle transizioni).
+
+Le tecniche dinamiche ragionano sugli **stati raggiungibili** durante l'esecuzione della rete di Petri (o di un programma), mentre le statiche sulla **topologia della rete**.
+
+  - [**Albero di raggiungibilità**](./01_albero-raggiungibilita.md)
+  - [**Albero di copertura**](./02_albero-copertura.md)
+  - [**Rappresentazione matriciale**](./03_rappresentazione-matriciale.md)
+  - [**Analisi statica**](./04_analisi-statica/00_index.md)
+  - [**Controllori con specifica a stati proibiti**](./05_controllori.md)
+  - [**Reti con priorità**](./06_reti-priorita.md)
diff --git a/src/16_reti-petri-analisi/01_albero-raggiungibilita.md b/src/16_reti-petri-analisi/01_albero-raggiungibilita.md
new file mode 100644
index 0000000000000000000000000000000000000000..c6f7bdb6e61ce738e4664b95d876f7490001a407
--- /dev/null
+++ b/src/16_reti-petri-analisi/01_albero-raggiungibilita.md
@@ -0,0 +1,157 @@
+<style>
+  .algorithm p {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+</style>
+<div style="display: none">
+$$
+\require{color}
+\def\node#1{\fcolorbox{black}{white}{#1}}
+\def\nodenew#1{\fcolorbox{lime}{white}{#1}}
+$$
+</div>
+
+# Albero di raggiungibilità
+
+Per generare l'_albero di raggiungibilità_ di una rete di Petri si può applicare il seguente **algoritmo**.
+
+<ol class="algorithm">
+  <li>
+
+  **crea la radice** dell'albero corrispondente alla marcatura iniziale \\(M_0\\) ed etichettala come _nuova_;
+  </li>
+  <li>
+
+  **_<u>finché</u>_ esistono nodi etichettati come _"nuovi"_** esegui:
+  <ol>
+  <li>
+
+  **seleziona** una marcatura \\(M\\) etichettata come _"nuova"_; \
+    prendila in considerazione e **rimuovi l'etichetta** _"nuova"_.
+  </li>
+  <li>
+
+  ***<u>se</u>*** la **marcatura** \\(M\\) è **identica** ad una marcatura di un altro nodo allora:
+  - **etichetta** \\(M\\) come **"duplicata"**;
+  - ***<u>continua</u>*** passando alla prossima iterazione.
+  </li>
+  <li>
+
+  ***<u>se</u>*** nella **marcatura** \\(M\\) non è abilitata **nessuna transizione** allora:
+  <ul>
+  <li>
+
+  **etichetta** \\(M\\) come **"finale"**;
+  </li>
+  <li>
+
+  _situazione di deadlock_.
+  </li>
+  </ul>
+
+  ***<u>altrimenti</u>*** esegui:
+  <ul>
+  <li>
+
+  ***<u>finché</u>* esistono transizioni abilitate** in \\(M\\) esegui:
+  <ul>
+  <li>
+
+  ***<u>per ogni</u> transizione* \\(t\\) abilitata** in \\(M\\) esegui:
+  <ol>
+  <li>
+
+  **crea** la **marcatura** \\(M'\\) prodotta dallo **scatto** di \\(t\\);
+  </li>
+  <li>
+
+  **crea** un nuovo **nodo** corrispondente alla marcatura \\(M'\\);
+  </li>
+  <li>
+
+  **aggiungi** un **arco** nell'albero al nodo corrispondente di \\(M\\) al nodo di \\(M'\\);
+  </li>
+  <li>
+
+  **etichetta** la **marcatura** \\(M'\\) come **"nuova"**.
+  </li>
+  </ol>
+  </li>
+  </ul>
+  </li>
+  </ul>
+  </li>
+  </ol>
+  </li>
+</ol>
+
+## Esempio
+
+Di seguito è mostrata una consegna di un esercizio riguardo gli alberi di raggiungibilità.
+
+> Modellare tramite una rete di Petri l'accesso ad una risorsa condivisa tra quattro lettori e due scrittori, ricordandosi che i lettori possono accedere contemporaneamente, mentre gli scrittori necessitano di un accesso esclusivo.
+
+Come primo approccio, si possono creare due reti, una per i lettori e una per gli scrittori.
+È possibile successivamente procedere modellando la _Risorsa_ condivisa collegando le diverse parti create.
+
+![Esempio albero di raggiungibilità](/assets/15_esempio-1-albero-raggiungibilita.png)
+
+Essendo presente un solo gettone nel posto _Risorsa_, i **lettori** non sono
+in grado di accedervi contemporaneamente.
+Per risolvere questo problema, si può aumentare il numero di gettoni all'interno di _Risorsa_ a 4.
+Per evitare che gli scrittori possano accedere alla _Risorsa_ mentre viene letta, è possibile aggiungere un peso pari a 4 sugli archi da "_Risorsa"_ a _"S\_inizia"_ e da _"S\_finisce"_ a _"Risorsa"_.
+
+Così facendo, per accedere alla _Risorsa_ uno **scrittore** dovrà attendere che tutti i token saranno depositati in essa, garantendo che nessun'altro sta utilizzando la risorsa.
+
+Il **risultato finale** è il seguente.
+
+![Esempio albero di raggiungibilità completo](/assets/15_esempio-1-albero-raggiungibilita-rete-completa.png)
+
+### Costruzione dell'albero di raggiungibilità
+
+Una volta creata la rete finale, è possibile **generare** l'albero di raggiungibilità seguendo l'**algoritmo precedente**.
+
+Il primo passo è creare il **nodo radice** corrispondente alla marcatura iniziale e marcarlo come <span style="color: green"><i>nuovo</i></span>: \\(\nodenew{40420}\\).
+
+Successivamente, occorre procedere _per ogni nodo marcato come nuovo_.
+In questo caso l'unico nodo marcato come _nuovo_ è \\(\nodenew{40420}\\).
+Dopo aver rimosso l'etichetta _nuovo_ si verifica che, partendo dalla radice dell'albero, non siano già presenti altri nodi uguali.
+Essendo \\(\node{40420}\\) esso stesso la radice (e unico nodo dell'albero), si procede.
+
+A questo punto, per ogni transizione abilitata nella marcatura presa in considerazione (\\(\node{40420}\\)) la si fa **scattare** generando le altre marcature marcate come _nuovo_ (\\(\nodenew{40011}\\) e \\(\nodenew{31320}\\)) che quindi si **collegano** con un arco alla marcatura originale (\\(\node{40420}\\)).
+
+La situazione attuale è la seguente.
+
+![Esempio albero primo giro algoritmo](/assets/15_esempio-1-albero-prima-giro-algoritmo.png)
+
+Si procede quindi con l'algoritmo ripetendo i passi fino ad arrivare in una situazione in cui **non esistono più nodi nuovi**, marcando nel mentre come duplicati tutti i nodi che si re-incontrano nonostante siano già presenti almeno una volta nell'albero.
+
+La **situazione finale** sarà la seguente.
+
+![Esempio albero finale](/assets/15_esempio-1-albero-finale.png)
+
+L'albero di raggiungibilità sopra in figura è a ora **completo** e rappresenta tutti gli _stati_ raggiungibili.
+
+Grazie a questo albero, se si volesse verificare che gli scrittori sono in **mutua esclusione** con i lettori, basterà controllare se esiste una marcatura in cui il secondo e il quinto numero (rispettivamente _"LettoriAttivi"_ e _"ScrittoriAttivi"_) sono entrambi contemporaneamente maggiori di zero.
+Si può verificare in modo esaustivo (model checking) guardando tutti i nodi dell'albero.
+Inoltre si può verificare se gli **scrittori** si **escludono a vicenda**, controllando se in ogni marcatura l'ultimo numero (_"ScrittoriAttivi"_) è maggiore di uno.
+Si infine verificare l'assenza di **deadlock**, data dalla presenza o meno di nodi terminali.
+
+_Collassando_ i nodi aventi la stessa marcatura, si può verificare dall'albero di raggiungibilità se la rete è **viva**.
+
+![Esempio di grafo di raggiungibilità](/assets/15_grafo-di-raggiungibilita.png)
+
+La rete è anche **reversibile** in quanto ogni stato è uno _stato base_ ed è quindi possibile raggiungere da ogni stato tutti gli altri stati. \
+Avendo questo grafo è quindi facile capire che la rete è **viva**, in quanto sono rappresentate tutte le transizioni all'interno del grafo, e siccome il sistema è reversibile è sempre possibile riportarsi ad una situazione in cui si può far scattare una transizione.
+
+### Limiti
+
+- Per poter creare un albero di raggiungibilità è necessario enumerare tutte le possibili marcature raggiungibili, di conseguenza **la rete deve essere obbligatoriamente limitata**: non sarebbe altrimenti possibile elencare tutti i nodi.
+- la **crescita** (esponenziale) del numero degli stati globali può risultare velocemente **ingestibile** per una rete limitata.
+
+Inoltre:
+
+- Questa tecnica di analisi non è in grado di rilevare se una rete è limitata o meno;
+- Nel caso in cui si sappia già che la rete è limitata:
+  - l'albero di raggiungibilità non perde informazioni ed è la esplicitazione degli stati della rete (quindi ne è di fatto la FSM corrispondente).
diff --git a/src/16_reti-petri-analisi/02_albero-copertura.md b/src/16_reti-petri-analisi/02_albero-copertura.md
new file mode 100644
index 0000000000000000000000000000000000000000..a37de53ab0afa62f2e275ee19ff35d2e7da17d61
--- /dev/null
+++ b/src/16_reti-petri-analisi/02_albero-copertura.md
@@ -0,0 +1,186 @@
+<style>
+  .algorithm p {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+</style>
+<div style="display: none">
+$$
+\require{color}
+\def\node#1{\fcolorbox{black}{white}{#1}}
+\def\nodenew#1{\fcolorbox{lime}{white}{#1}}
+$$
+</div>
+
+# Albero di copertura
+
+A questo punto risulterà normale chiedersi se sia possibile creare una struttura dati (albero e grafo) anche per le **reti illimitate**, cui nodi rappresenteranno _gruppi di stati_ potenzialmente infiniti. \
+È bene introdurre il concetto di **copertura** prima di procedere.
+
+_Una marcatura \\(M\\) **copre** una marcatura \\(M'\\) se e solo se:_
+
+$$
+\forall p \in P \quad  M(p) \geq M'(p).
+$$
+
+Ovvero _se per ogni posto in \\(P\\), la marcatura \\(M(p)\\) è maggiore o uguale a \\(M'(p)\\)_.
+
+Al contrario, \\(M\\) si dice **copribile** da \\(M'\\) se e solo se:
+
+$$
+\exists M\smash{''} \\! \in R(M') \\: \vert \\: M'' \\! \textit{ copre } M.
+$$
+
+Grazie al concetto di _copertura_ è possibile ridefinire quello di **transizione morte**: \
+una transizione \\(t\\) si dice **morta** se e solo se data la sua _marcatura minima_ \\(M\\) (ovvero il minor numero di gettoni necessario in ogni posto nel suo preset per abilitarla) questa **non è copribile** a partire dalla marcatura corrente.
+In caso contrario la transizione \\(t\\) è almeno 1-viva.
+
+Si conclude quindi che se una marcatura ne copre un'altra, tutte le azioni possibili nella prima **sono possibili** anche nella seconda.
+È quindi possibile modificare l'_albero di raggiungibilità_ in modo tale che, quando viene creato un nodo è necessario verificare se tra i suoi predecessori ne esiste uno che lo copre, allora a questo punto nei posti dove c'è copertura propria (ovvero \\(M(p) \geq M'(p)\\)) si mette \\(\omega\\). \
+Il **simbolo** \\(\omega\\) rappresenta un numero **grande a piacere** (e non _qualsiasi_), che può aumentare all'infinito: questo aspetto è da tenere in considerazione quando bisogna cercare quali transizioni sono abilitate da esso.
+Questo tipo di notazione (\\(\omega\\)) viene introdotta per limitare l'aumento spropositato di nodi nel diagramma, comprimendo marcature uguali se non per \\(\omega\\).
+
+Se una marcatura \\(M\\) copre una precedente \\(M'\\), infatti, è possibile **ripetere gli scatti** delle transizioni in \\(M'\\) per arrivare ad \\(M\\); se al termine di questa operazione sono presenti più gettoni in un posto, allora è possibile crearne un numero grande a piacere.
+
+È importante notare come le transizioni che erano **abilitate** in una certa marcatura \\(M'\\) lo saranno anche in una marcatura diversa che copre \\(M'\\), a meno che non ci siano **archi inibitori**.
+
+È ora possibile definire l'**algoritmo** per la creazione di un albero di copertura, comunque simile in molti punti al precedente:
+
+<ol class="algorithm">
+  <li>
+
+  **crea la radice** dell'albero corrispondente alla marcatura iniziale \\(M_0\\) ed etichettala come _nuova_;
+  </li>
+  <li>
+
+  **_<u>finché</u>_ esistono nodi etichettati come _"nuovi"_** esegui:
+  <ol>
+  <li>
+
+  **seleziona** una marcatura \\(M\\) etichettata come _"nuova"_; \
+    prendila in considerazione e **rimuovi l'etichetta** _"nuova"_.
+  </li>
+  <li>
+
+  ***<u>se</u>*** la **marcatura** \\(M\\) è **identica** ad una marcatura di un altro nodo allora:
+  - **etichetta** \\(M\\) come **"duplicata"**;
+  - ***<u>continua</u>*** passando alla prossima iterazione.
+  </li>
+  <li>
+
+  ***<u>se</u>*** nella **marcatura** \\(M\\) non è abilitata **nessuna transizione** allora:
+  <ul>
+  <li>
+
+  **etichetta** \\(M\\) come **"finale"**;
+  </li>
+  </ul>
+
+  ***<u>altrimenti</u>*** esegui:
+  <ul>
+  <li>
+
+  ***<u>finché</u>* esistono transizioni abilitate** in \\(M\\) esegui:
+  <ul>
+  <li>
+
+  ***<u>per ogni</u> transizione* \\(t\\) abilitata** in \\(M\\) esegui:
+  <ol>
+  <li>
+
+  **crea** la **marcatura** \\(M'\\) prodotta dallo **scatto** di \\(t\\);
+  </li>
+  <li>
+
+  ***<u>se</u>*** sul cammino dalla **radice** (\\(M_0\\)) alla **marcatura** \\(M\\) \
+  **esiste** una **marcatura** \\(M''\\) **coperta** da \\(M'\\) allora
+  <ul>
+  <li>
+
+  **aggiungi** \\(\omega\\) in tutte le posizioni corrispondenti a coperture proprie;
+  </li>
+  </ul>
+  </li>
+  <li>
+
+  **crea** un nuovo **nodo** corrispondente alla marcatura \\(M'\\);
+  </li>
+  <li>
+
+  **aggiungi** un **arco** nell'albero al nodo corrispondente di \\(M\\) al nodo di \\(M'\\);
+  </li>
+  <li>
+
+  **etichetta** la **marcatura** \\(M'\\) come **"nuova"**.
+  </li>
+  </ol>
+  </li>
+  </ul>
+  </li>
+  </ul>
+  </li>
+  </ol>
+  </li>
+</ol>
+
+Dall'albero generato da questo algoritmo è possibile arrivare al **grafo di copertura**.
+
+Inoltre, ripetendo l'algoritmo precedente su una rete limitata viene generato un grafo di copertura senza \\(\omega\\) e quindi equivalente a un albero di raggiungibilità.
+
+L'algoritmo **termina** in ogni caso: è sufficiente **osservare** l'albero risultante per stabilire se la rete considerata è limitata oppure no.
+
+## Esempio
+
+Partendo dalla rete di Petri sottostante ed applicando l'**algoritmo** appena descritto è possibile arrivare ad un **albero di copertura**.
+
+![](/assets/15_esempio-albero-copertura-rete.png)
+
+Come visto nell'esempio della creazione di un albero di raggiungibilità, il primo passo da fare è creare il nodo radice corrispondente alla marcatura iniziale (\\(\nodenew{100}\\)) e marcarlo come nuovo. \
+Successivamente, è necessario considerare l'unico nodo esistente (\\(\node{100}\\)) e iterare tra le sue transizioni.
+In questo caso, è abilitata la transizione \\(t_1\\) che porta a una marcatura \\(M' = \nodenew{101}\\).
+
+A questo punto si può notare come la radice sia una **marcatura coperta da \\(M'\\)**, in quanto:
+
+- \\(M \\: \vert \\: 1 \geq 1 \\: \vert \\: M'\\);
+- \\(M \\: \vert \\: 0\geq 0 \\: \vert \\: M'\\);
+- \\(M \\: \vert \\: 1 > 0 \\: \vert \\: M'\\).
+
+Nel nodo corrispondente alla marcatura \\(M'\\) è quindi possibile sostituire l'unica **copertura propria** (quella con il \\(>\\) e non il \\(\geq\\)) con il simbolo \\(\omega\\) e marcare il nodo.
+Questa è l'unica parte dell'algoritmo differente da quello che genera l'albero di raggiungibilità: il resto dell'esempio è quindi completato dall'immagine sottostante.
+
+![](/assets/15_esempio-albero-copertura-albero.png)
+
+Tramite lo stesso procedimento attuato per gli alberi di raggiungibilità, è possibile trasformare il precedente albero in un **grafo di copertura**.
+
+![](/assets/15_esempio-albero-copertura-grafo.png)
+
+## Considerazioni
+
+- se \\(\omega\\) non compare mai nell'albero di copertura la rete è **limitata**;
+- una rete di Petri è **binaria** se nell'albero di copertura compaiono solo 0 e 1;
+- una transizione è **morta** (0-viva) se non compare mai come etichetta di un arco dell'albero di copertura;
+- condizione necessaria affinché una marcatura \\(M\\) sia **raggiungibile** è l'esistenza di un nodo etichettato con una marcatura che copre \\(M\\) (non sufficiente: _le marcature coperte non sono necessariamente raggiungibili_);
+- non è possibile stabilire se una rete è **viva**.
+
+## Esempio particolare
+
+È doveroso un ulteriore esempio particolare nel caso di reti **non vive**. \
+Data una _rete non viva_ (come nella figura sotto) dall'albero di copertura **non è possibile** evincere se la rete è effettivamente viva o no: infatti se il nodo \\\(\node{01$\omega$}\\) è duplicato, quindi non verrà più espanso.
+A questo punto non è possibile aggiungere all'interno dell'albero il nodo \\(\node{010}\\), in cui la rete raggiunge un deadlock.
+Questo però significa che questo albero di copertura è uguale a quello della stessa rete senza arco che collega \\(p_3\\) a \\(t_4\\), che in quel caso è una rete viva.
+Detto ciò si può affermare che tramite l'albero di copertura non è possibile dire se una rete è viva oppure no.
+
+![](/assets/15_esempio-particolare.png)
+
+### Da albero di copertura a rete
+
+Passare dall'albero di copertura alla rete di Petri è un'operazione che rispetto all'inverso crea più **incertezza**.
+L'albero di copertura permette infatti di rappresentare reti potenzialmente illimitate, è quindi normale avere come risultato reti di cui non si conosce la struttura: molte reti potrebbero essere associate **allo stesso albero**.
+
+Nel seguente esempio si può notare come la rete ricavata presenta degli _archi tratteggiati_: **potrebbero essere presenti**, oppure no.
+Inoltre, sono assenti anche i pesi negli archi.
+Tale mancanza di informazioni è dovuta in gran parte dalla presenza di \\(\omega\\): un nodo con all'interno \\(\omega\\) rappresenta **diverse marcature**.
+
+![](/assets/15_esempio-da-albero-a-rete.png)
+
+È importante notare come le marcature **sicuramente raggiungibili** siano quelle i cui nodi nell'albero di copertura non contengono \\(\omega\\): delle altre non si può essere certi.
diff --git a/src/16_reti-petri-analisi/03_rappresentazione-matriciale.md b/src/16_reti-petri-analisi/03_rappresentazione-matriciale.md
new file mode 100644
index 0000000000000000000000000000000000000000..62aaaa387b1853ccc2e572bf814d79f12834c2c6
--- /dev/null
+++ b/src/16_reti-petri-analisi/03_rappresentazione-matriciale.md
@@ -0,0 +1,146 @@
+# Rappresentazione matriciale
+
+Prima di procedere con la spiegazione delle tecniche di analisi statiche, è necessario introdurre una nuovo modo per rappresentare le reti di Petri: la **rappresentazione matriciale**.
+Essendo tutte rappresentazioni _formali_, _non ambigue_ e _complete_, data una qualsiasi rete rappresentata graficamente o in forma logica, è possibile **trasformarla automaticamente** in una rete in forma matriciale, e viceversa.
+
+Il vantaggio principale della rappresentazione matriciale è la **maggiore semplicità** ed **efficienza** nel **trattamento matematico** delle reti.
+
+Le matrici che verranno utilizzate sono diverse, tra cui:
+
+- \\(I\\): rappresenta gli **archi in ingresso**, ovvero le coppie di flusso che da un posto vanno nelle transizioni;
+- \\(O\\): rappresenta gli **archi in uscita**, ovvero le coppie di flusso che da una transizione vanno nei posti;
+- vettore \\(m\\): rappresenta la **marcatura** dei posti.
+
+## Definizione parte statica
+
+### Matrici \\(I\\) e \\(O\\)
+
+Diversamente dalla rappresentazione logica in cui venivano utilizzati degli indicatori alfanumerici per riferirsi ai posti e alle transizioni, nella rappresentazione matriciale viene assegnato un **indice** ad ogni posto e ad ogni transizione.
+Ogni indice deve essere possibilmente **continuo** (senza salti) e **biunivoco**: ogni indice corrisponde ad un posto e ogni posto corrisponde ad un indice.
+
+- indice dei **posti**: \\(p: 1..\vert P \vert \rightarrow P\\)
+- indice delle **transizioni**: \\(t: 1..\vert T \vert \rightarrow T\\)
+
+La **dimensione** delle due matrici è \\(\vert P \vert \times \vert T \vert\\): la **cardinalità dei posti** corrisponde al numero di righe e il **numero delle transizioni** corrisponde al numero delle colonne.
+
+Per ogni flusso uscente dal posto \\(i\\)-esimo ed entrate nella transizione \\(j\\)-esima, l'elemento \\(I[i][j]\\) equivale al **peso** di tale flusso, oppure \\(0\\) se il flusso non esiste.
+In sintesi:
+
+$$
+\forall i \in 1..\vert P \vert , \\, \forall j \in 1..\vert T \vert \quad I[i][j] = \begin{cases}
+W(\langle p(i), \\, t(j) \rangle) &\text{se} \\ \langle p(i), \\, t(j) \rangle \in F, \\
+0 &\text{altrimenti}.
+\end{cases}
+$$
+
+Analogamente, per la matrice degli output \\(O\\):
+
+$$
+\forall i \in 1..\vert P \vert , \\, \forall j \in 1..\vert T \vert \quad O[i][j] = \begin{cases}
+W(\langle t(j), \\, p(i) \rangle) &\text{se} \\ \langle t(j), \\, p(i) \rangle \in F, \\
+0 &\text{altrimenti}.
+\end{cases}
+$$
+
+Per indicare il vettore colonna \\(k\\) da una matrice \\(X\\) spesso verrà utilizzata la notazione \\(X[.][k]\\).
+
+![](/assets/15_esempio-rappresentazione-matriciale-I-O.png)
+
+### Marcatura \\(m\\)
+
+Per ogni posto, il vettore \\(m\\) di dimensione \\(\vert P \vert\\) indica la **marcatura corrente**.
+
+$$
+\forall i \in 1..\vert P \vert \quad m[i] = M(p(i))
+$$
+
+Che **differenza** c'è tra il vettore \\(m\\) e \\(M\\)? Entrambi logicamente indicano la **stessa cosa**, ma:
+
+- gli indici di \\(m\\) sono nell'insieme \\(1..\vert P \vert\\);
+- gli indici di \\(M\\) sono nell'insieme \\(P\\).
+
+## Definizione parte dinamica
+
+### Abilitazione di una transizione
+
+La transizione \\(j\\)-esima è **abilitata in una marcatura** (espressa dal vettore \\(m\\)) se e solo se il _vettore colonna_ della sua matrice di **input** \\(I[.][j]\\) è minore o uguale alla marcatura corrente \\(m\\):
+
+$$
+\boxed{m \\ [ \\ t (j) >} \Longleftrightarrow I[.][j] \leq m \\\\
+\textit{o se proprio vogliamo essere precisi...} \\\\
+\boxed{m \\ [ \\ t(j) >} \Longleftrightarrow \forall i \in 1..\vert P \vert \quad I[i][j] \leq m[i].
+$$
+
+In sostanza, si controlla che il numero dei gettoni di ogni posto \\(p(i)\\) del _preset_ sia maggiore o uguale del peso dell'arco che collega \\(p(i)\\) alla transizione.
+
+![](/assets/15_esempio-marcature-abilitate.png)
+
+### Scatto di una transizione
+
+Lo **scatto** di una transizione \\(j\\) in una marcatura \\(m\\) produce una marcatura \\(m'\\) che si ricava sottraendo elemento per elemento al vettore di partenza la colonna \\(j\\)-esima della matrice di input e quindi sommando al risultato la colonna j-esima della matrice output.
+
+$$
+\boxed{\boxed{m [ \\ t(j) >} \\: m'} \Longleftrightarrow m' = m - I[.][j] + O[.][j] \\\\
+\textit{o se proprio vogliamo essere precisi...} \\\\
+\boxed{\boxed{m [ \\ t(j) >} \\: m'} \Longleftrightarrow \forall i \in 1..\vert P \vert \quad m'[i] = m[i] - I[i][j] + O[i][j]. \\\\
+$$
+
+![](/assets/15_esempio-scatto-transizione.png)
+
+È importante notare come nell'operazione sopra due operandi su tre sono matrici costanti (\\(I\\) e \\(O\\)): è quindi possibile **pre-calcolare** \\(O - I\\) per efficienza.
+
+### Matrice di incidenza \\(C\\)
+
+La matrice \\(O - I\\) presentata sopra è infatti chiamata **matrice di incidenza** e si indica con la lettera \\(C\\).
+È utile per ottimizzare l'operazione _scatto_ di una rete in forma matriciale.
+In formule:
+
+$$
+\forall i \in 1..\vert P \vert, \\, \forall j \in 1.. \vert T \vert \quad C[i][j] = O[i][j] - I[i][j].
+$$
+
+![](/assets/15_esempio-matrice-incidenza.png)
+
+\\(C\\) **non sostituisce** le matrici di input \\(I\\) e output \\(O\\), in quanto \\(I\\) è ancora necessaria per calcolare l'abilitazione di una transizioni.
+Per le **reti non pure**, infatti, il valore presente in un qualsiasi posto della matrice potrebbe essere dato da una _qualsiasi combinazione_ di pesi relativi ad archi in ingresso ed uscita, in quanto per la stessa posizione \\(\langle i, \\, j \rangle\\) entrambe le matrici potrebbero assumere un valore.
+
+### Sequenze di scatti
+
+Si consideri una **sequenza** di **\\(n\\) scatti** che porti la rete da una marcatura iniziale \\(M\\) a una marcatura \\(M^n\\). \
+Ripetendo il seguente processo per \\(n\\) scatti
+
+$$
+\boxed{\boxed{M [ \\ t_1 >} \\: M' \vphantom{M''}}, \\; \boxed{\boxed{M' [ \\ t_2 >} \\: M''} \rightarrow \boxed{\boxed{M [ \\ t_1t_2 >} \\: M''},
+$$
+
+si rinomini la sequenza ottenuta nel seguente modo:
+
+$$
+\boxed{\boxed{M [ \\ s >} \\: M^{(n)}}.
+$$
+
+Esiste un **legame diretto** tra la marcatura iniziale e quella finale, che non preveda eseguire i **singoli passi**?
+A livello di matrici, l'esecuzione in sequenza di \\(x_1\\) volte di \\(t_1\\), \\(x_2\\) volte di \\(t_2\\) fino a \\(x_n\\) volte di \\(t_n\\) è **fattorizzabile**.
+Definendo un vettore \\(s\\) tale per cui
+
+$$
+\forall j \in 1..\vert T \vert \quad s[j] = \text{# di volte in cui $t(j)$ scatta}
+$$
+
+è facile notare come l'**ordine di scatto non conta**.
+Calcolando quindi \\(Cs\\) è quindi possibile calcolare l'**effetto netto** dell'intera sequenza di scatti, svolgendo un'unica operazione.
+Sommando \\(Cs\\) alla marcatura iniziare \\(M\\), si ottiene lo stato della marcatura finale \\(M^{(n)}\\).
+
+$$
+M^{(n)} = M + C s.
+$$
+
+È opportuno specificare che \\(s\\) non è in grado di determinare l'**esistenza** o l'**ordine** della sequenza presa in considerazione.
+Non è quindi possibile sapere se \\(s\\) corrisponde a una _sequenza ammissibile_ di scatti, ma è facile escluderlo: se \\(M^{(n)}\\) contiene numeri negativi, allora \\(s\\) corrisponde sicuramente ad una **sequenza inammissibile**.
+In generale, se anche in un solo passo intermedio \\(M^{(i)}\\) è negativo, allora la sequenza considerata non è ammissibile.
+
+In conclusione, è possibile effettuare questo calcolo solo se si è **certi** che la sequenza di scatti sia **ammissibile**.
+
+Di seguito è presente un **esempio** che potrebbe chiarire le idee.
+
+![](/assets/15_esempio-sequenza-scatti.png)
diff --git a/src/16_reti-petri-analisi/04_analisi-statica/00_index.md b/src/16_reti-petri-analisi/04_analisi-statica/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..d5f2c0acf3c20d97fb20841fb17d5725d202c9fe
--- /dev/null
+++ b/src/16_reti-petri-analisi/04_analisi-statica/00_index.md
@@ -0,0 +1,7 @@
+# Analisi statica
+
+È ora possibile introdurre due tecniche di **analisi statica** che si pongono come obiettivo la ricerca di **invarianti** all'interno della rete.
+Più nello specifico, esistono:
+
+- **\\(P\\)-invarianti**: invarianti relative alla **marcatura dei posti**;
+- **\\(T\\)-invarianti**: invarianti relative alla **sequenza di scatto**.
diff --git a/src/16_reti-petri-analisi/04_analisi-statica/01_p-invarianti.md b/src/16_reti-petri-analisi/04_analisi-statica/01_p-invarianti.md
new file mode 100644
index 0000000000000000000000000000000000000000..baa1ce7f8aafd9bc948558daed1d7190c522d2f0
--- /dev/null
+++ b/src/16_reti-petri-analisi/04_analisi-statica/01_p-invarianti.md
@@ -0,0 +1,267 @@
+# \\(P\\)-invarianti
+
+Una \\(P\\)-invariante è una caratteristica relativa alla marcatura dei posti che **non cambia**; viene rappresentata da un **vettore di pesi** \\(h\\) di dimensione \\(\vert P \vert\\).
+
+Il vettore \\(h\\) ricorda la funzione \\(H: P \rightarrow \mathbb N \setminus \{ 0 \}\\) dalla definizione di **rete conservativa**, con l'unica differenza che gli elementi di \\(h\\) possono essere nulli o negativi. \
+Nel caso in cui una \\(P\\)-invariante abbia tutti i pesi maggiori di zero allora \\(h \equiv H\\): la rete sarebbe quindi conservativa e quindi anche **limitata**.
+
+Tramite l'analisi delle \\(P\\)-invarianti è quindi possibile stabilire se una rete è conservativa e quindi limitata, fornendo un'**alternativa** al metodo dell'albero di copertura.
+
+Per ogni marcatura \\(m'\\) _raggiungibile_ da \\(m\\), l'**invariante** è il prodotto vettoriale della marcatura con \\(h\\).
+
+$$
+\forall m' \text{ raggiungibile da }m \quad hm = hm'
+$$
+
+Se \\(m'\\) è raggiungibile da \\(m\\), allora esiste una **sequenza di scatti** ammissibile \\(s\\) tale per cui
+
+$$
+m' = m + C s,
+$$
+
+è quindi possibile moltiplicare entrambi i membri per \\(h\\) in modo da avere
+
+$$
+hm = h(m + Cs) \\
+hm = hm + hCs
+$$
+
+quindi, semplificando i due \\(hm\\),
+
+$$
+\boxed{hCs = 0}.
+$$
+
+Ritornando alle assunzioni iniziali, tale proprietà vale solo se esiste una **sequenza di scatti ammissibile**: le informazioni su \\(m\\) non sono andate perse.
+
+Nell'ultima formula è presente la matrice \\(C\\) (**nota**), il vettore di pesi \\(h\\) (**incognita**) e il vettore \\(s\\) (**variabile libera**).
+La relazione vale infatti **per ogni** sequenza di scatti ammissibile \\(s\\).
+La **formula precisa** è quindi:
+
+$$
+\forall s \quad hCs = 0 \\
+\text{con $s$ rappresentante una sequenza di scatti ammissibile.}
+$$
+
+Assumendo per un momento che \\(hC = 0\\), allora qualsiasi sia \\(s\\) il risultato è sempre zero, **perdendo informazione** su quest'ultima. \
+Analogamente, in una rete che possiede una **transizione morta** la corrispondente posizione in \\(s\\) sarà sempre zero causando l'azzeramento anche della relativa posizione nel risultato.
+
+Non è quindi **necessario** che \\(hC = 0\\) per far sì che \\(hCs = 0\\), ma è sicuramente **sufficiente**.
+
+In conclusione, considerando solo \\(hC = 0\\) è possibile **escludere** la **componente dinamica** dalla proprietà ragionando solo in base alle informazioni topologiche (\\(C\\)) della rete.
+Trovare l'\\(h\\) che rende \\(hC = 0\\) è quindi **condizione sufficiente** ma non necessaria per cui \\(h\\) è una \\(P\\)-invariante, tenendo a mente che esistono comunque \\(h\\) che non rendono \\(hC = 0\\) ma potrebbero essere \\(P\\)-invarianti.
+
+I \\(P\\)-invarianti determinati con l'espressione \\(hC = 0\\) non dipendono dalla marcatura iniziale ma solo dalla **topologia** della rete: se venisse considerato anche \\(s\\) sarebbero \\(P\\)-invarianti per qualunque evoluzione della rete _a partire dalla marcatura \\(m\\)_.
+
+Il sistema \\(hC = 0\\) è un **sistema di equazioni lineare**, risolvibile con varie tecniche presentate successivamente.
+
+### Copertura di \\(P\\)-invarianti
+
+Una **combinazione lineare** di \\(P\\)-invarianti (e quindi di soluzioni del sistema) è anch'essa una \\(P\\)-invariante.
+
+Una \\(P\\)-invariante \\(h\\) avente tutti i pesi \\(\geq 0\\) è detta **semi-positiva**.
+Se un posto ha _peso positivo_ in una \\(P\\)-invariante semi-positiva, allora  è **limitato** nel numero di gettoni massimi che può contenere. \
+Se così non fosse, infatti, il contributo nella sommatoria vista precedentemente di \\(h[i]m[i]\\) (con \\(h[i] \geq 0\\) e \\(m[i] > 0\\)) sarebbe **potenzialmente illimitato**. \
+Se un posto ha peso nullo, potrebbe quindi essere **illimitato**.
+
+Avere pesi dei posti **negativi** non fornisce nessuna informazione sulla limitatezza degli stessi nella rete.
+
+Infine, se **per ogni posto** esiste una \\(P\\)-invariante semi-positiva il cui peso del posto è positivo, allora la rete è **limitata**.
+Matematicamente:
+
+$$
+\begin{align*}
+&\forall i \in 1..\vert P \vert, \\, \exists h \in \mathbb{R}^{\vert P \vert} \quad hC = 0 \land h[.] \geq 0 \land h[i] > 0 \\
+&\Rightarrow \text{$C$ rappresenta una rete limitata.}  
+\end{align*}
+$$
+
+Si può anche dire che se esiste una **combinazione lineare** di \\(P\\)-invarianti tale per cui il \\(P\\)-invariante risultante è **strettamente positivo**, allora vuol dire che la funzione \\(H : P \rightarrow \mathbb N^+\\) (che restituisce proprio quei pesi trovati) è una delle funzioni per cui la rete risulta **conservativa**.
+
+### Esempio
+
+Di seguito è illustrato un esempio sulle proprietà viste delle \\(P\\)-invarianti.
+
+![](/assets/15_esempio-p-invarianti.png)
+
+Date le matrici \\(I\\), \\(O\\) e \\(C = O - I\\) sopra è necessario risolvere il sistema \\(hC = 0\\)
+
+$$
+h \begin{bmatrix}
+-1 & 1 & 0 & 0 \\\\
+1  & -1 & 0 & 0 \\\\
+-1 & 1 & -4 & 4 \\\\
+0 & 0 & -1 & 1 \\\\
+0 & 0 & 1 & -1
+\end{bmatrix} \\! = 0,
+$$
+
+che si trasforma nel seguente sistema di equazioni lineari:
+
+$$
+\begin{cases}
+-h_0 &+h_1 &-h_2 & & \hphantom{+h_4} = 0 \\\\
++h_0 &-h_1 &+h_2 & & \hphantom{+h_4} = 0 \\\\
+& &-4h_2 &-h_3 &+h_4 = 0 \\
+& &+4h_2 &+h_3 &-h_4 = 0.
+\end{cases}
+$$
+
+È facilmente notabile che la prima e la seconda equazioni sono uguali a meno di una costante moltiplicativa: sono quindi **linearmente dipendenti**, insieme alla terza e alla quarta. \
+Chiedendo quindi a Wolfram|Alpha di risolvere il sistema
+
+$$
+\begin{cases}
+-h_0 &+h_1 &-h_2 & & \hphantom{+h_4} = 0 \\\\
+& &-4h_2 &-h_3 &+h_4 = 0,
+\end{cases}
+$$
+
+otteniamo le seguenti **basi**:
+
+$$
+\{ \langle -1, 0, 1, 0, 4 \rangle, \\, \langle 1, 0, -1, 4, 0 \rangle, \\, \langle 1, 1, 0, 0, 0 \rangle \},
+$$
+
+che se combinate linearmente generano **infinite soluzioni**.
+
+Tra le tre basi ottenute, l'ultima è **particolare** in quanto ha tutti gli elementi semi-positivi: di conseguenza i posti corrispondenti alle prime due posizioni (con pesi strettamente positivi) sono **limitati**. \
+Rimane comunque difficile stabilire se la **rete** è limitata oppure no.
+
+### Algoritmo di Farkas (1902)
+
+E se esistesse un algoritmo che predilige la ricerca di **basi minime** per un sistema di equazioni, privilegiando quelle **semipositive**?
+L'algoritmo in questione è l'**algoritmo di Farkas**.
+
+![](/assets/15_algoritmo-farkas.png)
+
+Sia \\(n\\) il numero di righe e \\(m\\) il numero di colonne della matrice di incidenza \\(C\\). \
+L'algoritmo inizia creando una matrice \\(D_0\\) ottenuta da \\(C\\) alla quale viene appesa una matrice di identità \\(E_n\\) di dimensione \\(n \cdot n\\).
+Quindi, **per ogni colonna** \\(i\\) da \\(1\\) a \\(m\\) si considerano le **coppie di righe** \\(\langle d_1, \\, d_2 \rangle\\) aventi nella \\(i\\)-esima riga numeri di segno opposto (non necessariamente uguali in valore assoluto).
+
+Per **ogni coppia di righe** \\(\langle d_1, \\, d_2 \rangle\\):
+
+- si crea una riga temporanea \\(d\\) ottenuta **combinando linearmente** la linea \\(d_1\\) moltiplicata per il valore assoluto dell'\\(i\\)-esimo elemento della riga \\(d_2\\) e sommando il viceversa. \
+Così facendo, l'\\(i\\)-esimo argomento della riga \\(d\\) è uguale a **zero**;
+- per evitare instabilità numerica dovuta a numeri troppo grandi si divide \\(d\\) per il **massimo comun divisore** dei suoi elementi, assegnando il risultato a \\(d'\\);
+- si estende la matrice \\(D_{i-1}\\) aggiungendo una nuova ultima riga \\(d'\\).
+
+Una volta terminato il ciclo sulla coppia di righe, si **scartano** tutte le righe della matrice \\(D_{i-1}\\) cui \\(i\\)-esimo elemento è diverso da \\(0\\).
+Infine, al termine del ciclo esterno si eliminano le prime \\(m\\) colonne di \\(D_{m}\\), essendo azzerate.
+Nella matrice risultante (corrispondente alla matrice \\(E_n\\)) sono presenti i \\(P\\)-invarianti.
+
+### Continuazione dell'esempio con Farkas
+
+Nell'esempio iniziato in precedenza si era arrivati ad un punto in cui si necessitava ottenere **basi semi-positive** e quindi \\(P\\)-invarianti semi-positivi: per fare ciò si può applicare l'algoritmo sopra descritto.
+
+Si inizia creando la matrice \\(D_0 = [C \\: \vert \\: E_n]\\):
+
+$$
+D_0 = \begin{bmatrix}
+-1  &1  &0  &0  &1 &0  &0  &0  &0 \\\\
+ 1  &-1 &0  &0  &0 &1  &0  &0  &0 \\\\
+-1  &1  &-4 &4  &0 &0  &1  &0  &0 \\\\
+ 0  &0  &-1 &1  &0 &0  &0  &1  &0 \\\\
+ 0  &0  &1  &-1 &0 &0  &0  &0  &1
+\end{bmatrix}.
+$$
+
+Osservando la **prima colonna** (\\(i = 1\\)) si nota che sono presenti due coppie di righe aventi segno opposto: la prima e la seconda, la seconda e la terza.
+
+A questo punto si possono **combinare linearmente** le coppie appendendo i risultati come **ultima riga**:
+
+$$
+D_0 = \begin{bmatrix}
+-1  &1  &0  &0  &1 &0  &0  &0  &0 \\\\
+ 1  &-1 &0  &0  &0 &1  &0  &0  &0 \\\\
+-1  &1  &-4 &4  &0 &0  &1  &0  &0 \\\\
+ 0  &0  &-1 &1  &0 &0  &0  &1  &0 \\\\
+ 0  &0  &1  &-1 &0 &0  &0  &0  &1 \\\\
+ 0  &0  &0  &0  &1  &1  &0  &0  &0 \\\\
+ 0  &0  &-4 &4  &0  &1  &1  &0  &0
+\end{bmatrix}.
+$$
+
+Le prime tre righe contengono nella colonna \\(i\\)-esima (la prima) elementi non nulli; si **scartano**:
+
+$$
+D_1 = \begin{bmatrix}
+ 0  &0  &-1 &1  &0 &0  &0  &1  &0 \\\\
+ 0  &0  &1  &-1 &0 &0  &0  &0  &1 \\\\
+ 0  &0  &0  &0  &1  &1  &0  &0  &0 \\\\
+ 0  &0  &-4 &4  &0  &1  &1  &0  &0
+\end{bmatrix}.
+$$
+
+Si procede iterativamente senza ulteriori azioni fino alla terza colonna, dove sono presenti **due coppie di righe** aventi segni opposti in posizione \\(i\\): la prima e la seconda, la prima e la quarta.\
+Applicando gli stessi passaggi di prima, la matrice \\(D_3\\) che si ottiene è la seguente:
+
+$$
+D_3 = \begin{bmatrix}
+ 0  &0  &0  &0  &1  &1  &0  &0  &0 \\\\
+ 0  &0  &0  &0  &0  &1  &1  &0  &4 \\\\
+ 0  &0  &0  &0  &0  &0  &0  &1  &1
+\end{bmatrix}.
+$$
+
+Infine, considerando la matrice \\(D_m\\) senza le prime colonne nulle, si ottengono le seguenti basi di \\(h\\):
+
+$$
+\{ \langle 1, 1, 0, 0, 0 \rangle, \\, \langle 0, 1, 1, 0, 4 \rangle, \\, \langle 0, 0, 0, 1, 1 \rangle \}.
+$$
+
+#### Interpretazione dei risultati ottenuti
+
+È facile notare come la rete sia **limitata**, in quanto per ogni posizione (_posto_) esiste almeno un \\(P\\)-invarianti semipositivi cui valore in tale posizione è **strettamente positivo**.
+
+Conoscendo ora possibili valori per \\(h\\), nella relazione \\(hm = hm_0\\) l'unica incognita al momento è \\(m\\): la **marcatura generica** che è possibile raggiungere.
+
+Considerando il **primo \\(P\\)-invariante** \\(h_1 = \langle 1, 1, 0, 0, 0 \rangle\\) e la marcatura iniziale \\(m_0 = \langle 4, 0, 4, 2, 0 \rangle\\) si ottiene la **relazione** \\(h_1m = hm_0 = 4\\) riguardante i seguenti posti:
+
+$$
+\text{LettoriPronti} + \text{LettoriAttivi} = 4. \tag{$h_1$}
+$$
+
+I posti cui peso è \\(1\\) (proveniente da \\(h_1\\)) sono \\(\text{LettoriPronti}\\) e \\(\text{LettoriAttivi}\\), mentre il \\(4\\) dipende dal numero di gettoni in \\(\text{LettoriPronti}\\) in \\(m_0\\): la somma dei due è garantita essere **costante**. \
+In generale, i **termini** a sinistra dipendono da \\(h\\), quelli a destra da \\(m_0\\).
+
+Per \\(h_2 = \langle 0, 0, 0, 1, 1 \rangle\\) il procedimento è lo stesso:
+
+$$
+\text{ScrittoriPronti} + \text{ScrittoriAttivi} = 2. \tag{$h_2$}
+$$
+
+Il terzo risultato, per \\(h_3 = \langle 0, 1, 1, 0, 4  \rangle\\) è il **più interessante**:
+
+$$
+\text{LettoriAttivi} + \text{Risorsa} + 4 \cdot \text{ScrittoriAttivi} = 4 \tag{$h_3$}.
+$$
+
+In tutti i risultati è **implicito** che tutti gli **operandi** devono essere **interi maggiori o uguali a zero**.
+
+Con dell'algebra è possibile riscrivere l'**ultimo risultato** (\\(h_3\\)) nel seguente modo:
+
+$$
+\begin{align}
+\frac{4 \cdot \text{ScrittoriAttivi}}{4} &= \frac{4 - \text{LettoriAttivi} - \text{Risorsa}}{4} \\\\
+\text{ScrittoriAttivi} &= 1 - \frac{\text{LettoriAttivi}}{4} -\frac{\text{Risorsa}}{4} \\\\
+\text{ScrittoriAttivi} &= 1 - \frac{\text{LettoriAttivi} + \text{Risorsa}}{4}.
+\end{align}
+$$
+
+Dall'ultima espressione è possibile determinare che gli \\(\text{ScrittoriAttivi}\\) sono o **zero** o **uno**, in quanto \\(\frac{\text{LettoriAttivi} + \text{Risorsa}}{4}\\) è necessariamente positivo.
+A questo punto "LettoriAttivi" + "Risorsa" si sa essere un valore positivo, che diviso per 4 rimane un numero positivo, a meno che non siano entrambi 0. \
+È quindi garantito matematicamente che gli scrittori sono in **mutua esclusione**.
+
+Procedendo similmente per i lettore, si ottiene che:
+
+$$
+\text{LettoriAttivi} = 4 \cdot (1 - \text{ScrittoriAttivi}) -  \text{Risorsa}.
+$$
+
+Si può quindi concludere che:
+
+- \\(\text{ScrittoriAttivi} \leq 1\\);
+- \\(\text{LettoriAttivi} \leq 4\\);
+- \\(\text{LettoriAttivi} > 0 \Longrightarrow \text{ScrittoriAttivi} = 0\\);
+- \\(\text{ScrittoriAttivi} > 0 \Longrightarrow \text{LettoriAttivi} = 0\\).
diff --git a/src/16_reti-petri-analisi/04_analisi-statica/02_t-invarianti.md b/src/16_reti-petri-analisi/04_analisi-statica/02_t-invarianti.md
new file mode 100644
index 0000000000000000000000000000000000000000..683e1e761af5087cdb8113f2c6f2f61ea803caa1
--- /dev/null
+++ b/src/16_reti-petri-analisi/04_analisi-statica/02_t-invarianti.md
@@ -0,0 +1,21 @@
+# \\(T\\)-invarianti
+
+I \\(T\\)-invarianti sono concettualmente **molto simili** ai \\(P\\)-invarianti, ma pongono alcuni vincoli di **invariabilità** sulle **sequenze di scatti**, ovvero:
+
+- si **possono ripetere** ciclicamente;
+- portano alla situazione iniziale (**stato base**).
+
+Partendo dall'equazione \\(m' = m + Cs\\), poniamo il **vincolo** \\(m' = m\\) in quanto la sequenza deve tornare alla marcatura iniziale.
+Le **soluzioni** del sistema sono quindi:
+
+$$
+Cs = 0,
+$$
+
+con \\(C\\) costante e \\(s\\) un **vettore di incognite**, rappresentante una sequenza ammissibile.
+
+Se si risolve il sistema e si trova un vettore \\(s\\) rappresentante una sequenza di scatti ammissibile, allora tale sequenza è **ciclica** per cui \\(s\\) è un \\(T\\)-invariante.
+
+A differenza dei \\(P\\)-invarianti (trovarne uno è _condizione sufficiente_ purché sia valido), per un \\(T\\)-invariante soddisfare l'equazione è **condizione necessaria** ma non sufficiente per la **validità** della sequenza.
+
+Se una rete è **limitata** e copribile da \\(T\\)-invarianti, allora è dimostrabile che è anche **viva**.
diff --git a/src/16_reti-petri-analisi/05_controllori.md b/src/16_reti-petri-analisi/05_controllori.md
new file mode 100644
index 0000000000000000000000000000000000000000..80042167abd6f35d5cc78c637e03d4f0a4c0060b
--- /dev/null
+++ b/src/16_reti-petri-analisi/05_controllori.md
@@ -0,0 +1,199 @@
+# Controllori con specifica a stati proibiti
+
+Tramite le reti di Petri si possono **modellare dei controllori** che forzano o limitano certi comportamenti del sistema: se si desidera cioè che la rete rispetti una certa **invariante** si introduce un controllare che la forzi.
+_Controllare_ significa **assicurarsi** che vengano rispettate certe proprietà.
+
+È possibile definire gli **stati** come situazioni che _si possono verificare_, e le **transizioni** come _eventi che si verificano_.
+Lo scopo è **controllare** che le transizioni possano svolgere certe operazioni, oppure no.
+
+Esistono due **classi di problemi** che limitano la capacità espressiva dei controllori:
+
+- **non tutte le transizioni sono osservabili**: il controllore non ne ha le capacità, oppure è un'attività troppo onerosa;
+- l'osservazione di alcune situazioni ne **comporta il cambiamento**.
+
+Inoltre, **non tutto è controllabile**: non si può chiedere ad una centrale nucleare in surriscaldamento di non esplodere, ma si possono attivare i sistemi di sicurezza.
+
+Nel modello del **controllore a stati proibiti**, l'attività di _controllo_ si traduce formalmente in una **combinazione lineare** delle marcature che deve rimanere sotto una certa soglia. \
+Si vincola quindi per un **sottoinsieme di posti** che la combinazione lineare di una marcatura \\(M\\) con un **vettore dei pesi** \\(L\\) sia minore o uguale (e non solo _uguale_ come nei \\(P\\)-invarianti) di una soglia data:
+
+$$
+LM \leq b.
+$$
+
+Come abbiamo visto nel corso di _Ricerca Operativa_, è **sempre** possibile riportare un **sistema di disequazioni** ad un sistema di equazioni **inserendo variabili aggiuntive** (_**slack**_) semipositive:
+
+$$
+LM + x = b \\: \vert \\: x \geq 0.
+$$
+
+## Mutua esclusione
+
+Il problema della mutua esclusione è l'**accesso esclusivo** a zona critica da parte di più soggetti.
+Nel seguente esempio si vuole imporre che non sia possibile avere gettoni contemporaneamente in \\(P_1\\) e \\(P_3\\).
+
+![](/assets/15_mutua-esclusione-situazione-iniziale.png)
+
+Matematicamente il vincolo si può esprimere con la seguente **disequazione**.
+
+$$
+P_1 + P_3 \leq 1
+$$
+
+La tecnica del _controllore a stati proibiti_ aggiunge tanti **posti di controllo** quanti sono il **numero di disequazioni** (e quindi il numero di variabili di _slack_) per modificare il comportamento delle transizioni.
+
+In questo caso, per trasformare la disequazione in un'equazione si aggiunge una variabile di slack, rappresentante il nuovo **posto controllore** \\(P_c\\).
+
+$$
+P_1 + P_3 + P_c = 1
+$$
+
+![](/assets/15_aggiunta-posto-controllore.png)
+
+Per collegare \\(P_c\\) alle diverse transizioni occorre aggiungere una riga \\(C_c\\) nella **matrice di incidenza** \\(C_s\\):
+
+$$
+C_\text{nuova} = \begin{bmatrix}
+  C_s \\\\
+  C_c
+\end{bmatrix}.
+$$
+
+Inoltre, bisogna aggiungere la marcatura iniziale \\(M_{0c}\\) del posto \\(P_c\\) alla **marcatura iniziale** del sistema \\(M_{0s}\\):
+
+$$
+M_{0} = \begin{bmatrix}
+  M_{0s} \\\\
+  M_{0c}
+\end{bmatrix}.
+$$
+
+Riscrivendo quindi il vincolo tramite le matrici otteniamo:
+
+$$
+LM_s + M_c = b.
+$$
+
+Sia \\(\begin{bmatrix} L I\end{bmatrix}\\) la giustapposizione tra \\(L\\) e la **matrice identità** \\(I\\) e \\(M\\) la giustapposizione di \\(M_s\\) e \\(M_c\\), allora:
+
+$$
+\begin{bmatrix}
+  L I
+\end{bmatrix}
+M = b.
+$$
+
+L'espressione sopra ricorda la definizione di \\(P\\)-invariante (\\(hm = 0\\)).
+Volendo forzare che \\([L I]\\) sia un'invariante, riprendiamo quindi la relativa definizione:
+
+$$
+\begin{bmatrix}
+  L I
+\end{bmatrix}
+C = 0,
+$$
+
+che, rifacendosi al vincolo originale, si può a sua volta riscrivere come
+
+$$
+L C_s + I C_c = 0 \\\\
+\boxed{C_c = -LC_s}.
+$$
+
+Le righe da aggiungere al sistema \\(C_c\\) sono quindi **uguali** a \\(-LC_s\\), dove:
+
+- \\(C_s\\) è la **matrice di incidenza** del **sistema** originale;
+- \\(L\\) è il **vincolo desiderato**, fissato;
+- \\(C_c\\) la si trova con un **semplice calcolo matriciale**.
+
+### Sintesi del controllore
+
+Continuando l'esempio precedente, l'obiettivo è trovare
+
+$$
+\begin{align*}
+C_s &= \begin{bmatrix}
+  0  &-1    &0   &1 \\\\
+  0   &1    &0  &-1 \\\\
+ -1   &0    &1   &0 \\\\
+  1   &0   &-1   &0
+\end{bmatrix} \quad
+L = \begin{bmatrix}
+  0  &1  &0  &1
+\end{bmatrix} \\\\
+-LC_s &= 
+\begin{bmatrix}
+  -1  &-1  & \phantom{-} 1  & \phantom{-} 1
+\end{bmatrix}.
+\end{align*}
+$$
+
+![](/assets/15_archi-posto-controllore.png)
+
+Il vettore \\(-LC_s\\) definisce gli **archi in ingresso** e **in uscita** dalle transizioni per il **posto controllore** \\(P_c\\): \
+il posto ha in ingresso \\(T_0\\) e \\(T_1\\) (gli elementi con -1) mentre in uscita \\(T_2\\) e \\(T_3\\) (gli elementi con 1).
+
+Da questi risultati è possibile ottenere anche la **marcatura iniziale** del posto controllore (\\(M_{0_c}\\)):
+
+$$
+LM_{0s} + M_{0c} = b \\\\
+\boxed{M_{0c} = b - LM_{0s}}.
+$$
+
+Essendo tutti termini noti, è facile rispondere che la **marcatura iniziale** di \\(P_c\\) è uguale a 1.
+
+In **conclusione**, le due formule principali da conoscere sono le seguenti:
+
+- \\(\boxed{C_c = -LC_s}\\) per calcolare le **righe** da aggiungere alla **matrice di incidenza** \\(C_s\\);
+- \\(\boxed{M_{0c} = b - LM_{0s}}\\) per calcolare la **marcatura iniziale** del posto controllore \\(P_c\\).
+
+### Esempio
+
+Riprendendo il classico esempio dei **lettori** e **scrittori**, lo scopo di questo esercizio è collegare le due parti assicurando l'accesso esclusivo alla risorsa.
+
+![](/assets/15_esempio_mutua_esclusione_lettori_scrittori.png)
+
+Dovendo imporre la **mutua esclusione** tra lettori e scrittori, poniamo i seguenti vincoli:
+
+$$
+\begin{cases}
+\text{LettoriAttivi} + \text{ScrittoriAttivi} \leq 1 \\\\
+\text{LettoriAttivi} + 4 \cdot \text{ScrittoriAttivi} \leq 4 .
+\end{cases}
+$$
+
+Il primo vincolo è incluso nel secondo, quindi possiamo ignorarlo. \
+Date le **seguenti informazioni**, possiamo realizzare nella rete i vincoli sopra.
+
+$$
+M_0 = \begin{bmatrix}
+  4  &0  &2  &0
+\end{bmatrix},
+\quad
+C = \begin{bmatrix}
+  -1  &1    &0   &0 \\\\
+  1   &-1    &0  &0 \\\\
+  0   &0    &1   &-1 \\\\
+  0   &0   &-1   &1
+\end{bmatrix}
+\tag{Dati della rete}
+$$
+$$
+LM \leq b \tag{Vincolo}
+$$
+$$
+L = \begin{bmatrix} 0 &1 &0 &4 \end{bmatrix}
+\quad
+b = 4
+\tag{Parametri del vincolo}
+$$
+
+È sufficiente quindi sfruttare le formule viste prima per trovare la **nuova riga della matrice di incidenza** e la **marcatura iniziale** di \\(P_0\\).
+
+$$
+\begin{align}
+C_c = -LC_s &= \begin{bmatrix}
+  -1  &1  &4  &-4
+\end{bmatrix} \\\\
+M_{0_c} = b - LM_{0_s} &= 4.
+\end{align}
+$$
diff --git a/src/16_reti-petri-analisi/06_reti-priorita.md b/src/16_reti-petri-analisi/06_reti-priorita.md
new file mode 100644
index 0000000000000000000000000000000000000000..0afae1b57b8b0b209b65d11f107ee64ef581f9a3
--- /dev/null
+++ b/src/16_reti-petri-analisi/06_reti-priorita.md
@@ -0,0 +1,10 @@
+# Reti con priorità
+
+Ad ogni transizione è associata una **priorità**: quando in una marcatura \\(n\\) transizioni sono abilitate, la scelta della prossima da far scattare è **determinata** dalla sua priorità.
+
+Date le opportune priorità, è quindi possibile **guidare** la progressione della rete verso la soluzione richiesta.
+
+Ci sono due svantaggi principali a questo approccio:
+
+- rischio di creare di **cicli infiniti**;
+- si perde la _località di decisione_ della abilitazione di una transizione: non è quindi più possibile fare analisi locale.
diff --git a/src/17_reti-petri-temporizzate/00_index.md b/src/17_reti-petri-temporizzate/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..e5534a5d9659e5e3a9c02d7d215bdea3fe5a0d44
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/00_index.md
@@ -0,0 +1,18 @@
+# Reti temporizzate
+
+Abbiamo visto come le reti di Petri siano un modello estremamente potente per modellare un'infinita varietà di situazioni anche molto diverse tra loro.
+Tuttavia, alcune categorie di problemi richiedono un approccio più mirato, ovvero un'__estensione delle reti di Petri__ specifica per il loro studio: è questo il caso dei __sistemi Hard Real-time__, di cui ora tratteremo approfonditamente.
+
+In molte applicazioni il __tempo__ è un fattore essenziale: si pensi per esempio ad un termostato intelligente che deve accendere e spegnere i termosifoni di una casa in base ad un programma giornaliero oppure ad un autovelox, che in base al tempo di andata e ritorno di un'onda elettromagnetica dev'essere in grado di calcolare la velocità di un veicolo. \
+Ma non tutti i sistemi basati sul tempo sono uguali: alcuni di essi richiedono infatti il rispetto assoluto di una serie di __vincoli temporali stretti__, ovvero requisiti sul tempo di esecuzione di certe operazioni che devono essere rispettati per evitare gravi conseguenze.
+Considerando per esempio il sistema di controllo di una centrale nucleare, qualora si inizi a rilevare un'aumento eccessivo delle temperature nel reattore tale software dev'essere in grado di reagire entro un certo tempo strettissimo, pena l'esplosione dell'apparato.
+Sistemi di questo tipo prendono il nome, come già detto, di __sistemi Hard Real-time__, dove l'aggettivo "Hard" indica proprio la durezza richiesta nel rispetto dei vincoli temporali.
+
+Visto il loro tipico impiego in situazioni di rischio o di pericolo, i committenti di sistemi di questo tipo potrebbero voler avere __prova del loro corretto funzionamento__ prima ancora che questi vengano installati.
+I modelli finora descritti potrebbero però non essere sufficienti: non è per esempio abbastanza un'_analisi stocastica_ della rete, in quanto in virtù dei rischi a cui un malfunzionamento del sistema esporrebbe bisogna essere assolutamente __certi__ del suo corretto funzionamento, certezza che può essere ottenuta solo con un __modello deterministico__. \
+Ecco quindi che come strumento di specifica e comunicazione col cliente vengono sviluppate una serie di estensioni alle reti di Petri progettate specificamente per trattare il concetto di _tempo_ e _ritardo_: tra queste distingueremo in particolare le __reti Time Basic__ (_Ghezzi et al., 1989_), oggi le più usate.
+
+- [**Modelli temporali**](./01_modelli-temporali.md)
+- [**Reti Time Basic**](./02_reti-tb.md)
+- [**Evoluzione**](./03_evoluzione/00_index.md)
+- [**Analisi delle reti Time Basic**](./04_analisi/00_index.md)
diff --git a/src/17_reti-petri-temporizzate/01_modelli-temporali.md b/src/17_reti-petri-temporizzate/01_modelli-temporali.md
new file mode 100644
index 0000000000000000000000000000000000000000..66b98e4dde8dad1eb9829f463206ebda23eaa26c
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/01_modelli-temporali.md
@@ -0,0 +1,70 @@
+# Modelli temporali
+
+Esistono una serie di proposte per modellare il concetto di __tempo__ (_deterministico_) all'interno delle reti di Petri.
+Esse si dividono sostanzialmente in due grandi categorie:
+
+- quelle che introducono __ritardi sui posti__;
+- quelle che introducono __ritardi sulle transizioni__.
+
+### Tempo sui posti
+
+Il tempo associato a ciascun posto rappresenta il __tempo che un gettone deve rimanere in tale posto prima di essere considerato per l'abilitazione__ di transizioni che hanno tale posto nel proprio preset.
+
+![](/assets/16_tempo-sui-posti.png)
+
+Dopo lo scatto di una transizione i gettoni generati in un posto non fanno cioè funzionalmente parte della sua marcatura prima che sia passato un dato intervallo di tempo \\(\Delta\\).
+Tale \\(\Delta\\) può quindi essere considerato la __durata minima di permanenza__ del gettone in tale posto, bloccando così quella porzione di stato del sistema per un certo periodo.
+
+### Tempo sulle transizioni
+
+Quando si associa un tempo ad una transizione è bene indicare che cosa esso rappresenti.
+Il tempo di una transizione può infatti rappresentare due concetti molto differenti:
+
+- la __durata della transizione__, ovvero il tempo richiesto dopo lo scatto della transizione perché vengano generati i gettoni nel suo postset (una sorta di _ritardo di scatto_);
+- il __momento dello scatto__ della transizione, che può essere espresso in modo diverso a seconda del modello.
+
+Esistono a dire il vero anche modelli misti che permettono di specificare sia la durata di una transizione che il suo tempo di scatto.
+
+#### Equivalenza tra tempi sui posti e sulle transizioni
+
+È facile dimostrare che sia le reti che definiscono tempi sui posti che quelle che definiscono tempi sulle transizioni, sia come durata che come momento dello scatto, sono __funzionalmente equivalenti__, ovvero permettono di rappresentare lo stesso insieme di sistemi.
+
+Ciò è testimoniato dal fatto che, come mostra la figura sottostante, ogni rete avente tempo sui posti può essere trasformata in una rete con durata delle transizioni semplicemente aggiungendo una transizione di "ritardo" e separando il posto in due.
+Ovviamente vale anche il viceversa.
+
+![](/assets/16_tempo-posti-to-durata.png)
+
+Similmente, reti con durata delle transizioni possono essere trasformate in reti con tempi di scatto per le transizioni modellando esplicitamente con un posto il ritardo con cui vengono generati gettoni nel postset della transizione originale.
+
+![](/assets/16_durata-to-tempo-scatto.png)
+
+#### <big>Tempi di scatto</big>
+
+Ritornando un attimo sui nostri passi, diamo ora un'occhiata migliore a come si possono definire i tempi di scatto di una transizione.
+
+Nella definizione dei tempi di scatto delle transizioni esistono infatti una serie di alternative molto differenti.
+Innanzitutto, i tempi possono essere:
+
+- __unici__, ovvero ogni transizione scatta (o può scattare) in _uno e un solo_ specifico momento;
+- __multipli__, ovvero ogni transizione scatta (o può scattare) in _uno in un insieme_ di momenti.
+    A seconda del modello considerato tali insiemi possono essere veri e propri __insiemi matematici__ (_es. reti TB_) oppure __intervalli__.
+
+Si noti come i primi possono essere visti come casi particolari dei secondi. \
+Considerando ciò, gli insiemi (anche unitari) di tempi di scatto si distinguono poi in due categorie:
+
+- __insiemi costanti__, ovvero tali per cui l'insieme dei tempi di scatto è __definito staticamente__ ed è sempre uguale indipendentemente dall'evoluzione della rete;
+- __insiemi variabili__, ovvero tali per cui l'insieme dei tempi di scatto può __variare dinamicamente__ in base allo stato della rete o a porzioni di esso (_es. reti TB e HLTPN_).
+
+Anche in questo caso i primi possono essere visti come un caso particolare dei secondi, in cui cioè l'insieme _potrebbe_ variare ma non varia mai. \
+Infine, i tempi di scatto stessi possono essere divisi in base a come vengono definiti:
+
+- __tempi relativi__, ovvero espressi _solo_ in termini relativi al tempo di abilitazione della transizione (_es. "2 ms dopo l'abilitazione"_);
+- __tempi assoluti__, ovvero espressi in termini relativi a tempi assoluti e ai tempi associati ai gettoni che compongono l'abilitazione (_es. "dopo 3 minuti dall'avvio del sistema" o "dopo 4 ms dal tempo associato all'ultimo gettone nell'abilitazione"_) (_es. reti TBe TCP_).
+
+Nuovamente, i primi possono essere visti come un sottoinsieme dei secondi.
+
+Tutto questo insieme di variabili permette di definire reti temporizzate basate su tempi di scatto delle transizioni anche molto diverse tra di loro.
+Avremo per esempio le _reti Time Petri_, che utilizzano tempi di scatto relativi, multipli e a intervalli costanti; le _reti Time Petri colorate_, simili alle precedenti ma che usano tempi assoluti; le _reti Time Petri ad alto livello_, che usano insiemi variabili, e molte altre.
+
+Tra tutte queste tipologie, tuttavia, ci concentreremo sulle __reti Time Basic__.
+In virtù delle inclusioni di cui abbiamo già detto tali reti saranno quindi le più generali possibile e, dunque, anche le più interessanti.
diff --git a/src/17_reti-petri-temporizzate/02_reti-tb.md b/src/17_reti-petri-temporizzate/02_reti-tb.md
new file mode 100644
index 0000000000000000000000000000000000000000..5b50b50b57cf8cbfb781f6d0c3c6ff7de31b0ae2
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/02_reti-tb.md
@@ -0,0 +1,44 @@
+# Reti Time Basic
+
+Prima di darne una vera e propria definizione matematica iniziamo a introdurre le __reti Time Basic__ (__TB__) in modo informale.
+
+Introdotte per la prima volta da Ghezzi e dai suoi collaboratori nel 1989, le reti TB associano __insiemi variabili__ di tempi di scatto __assoluti__ alle transizioni: ciascuna transizione possiede cioè un insieme di tempi in cui _potrebbe_ scattare, definito in maniera dinamica a seconda dello stato.
+Tali tempi di scatto potrebbero poi essere definiti sia in termini assoluti che in termini dei __tempi associati ai gettoni__.
+
+![](/assets/16_TBN-intro.png)
+
+Nelle reti TB infatti i gettoni non sono più anonimi, ma caratterizzati ciascuno da un __timestamp__ che indica il __momento in cui sono stati creati__ (\\(\operatorname{t}(posto)\\)).
+A differenza delle normali reti di Petri i gettoni sono quindi __distinguibili__: questo non significa che due gettoni non possano avere lo stesso timestamp, ma solo che non tutti i gettoni sono uguali (_mentre gettoni generati dalla stessa transizione o da transizioni diverse scattate in parallelo avranno invece lo stesso timestamp_).
+
+Per ogni transizione viene poi introdotto il concetto di __tempo di abilitazione__ (\\(\bf{enab}\\)), ovvero il __momento in cui la transizione viene abilitata__: poiché una transizione è abilitata quando tutti i posti nel suo preset contengono tanti gettoni quanto il peso dell'arco entrante in essa, il tempo di abilitazione di una transizione sarà pari al __massimo tra i timestamp__ dei gettoni che compongono la __tupla abilitante__. \
+Poiché i posti nel preset della transizione potrebbero contenere più gettoni di quelli necessari per farla scattare, una transizione potrebbe avere __più tempi di abilitazione diversi__ in base ai gettoni considerati per la tupla abilitante.
+
+Ovviamente i __tempi di scatto__ delle transizioni __non potranno essere minori__ del tempo di abilitazione, in quanto una transizione non può scattare prima di essere abilitata.
+Gli insiemi dei tempi di scatto potranno invece _dipendere_ dal tempo di abilitazione: così, per esempio, una transizione potrebbe scattare 2 secondi dopo essere stata abilitata, oppure tra 3 e 5 minuti dall'abilitazione. \
+A tal proposito, molto spesso i tempi di scatto saranno rappresentati come __intervalli__ \\([min,max]\\) piuttosto che come insiemi: nei nostri esempi adotteremo questa convenzione, ma è bene tenere in mente che tali insiemi potrebbero avere qualunque possibile forma.
+
+## Definizioni matematiche
+
+Facciamo un po' di chiarezza introducendo delle definizioni rigorose per tutto quanto citato nell'introduzione. \
+Una rete Time Basic è una __6-tupla__ del tipo \\(\langle P, T, \Theta, F, tf, m_0 \rangle\\), dove:
+
+- \\(P, T, F\\) sono identici all'insieme dei posti, delle transizioni e al flusso delle normali reti di Petri;
+- \\(\Theta\\) (_theta_) è il __dominio temporale__, ovvero l'insieme numerico che contiene le rappresentazioni degli istanti di tempo;
+- \\(tf\\) è una funzione che associa ad ogni transizione \\(t \in T\\) una __funzione temporale__ \\(\operatorname{tf_{t}}\\) che data in input la __tupla abilitante__ \\(\bf{en}\\), ovvero l'__insieme dei timestamp__ dei gettoni scelti per l'abilitazione nel preset, restituisce un __insieme di tempi di scatto possibili__:
+
+  $$
+  \operatorname{tf_{t}}(en) \subseteq \Theta
+  $$
+
+  Per esempio, se per una transizione \\(t\\) i tempi di scatto sono nell'intervallo \\([min, max]\\), allora \\(\operatorname{tf_{t}}(en) = \{r \\, \vert \\, min \leq r \leq max\}\\).
+
+- \\(m_0\\) è un multiset che esprime la __marcatura iniziale__: si tratta cioè di una funzione che ad ogni __posto__ associa un insieme di coppie __timestamp-molteplicità__ che indicano il numero di gettoni con tale timestamp all'interno del posto:
+
+  $$
+  m_0 : P \rightarrow \{ (\theta, \operatorname{mul}(\theta)) \\, \vert \\, \theta \in \Theta \}
+  $$
+
+  Tutte le __marcature__ esprimibili per le reti Time Basic assumeranno la forma di simili funzioni.
+
+Con questi costrutti matematici siamo in grado di descrivere completamente lo stato di una rete Time Basic.
+Tuttavia sorge ora spontanea una domanda: dovendo modellare il concetto di tempo, come __evolve__ una rete TB?
diff --git a/src/17_reti-petri-temporizzate/03_evoluzione/00_index.md b/src/17_reti-petri-temporizzate/03_evoluzione/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..f1471bb4394b70c0963e74937e8fc94b3f56ff73
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/03_evoluzione/00_index.md
@@ -0,0 +1,13 @@
+# Evoluzione
+
+Dovendo modellare lo __scorrere del tempo__, le reti Time Basic dovranno operare una serie di accortezze per quanto riguarda la loro evoluzione.
+
+Abbiamo per esempio già detto che il tempo di scatto di una transizione dovrà necessariamente essere maggiore del suo tempo di abilitazione, e che tale tempo di scatto sarà pari al timestamp dei gettoni generati dalla transizione.
+Tuttavia, questo non è abbastanza: il concetto di tempo è particolarmente sfuggente e, soprattutto, __difficile da definire in maniera univoca__. \
+Al contrario, per le reti Time Basic vengono definite diverse __semantiche temporali__, ovvero diverse interpretazioni del concetto di "tempo" che richiederanno il rispetto di una serie di assiomi durante l'evoluzione della rete.
+Tali interpretazioni, ciascuna utile per modellare diversi sistemi e requisiti di tempo, variano anche in complessità; in questo corso partiremo dunque dalla semantica più semplice per poi costruire su di essa quelle più complesse.
+
+- [**Semantica temporale debole**](./01_wts.md) (WTS)
+- [**Semantica temporale monotonica debole**](./02_mwts.md) (MWTS)
+- [**Semantica temporale forte**](./03_sts.md) (STS)
+- [**Semantica temporale mista**](./04_mista.md)
diff --git a/src/17_reti-petri-temporizzate/03_evoluzione/01_wts.md b/src/17_reti-petri-temporizzate/03_evoluzione/01_wts.md
new file mode 100644
index 0000000000000000000000000000000000000000..575596f027c7407f5698a59454d910a7a47b2715
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/03_evoluzione/01_wts.md
@@ -0,0 +1,18 @@
+# Semantica temporale debole (WTS)
+
+Informalmente, la __semantica temporale debole__ (_Weak Time Semantic_, __WTS__) impone che una transizione possa scattare _solo_ in uno degli __istanti identificati dalla sua funzione temporale__ e __non possa scattare _prima_ di essere stata abilitata__.
+
+Tuttavia, ___una transizione non è costretta a scattare___ anche se abilitata: essa _potrebbe_ scattare, ma non è forzata a farlo.
+Questo permette di modellare eventi solo __parzialmente definiti__, ovvero che potrebbero accadere sotto determinate condizioni ma non è possibile dire se lo faranno o no: esempi notevoli sono guasti o decisioni umane, eventi cioè non completamente prevedibili.
+Si noti che a differenza dei modelli stocastici delle reti di Petri in questo caso non ci interessa la _probabilità_ con cui gli eventi potrebbero accadere, ma solo che potrebbero accadere.
+
+Per imporre questa interpretazione del concetto di tempo l'evoluzione di una rete Time Basic deve seguire i seguenti __assiomi temporali__:
+
+- (__A1__) __Monotonicità rispetto alla marcatura iniziale__: tutti i tempi di scatto di una sequenza di scatto devono essere __non minori__ (\\(\geq\\)) di uno qualunque dei timestamp dei gettoni della marcatura iniziale. \
+  Ogni marcatura deve cioè essere __consistente__, ovvero non contenere gettoni prodotti "nel futuro".
+
+- (__A3__) __Divergenza del tempo__ (_non-zenonicità_): __non__ è possibile avere un __numero infinito__ di scatti in un intervallo di __tempo finito__. \
+  Questo assioma serve ad assicurarsi che il tempo __avanzi__!
+  Esso assicura cioè che il tempo non si possa fermare e soprattutto che esso non possa essere suddivisibile in infinitesimi: il sistema evolve soltanto quando il tempo va avanti.
+
+Le sequenze di scatti che soddisfano questi due assiomi vengono dette __sequenze ammissibili in semantica debole__.
diff --git a/src/17_reti-petri-temporizzate/03_evoluzione/02_mwts.md b/src/17_reti-petri-temporizzate/03_evoluzione/02_mwts.md
new file mode 100644
index 0000000000000000000000000000000000000000..2f881b0cfc32b9f26dc4e0bc3b833c4eaa57b4c8
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/03_evoluzione/02_mwts.md
@@ -0,0 +1,39 @@
+# Semantica temporale monotonica debole (MWTS)
+
+Come i più attenti avranno notato, nell'elencare gli assiomi necessari per la semantica temporale debole abbiamo saltato un ipotetico assioma A2.
+Ebbene, ciò non è un caso: esiste infatti un'estensione della semantica WTS che aggiunge tra i propri requisiti il rispetto di tale assioma.
+
+Si tratta della __semantica temporale monotonica debole__ (_Monotonic WTS_, __MWTS__), e differisce dalla semantica WTS perché impone necessariamente che i tempi di scatto delle transizioni all'interno di una sequenza siano monotoni non decrescenti, forzando così il fatto che nell'intera rete __il tempo non possa tornare indietro__. \
+Più formalmente, la semantica introduce il seguente assioma:
+
+- (__A2__) __Monotonicità dei tempi di scatto di una sequenza__: tutti i tempi di scatto di una sequenza di scatti devono essere ordinati nella sequenza in maniera __monotonicamente non decrescente__ (\\(\geq\\)). \
+  Anche questo serve a garantire la proprietà intuitiva di __consistenza__, evitando cioè che il tempo torni indietro.
+  Non richiedendo però che i tempi siano disposti in modo strettamente crescente ma ammettendo che nella sequenza lo __stesso tempo sia ripetuto__ si lascia aperta la possibilità che nella rete più transizioni scattino in contemporanea, oppure che due transizioni scattino in tempi talmente ravvicinati che la granularità temporale del modello non è in grado di rilevare la differenza.
+
+Le sequenze di scatti che soddisfano gli assiomi A1, A2 e A3 vengono dette __sequenze ammissibili in semantica monotonica debole__. \
+Sebbene sembri una differenza da nulla, imporre la monotonicità dei tempi di scatto ha in realtà ripercussioni piuttosto grandi: in una rete che segue la MWTS quando si analizzano gli scatti è necessario non solo fare un'analisi locale del preset e del tempo di abilitazione e scatto della transizione, ma anche assicurarsi che non ci sia nessuna transizione nella rete in grado di scattare prima.
+Si __perde cioè la caratteristica di località__, introducendo la necessità di mantenere un'informazione comune sull'__ultimo scatto__ nella rete.
+
+## WTS \\(\equiv\\) MWTS
+
+Fortunatamente per noi esiste un teorema che afferma che _per ogni sequenza di scatti ammissibile in semantica debole \\(S_{WTS}\\) __esiste__ una sequenza di scatti ammissibile in semantica monotonica debole \\(S_{MWTS}\\) __equivalente__ ottenibile per semplice __permutazione__ delle occorrenze degli scatti._
+
+Non si tratterà di sequenze uguali, ma entrambe le sequenze produrranno la __stessa marcatura finale__.
+Questo è un enorme vantaggio, in quanto ciò ci permette di infischiarcene della monotonicità degli scatti durante l'analisi della rete, potendo così sfruttare la __località__ e conseguentemente le __tecniche di analisi per le reti di Petri__ (ad alto livello) già viste in precedenza.
+
+### Esempio di traduzione
+
+Si prenda in esame la rete in figura:
+
+![](/assets/16_esempio-wts-mwts.png)
+
+Assumendo i timestamp iniziali di tutti i gettoni uguali a zero, si consideri la seguente sequenza ammissibile WTS di scatti:
+
+$$ \text{T1 scatta al tempo 12} \rightarrow \text{T3 scatta al tempo 14} \rightarrow \text{T2 scatta al tempo 4} $$
+
+Tale sequenza non rispetta la monotonicità, in quanto T2 scatta "nel passato" dopo lo scatto di T3, e produce la marcatura \\(\langle0, 0, 1, 0, 1\rangle\\).
+Tuttavia, riordinando la sequenza come:
+
+$$ \text{T2 scatta al tempo 4} \rightarrow \text{T1 scatta al tempo 12} \rightarrow \text{T3 scatta al tempo 14} $$
+
+è possibile ottenere una marcatura identica ma con una sequenza che rispetta ora la monotonicità, essendo cioè ammissibile in semantica temporale monotonica debole.
diff --git a/src/17_reti-petri-temporizzate/03_evoluzione/03_sts.md b/src/17_reti-petri-temporizzate/03_evoluzione/03_sts.md
new file mode 100644
index 0000000000000000000000000000000000000000..1eb9eeb7caaf00dcc99e0550175df59a89dc8648
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/03_evoluzione/03_sts.md
@@ -0,0 +1,33 @@
+# Semantica temporale forte (STS)
+
+Finora abbiamo lasciato aperta la possibilità che una transizione pur _potendo_ scattare non lo facesse.
+Questa alternativa non è però contemplata in molti modelli temporizzati, in cui il __determinismo__ gioca un forte ruolo: spesso si vuole che se una transizione può scattare, allora __deve__ scattare entro il suo massimo tempo di scatto ammissibile.
+
+Per forzare questo comportamento viene creata una semantica temporale apposita, che prende il nome di __semantica temporale forte__ (_Strong Time Semantic_, __STS__): essa impone che _una transizione __deve scattare__ ad un suo possibile tempo di scatto __a meno che non venga disabilitata__ prima del proprio massimo tempo di scatto ammissibile_.
+Aggiungere quest'ultima clausola permette alle transizioni di non dover prevedere il futuro: se esse fossero programmate per scattare in un certo istante ma prima di esso lo scatto di un'altra transizione le disabilitasse non si richiederebbe che esse tornino indietro nel tempo per scattare all'ultimo istante di tempo _utile_.
+
+Essendo un ulteriore irrigidimento rispetto alla semantica temporale monotonica debole, la STS dovrà sia rispettare gli assiomi A1, A2 e A3, sia la seguente nuova coppia di assiomi, che porta il totale a cinque:
+
+- (__A4__) __Marcatura forte iniziale__: il __massimo tempo di scatto__ di tutte le transizioni abilitate nella __marcatura iniziale__ dev'essere __maggiore o uguale del massimo timestamp__ associato ad un gettone in tale marcatura. \
+  Questo assicura cioè che la marcatura iniziale sia __consistente con la nuova semantica__ temporale: un gettone dotato di timestamp superiore al tempo di scatto massimo di una transizione abilitata non sarebbe potuto essere generato _prima_ che la transizione scattasse (cosa che deve fare!), rendendo quindi la marcatura in questione non più quella iniziale.
+
+- (__A5__) __Sequenza di scatti forte__: una sequenza di scatti ammissibile in semantica __MWTS__ che parta da una __marcatura forte iniziale__ è una __sequenza di scatti _forte___ se per ogni scatto il tempo di scatto della transizione __non è maggiore__ del massimo tempo di scatto di un'altra transizione abilitata. \
+  Si sta cioè accertando che ogni transizione scatti entro il suo tempo massimo se non viene disabilitata prima da un altro scatto: per fare ciò, si permette alle transizioni di scattare _solo_ se non ci sono altre transizioni abilitate che sarebbero già dovute scattare, costringendo quindi queste ultime a farlo per far continuare a evolvere la rete.
+
+Ecco dunque che sequenze di scatto che soddisfano gli assiomi A1, A2, A3, A4 e A5 vengono dette __sequenze ammissibili in semantica forte__.
+
+## STS \\(\not\equiv\\) MWTS
+
+In virtù dell'ultimo assioma si potrebbe pensare che esista un modo per trasformare ogni sequenza di scatti MWTS in una sequenza STS, realizzando così un'equivalenza.
+Purtroppo, però, non è così: una sequenza STS è sempre anche MWTS, ma __non è sempre vero il contrario__.
+
+Poiché infatti non è più possibile a causa dell'assioma A2 riordinare le sequenze per ottenerne altre di equivalenti, è possibile trovare numerose sequenze che sono MWTS ma non STS.
+Riprendendo la rete già vista in precedenza e assumendo anche in questo caso dei timestamp iniziali nulli per i gettoni:
+
+![](/assets/16_esempio-wts-mwts.png)
+
+è facile vedere che la sequenza ammissibile in semantica monotonica debole:
+
+$$ \text{T2 scatta al tempo 6} \rightarrow \text{T1 scatta al tempo 12} \rightarrow \text{T3 scatta al tempo 14} $$
+
+non è invece una sequenza ammissibile in semantica forte, in quanto lo scatto di T2 abilita la transizione T3, che dovrebbe quindi scattare entro il tempo 9 (\\(enab = 6\\)) ma non lo fa.
diff --git a/src/17_reti-petri-temporizzate/03_evoluzione/04_mista.md b/src/17_reti-petri-temporizzate/03_evoluzione/04_mista.md
new file mode 100644
index 0000000000000000000000000000000000000000..3aa3de809ff9522d056598ac6b8f85fdd1d4dfab
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/03_evoluzione/04_mista.md
@@ -0,0 +1,32 @@
+# Semantica temporale mista
+
+Può però capitare che dover imporre una semantica temporale fissa per l'intera rete si riveli limitante nella modellazione di sistemi reali: questi potrebbero infatti includere sia agenti deterministici (_es. computer_) che agenti stocastici (_es. esseri umani_).
+
+Si introduce quindi una nuova __semantica temporale mista__ (_Mixed Time Semantic_), in cui la semantica temporale debole (monotonica) o forte viene associata alle __singole transizioni__ piuttosto che all'intera rete.
+In questo modo:
+
+- le __transizioni forti__ _dovranno_ scattare entro il loro tempo massimo a meno che non vengano disabilitate prima;
+- le __transizioni deboli__ _potranno_ scattare in uno qualunque dei loro possibili tempi di scatto.
+
+Essendo meno comuni, solitamente sono le transizioni deboli ad essere esplicitamente indicate graficamente nelle reti con una \\(W\\) all'interno del rettangolo che le rappresenta: tutte le altre transizioni sono invece di default considerate forti.
+
+## Analisi di abilitazione in presenza di transizioni fortis
+
+Introdotta quindi la possibilità che esistano all'interno delle reti delle transizioni forti che devono necessariamente scattare entro il loro tempo massimo di scatto non è ora più tanto semplice fare __analisi di abilitazione__, vale a dire quel tipo di analisi che cerca di tracciare su una linea temporale gli intervalli durante i quali certe transizioni sono abilitate.
+
+Per capire perché, osserviamo la seguente rete Time Basic, che segue una semantica temporale mista:
+
+![](/assets/16_analisi-rete.png)
+
+Analizzando localmente le singole transizioni, come se avessero tutte semantica temporale debole, si può ottenere il seguente diagramma temporale di abilitazione:
+
+![](/assets/16_analisi-1.png)
+
+Tuttavia, questo diagramma è __scorretto__.
+La presenza di una transizione forte che deve scattare entro il tempo 10, ovvero T2, non ci permette di dire nulla oltre tale tempo, in quanto _il suo scatto potrebbe disabilitare altre transizioni_.
+Questo era vero anche per la transizione debole T1, ma il suo essere debole permetteva comunque di ignorare tale eventualità nella prospettiva che la transizione, pur potendo, non scattasse: questo tipo di ragionamento non è però purtroppo più possibile in semantica temporale forte.
+
+In sostanza, __le transizioni forti bloccano il nostro orizzonte temporale__. \
+Ecco dunque che il vero diagramma temporale di abilitazione della rete è il seguente:
+
+![](/assets/16_analisi-2.png)
diff --git a/src/17_reti-petri-temporizzate/04_analisi/00_index.md b/src/17_reti-petri-temporizzate/04_analisi/00_index.md
new file mode 100644
index 0000000000000000000000000000000000000000..b975ef5e60a7eed7d83999778e3fda13b996c87e
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/04_analisi/00_index.md
@@ -0,0 +1,9 @@
+# Analisi delle reti Time Basic
+
+Definite le reti Time Basic in ogni loro aspetto è dunque arrivato il momento di __analizzarle__.
+
+Esattamente come le reti di Petri, infatti, le reti TB fanno parte di quei __linguaggi operazionali__ utilizzati per illustrare il funzionamento di un sistema senza entrare nei dettagli della sua effettiva implementazione (\\(\neq\\) _linguaggi dichiarativi/logici, che si usano invece per costruire il sistema a partire dalle proprietà richieste_).
+Visto questo ruolo, è necessario possedere una serie di __strumenti di analisi__ specifici che permettano di "simulare" il funzionamento della rete per comprenderne l'evoluzione e le proprietà: ciò appare particolarmente evidente se si ricorda che le reti TB vengono spesso utilizzate per modellare sistemi _Hard Real-Time_ in cui si deve avere la __certezza__ del fatto che il sistema rispetterà tutta una serie di caratteristiche prima ancora di iniziare i lavori.
+
+- [**Reti TB come reti ad alto livello?**](./01_alto-livello.md)
+- [**Analisi di raggiungibilità temporale**](./02_raggiungibilita.md)
diff --git a/src/17_reti-petri-temporizzate/04_analisi/01_alto-livello.md b/src/17_reti-petri-temporizzate/04_analisi/01_alto-livello.md
new file mode 100644
index 0000000000000000000000000000000000000000..c3088aaf86ae00be926af58336c5591299a10b56
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/04_analisi/01_alto-livello.md
@@ -0,0 +1,37 @@
+# Reti TB come reti ad alto livello?
+
+Ma è davvero necessario sviluppare un'intera serie di nuove tecniche di analisi specifiche per le reti Time Basic, o è possibile riutilizzare almeno in parte metodologie già discusse?
+
+Si potrebbe infatti immaginare di considerare le reti TB come un tipo particolare di __reti ad Alto Livello__ (ER).
+Come abbiamo già accennato, questo tipo di reti permettono infatti ai gettoni di avere un __qualunque contenuto informativo__ e di definire le transizioni come una coppia __predicato-azione__: il predicato descrive la condizione di abilitazione della transizione in funzione dei valori dei gettoni nel preset, mentre l'azione determina che valore avranno i gettoni creati nel postset. \
+Volendo modellare il __tempo come concetto derivato__, ovvero non delineato esplicitamente ma che emerga comunque dal _funzionamento_ della rete, si potrebbero quindi creare delle reti ad Alto Livello con le seguenti caratteristiche:
+
+- __contenuto informativo dei gettoni__: un'unica variabile temporale __chronos__ che contiene il timestamp della loro creazione;
+- __predicati delle transizioni__: funzioni che controllano i timestamp dei gettoni nel preset e i propri tempi di scatto per determinare l'abilitazione o meno;
+- __azioni delle transizioni__: generazione di gettoni dotati tutti dello _stesso dato temporale_, il quale è _non minore dei timestamp di tutti i gettoni nella tupla abilitante_.
+
+Con queste accortezze è possibile riprodurre i timestamp e le regole di scatto di una rete TB.
+Come sappiamo bene, tuttavia, questo non basta per modellare il _concetto_ di tempo: per avere un'espressività simile a quella delle reti Time Basic è infatti necessario anche il rispetto di una __semantica temporale__ e, in particolare, di quella più stringente, ovvero la semantica temporale forte (STS).
+
+Nelle reti ad Alto Livello bisognerà dunque far rispettare i cinque assiomi temporali perché la traduzione da reti TB a reti ER sia completa.
+Vediamo dunque come ciò potrebbe essere fatto:
+
+- gli assiomi __A1__ e __A3__ sono sufficientemente semplici da modellare all'interno dei predicati delle transizioni, rendendo così la __semantica temporale debole__ rappresentabile nelle reti ad Alto Livello;
+
+- l'assioma __A2__ è già più complesso da realizzare, in quanto richiede che lo scatto di una transizione generi dei gettoni con un timestamp maggiore di quello di tutti gli altri gettoni nella rete (_imponendo così che il tempo avanzi_).
+  Tuttavia tale limite può essere aggirato con l'aggiunta di un __posto globale__ contenente il __gettone dell'ultimo scatto__ e aggiunto al preset di ogni transizione: in questo modo il gettone nel posto globale rappresenterà il _tempo corrente della rete_ e imporrà che i nuovi gettoni generati abbiano timestamp maggiore di esso.
+  In questo modo una rete ER può realizzare anche la __semantica temporale monotonica debole__;
+
+- gli assiomi __A4__ e __A5__, invece, si rivelano __estremamente problematici__: essi richiedono infatti che ciascuna transizione conosca il massimo tempo di scatto di tutte le altre transizioni per "decidere" se poter scattare oppure no.
+  I predicati delle transizioni dovrebbero cioè avere in input l'intero stato della rete: sebbene questa cosa sia _teoricamente_ realizzabile con l'aggiunta di un posto globale in ingresso e uscita da ogni transizione in cui un gettone contenga come _contenuto informativo l'intero stato della rete_, nella pratica cio è __irrealizzabile__.
+
+Come si vede, dunque, la necessità di modellare la __semantica temporale forte__ fa sì che __non si possa ridurre le reti TB a un caso particolare delle reti ER__, perdendo così anche la possibilità di utilizzare le tecniche di analisi già sviluppate per esse.
+
+## Reti Time Petri ad Alto Livello (HLTPN)
+
+Appurato che non è possibile tracciare un'equivalenza tra le reti TB e le reti ER viene dunque introdotto un modello ancora più completo, che racchiuda al suo interno sia gli __aspetti funzionali__ delle reti ad Alto Livello sia gli __aspetti temporali__ delle reti Time Basic: si tratta delle __reti Time Petri ad Alto Livello__ (_High-Level Time Petri Nets_, __HLTPN__).
+
+![](/assets/16_HLTPNs.png)
+
+Come appare chiaro dalla figura, all'interno delle reti HLTPN __gli aspetti funzionali possono dipendere da quelli temporali e viceversa__, espandendo così incredibilmente le capacità rappresentative del modello.
+Si tratta di reti ovviamente molto complesse, anche se a dire il vero gran parte della complessità giunge dall'analisi della componente temporale: se riusciremo ad analizzare le reti temporizzate potremo riuscire ad analizzare anche le reti HLTPN.
diff --git a/src/17_reti-petri-temporizzate/04_analisi/02_raggiungibilita.md b/src/17_reti-petri-temporizzate/04_analisi/02_raggiungibilita.md
new file mode 100644
index 0000000000000000000000000000000000000000..412c50d8e2285e7654b7d5fa4706f6aac19411f1
--- /dev/null
+++ b/src/17_reti-petri-temporizzate/04_analisi/02_raggiungibilita.md
@@ -0,0 +1,237 @@
+# Analisi di raggiungibilità temporale
+
+Rassegnatici dunque alla necessità di creare nuove tecniche di analisi specifiche per le reti temporizzate iniziamo a parlare di __analisi dinamica__ a partire dall'__analisi di raggiungibilità__, ovvero la tecnica con cui nelle reti di Petri classiche eravamo in grado di __enumerare gli stati finiti raggiungibili__.
+
+Provando ad adottare lo stesso approccio nei confronti delle reti TB ci si rende però subito conto di un enorme __problema__: le reti temporizzate hanno sempre __infiniti stati__, in quanto lo scatto di una singola transizione può produrre un'infinità di stati di arrivo che si differenziano unicamente per il timestamp dei gettoni generati.
+Sebbene la marcatura sia identica, le informazioni temporali legate ai gettoni sono differenti, distinguendo così ciascuno di tali stati della rete. \
+Bisogna inoltre considerare che per sua stessa natura il tempo avanza, rendendo così le reti temporizzate in grado di __evolvere all'infinito__: anche raggiungendo una marcatura che non abilita alcuna transizione, la rete continua ad evolvere in quanto il _tempo corrente_ continua ad avanzare.
+
+Dovendo costruire un __albero di raggiungibilità__ questo sarebbe quindi sicuramente __infinito__, anche se in un modo diverso rispetto a quanto già visto per le reti non limitate: in quel caso infatti i gettoni non erano distinguibili, cosa che ci aveva permesso di raggrupparne un numero qualsiasi sotto il simbolo \\(\omega\\), mentre in questo caso le differenze nei timestamp impediscono un simile approccio.
+
+Al contrario, per ottenere per le reti TB un'analisi simile all'analisi di raggiungibilità delle reti classiche è necessario __ridefinire__ completamente il concetto di __stato raggiungibile__.
+
+## Stati simbolici
+
+Per riformulare il concetto stesso di raggiungibilità partiamo da innanzitutto da quello di __marcatura__: nelle reti temporizzate queste associavano infatti a ciascun posto un _multiset_ in cui ad ogni timestamp era associato il numero di gettoni con tale timestamp presente nel posto.
+
+Per evitare la difficoltà di distinguere tra gettoni con timestamp diversi viene introdotto nelle reti TB il concetto di __stato simbolico__, un oggetto matematico che sostituendo ai timestamp specifici degli identificatori simbolici dei gettoni permette di _rappresentare un __insieme di possibili stati__ con in comune lo stesso numero di gettoni in ciascun posto_ (esattamente come la marcatura delle reti classiche).
+
+Più formalmente, uno stato simbolico è una __tupla__ \\([\mu, C]\\), dove:
+
+- \\(\mu\\) è la __marcatura simbolica__, che associa a ciascun posto un _multiset_ di __identificatori simbolici__ che rappresentano i timestamp dei gettoni in tale posto.
+  Timestamp uguali saranno rappresentati dallo stesso simbolo, anche se si trovano in posti diversi: questo permette di mantenere l'__identità__ tra timestamp;
+
+- \\(C\\) è un sistema di __vincoli__ (_constraint_), ovvero equazioni e disequazioni che rappresentano le relazioni tra gli identificatori simbolici dei gettoni.
+  In questo modo è possibile mantenere le __relazioni__ tra i timestamp dei gettoni pur non rappresentando esplicitamente il loro valore.
+
+Un'esempio aiuterà a chiarire ogni dubbio.
+Immaginiamo di avere una rete TB con 3 posti \\((P1, P2, P3)\\), ciascuno con un solo gettone al loro interno, e la seguente marcatura iniziale: \\(\langle \{0\}, \{1\}, \{0\} \rangle\\).
+Volendoci disfare dei timestamp espliciti dei gettoni, che tutto sommato ci interessano relativamente, dobbiamo __mantenere due informazioni__:
+
+- che i gettoni in \\(P1\\) e \\(P3\\) hanno lo _stesso timestamp_;
+- che il gettone in \\(P2\\) ha _timestamp maggiore_ di 1 del timestamp dei gettoni negli altri due posti.
+
+Per fare ciò lo stato simbolico generato assegnerà lo stesso identificatore ai gettoni in \\(P1\\) e \\(P3\\) e espliciterà nei vincoli la relazione tra tempi.
+Otterremo dunque lo stato simbolico iniziale:
+
+$$ \mu(P1)=\{\tau_0\}, \\: \\: \\: \mu(P2)=\{\tau_1\}, \\: \\: \\: \mu(P3)=\{\tau_0\} $$
+
+$$ C_0: \\: \\: \\: \tau_1=\tau_0+1 $$
+
+Infine, ci si potrebbe accorgere che in realtà non ci interessa che il timestamp del gettone in \\(P2\\) sia esattamente \\(\tau_0+1\\), ma solamente che esso sia maggiore di \\(\tau_0\\).
+Ecco dunque che spesso si mutano i vincoli in __disequazioni__:
+
+$$ C_0: \\: \\: \\: \tau_0<\tau_1 $$
+
+## Albero di raggiungibilità temporale
+
+Utilizzando la definizione di stato simbolico appena vista è possibile costruire tramite un algoritmo un __albero di raggiungibilità temporale__, in cui gli stati distinguibili solo dai timestamp vengono condensati in stati simbolici che conservano però le _molteplicità_ dei gettoni nei posti e le _relazioni_ tra i timestamp.
+
+Prima di fare ciò, però, è necessario rinnovare un'assunzione già fatta in precedenza: anche in questa analisi assumeremo che le funzioni temporali \\(tf_{t}\\) associate alle transizioni siano esprimibili come __intervalli con estremi inclusi__ \\(\bf{[tmin_t, tmax_t]}\\), dove questi ultimi possono dipendere ovviamente dai timestamp dei token in ingresso nonché da tempi assoluti.
+
+Fatte queste premesse, possiamo partire a costruire effettivamente l'albero di raggiungibilità temporale di una rete TB secondo il seguente algoritmo:
+
+1. __Inizializzazione__: si trasforma la marcatura iniziale della rete in uno stato simbolico, introducendo una serie di vincoli che descrivano le (pre-)condizioni iniziali della rete.
+  Tale stato viene poi trasformato in un nodo, diventando la radice dell'albero, e aggiunto alla lista dei nodi da esaminare;
+
+2. __Scelta del prossimo nodo__: tra i nodi dell'albero non ancora esaminati si seleziona il prossimo nodo da ispezionare;
+
+3. __Identificazione delle abilitazioni__: in base allo stato simbolico rappresentato dal nodo si individuano le transizioni abilitate al suo interno;
+
+4. __Aggiornamento di marcatura e vincoli__: ciascuna transizione abilitata trovata viene fatta scattare generando un nuovo stato simbolico, che viene rappresentato nell'albero come nodo figlio del nodo considerato e aggiunto alla lista dei nodi da esaminare;
+
+5. __Iterazione__: si ritorna al punto 2.
+
+Di questo semplice algoritmo approfondiamo dunque le due fasi più interessanti: l'aggiornamento di marcatura e vincoli e l'identificazione delle abilitazioni.
+
+### Aggiornamento di marcatura e vincoli
+
+Come si fa a __generare un nuovo stato simbolico__ a partire dallo stato simbolico corrente quando scatta una transizione abilitata?
+Sostanzialmente il processo si divide in due fasi: la __creazione e distruzione di gettoni__ e l'__espansione dei vincoli__.
+
+Il primo step è abbastanza semplice: è sufficiente distruggere i gettoni e i relativi simboli nel preset della transizione e generare nuovi gettoni nel suo postset, questi ultimi identificati tutti dallo __stesso nuovo identificatore simbolico__.
+
+La generazione di nuovi vincoli è invece più complessa.
+Essa deve infatti tenere in considerazione quattro diversi aspetti:
+
+- I __vecchi vincoli__ devono continuare a valere: essi esprimono infatti relazioni tra gli identificatori temporali che non possono essere alterate dallo scatto di una transizione;
+
+- Il __nuovo timestamp__ deve avere il __valore massimo__ nella rete: esso rappresenta infatti il tempo di scatto dell'ultima transizione scattata, e per monotonicità del tempo esso dovrà essere maggiore di tutti gli altri;
+
+- Il __nuovo timestamp__ dev'essere __compreso nell'intervallo__ dei possibili tempi di scatto della transizione scattata;
+
+- Il __nuovo timestamp__ dev'essere __minore del massimo tempo di scatto__ di tutte le __transizioni forti abilitate__ il cui intervallo di scatto non sia nullo: per la semantica temporale forte, infatti, se così non fosse la transizione non potrebbe scattare prima che tale transizione forte scatti (cambiando anche potenzialmente l'insieme delle transizioni abilitate).
+
+Tutte queste considerazioni devono essere condensate in un'__unica espressione logica__.
+Si dimostra quindi che _dato uno stato simbolico precedente avente vincoli \\(C_n\\), detto \\(maxT\\) il timestamp massimo all'interno della rete e \\(\tau_{n+1}\\) l'identificatore simbolico dei gettoni generati dalla transizione \\(t\\) è possibile definire i vincoli dello stato simbolico prodotto con la seguente __formula___:
+
+$$
+\boxed{
+\begin{align*}
+C_{n+1} = \\: & C_n \wedge \tau_{n+1} \geq maxT \\: \wedge tmin_t \leq \tau_{n+1} \leq tmax_t \\\\
+& \bigcap\limits_{t_{STS}}(tmax_{t_{STS}} < tmin_{t_{STS}} \vee tmax_{t_{STS}} < maxT \vee tmax_{t_{STS}} \geq \tau_{n+1} )
+\end{align*}
+}
+$$
+
+dove per \\(t_{STS}\\) si intende una qualunque __transizione forte__ diversa da \\(t\\); per __ciascuna__ di esse bisognerà infatti aggiungere la condizione tra parentesi, relativa appunto alla semantica STS.
+
+Tale catena di condizioni può ben presto diventare soverchiante, ma fortunatamente essa può essere semplificata sfruttando le __implicazioni__ e le proprietà degli operatori logici.
+In particolare:
+
+- se una condizione \\(A\\) implica un'altra condizione \\(B\\) con cui è in __AND__ (\\(\wedge\\)), allora la condizione __implicata__ \\(B\\) può essere cancellata;
+- se una condizione \\(A\\) implica un'altra condizione \\(B\\) con cui è in __OR__ (\\(\vee\\)), allora la condizione __implicante__ \\(A\\) può essere cancellata.
+
+### Identificazione delle abilitazioni
+
+Al contrario di quanto ci si potrebbe aspettare, però, la creazione di questo nuova catena di vincoli non è relegata alla sola creazione di un nuovo stato simbolico, ma è invece necessaria anche per __identificare le transizioni abilitate__.
+Avendo infatti introdotto degli identificatori simbolici che mascherano i timestamp dei gettoni, capire se una transizione sia abilitata o meno non è più così facile.
+
+Tuttavia, è possibile dimostrare che _la __soddisfacibilità__ del vincolo generato da un eventuale scatto della transizione __implica__ la sua __abilitazione___: se esiste cioè un assegnamento di timestamp agli identificatori simbolici che __rende vero__ il vincolo allora la transizione è abilitata e può scattare.
+Il motivo di ciò appare evidente quando ci si accorge che nella generazione del vincolo abbiamo già tenuto in conto di tutti gli aspetti che avremmo osservato per stabilire se la transizione fosse abilitata o meno.
+
+![](/assets/16_esempio_albero_raggiungibilita_reti_temporizzate-grafico.png)
+
+Proprio riguardo la soddisfacibilità viene poi fatta una distinzione a livello grafico nell'albero: essendo gli stati simbolici _insiemi_ di marcature è possibile che una transizione sia abilitata in alcune di esse mentre è disabilitata in altre. \
+Quando questo succede, lo stato generato dalla transizione __potrebbe essere uno stato finale__, in quanto potrebbe aver disabilitato tutte le transizioni: ciò si comunica graficamente con un __nodo circolare__, mentre i nodi (e quindi gli stati) i cui vincoli sono __sempre soddisfacibili__ si indicano con dei __nodo rettangolari__.
+
+![](/assets/16_esempio_albero_raggiungibilita_reti_temporizzate.png)
+
+Alcuni operano poi una distinzione sulle frecce che collegano i nodi dell'albero: una freccia con punta nera indica che la transizione è sempre possibile, mentre una freccia con alla base un pallino bianco indica che per rendere possibile la transizione è stato violato qualche parte del vincolo, per cui non è detto che la transizione sia possibile in nessuna delle marcature rappresentate dallo stato simbolico.
+
+### Proprietà _bounded_
+
+Eseguendo l'algoritmo a mano per un paio di iterazioni ci si rende ben presto conto di una cosa: il processo __non termina__!
+
+Questo è dovuto al fatto che __non avendo una forma normale__ per i vincoli che permetta di confrontare tra di loro gli stati simbolici non è possibile stabilire se si sia già visitato o meno uno stato: i vincoli si allungano così sempre di più, creando sempre stati simbolici nuovi (almeno sulla carta).
+
+Si ottiene cioè un __albero infinito__.
+Nonostante ciò, tale albero è comunque particolarmente utile perché permette di __verificare proprietà entro un certo limite di tempo__: si parla per esempio di __bounded liveness__ e __bounded invariance__, delle caratteristiche molto preziose sopratutto per lo studio dei sistemi Hard Real-Time.
+
+## Dall'albero al grafo aciclico
+
+Esattamente come per le reti di Petri classiche, costruito l'albero di raggiungibilità ci piacerebbe ristrutturarlo per trasformarlo in un __grafo__ che illustri più concisamente l'evoluzione del sistema rappresentato dalla rete.
+Di certo non possiamo sperare di ottenere un _grafo ciclico_ in quanto per sua stessa natura _il tempo non può tornare indietro_, ma c'è qualche chance di ottenere un __grafo aciclico__?
+
+Abbiamo già detto che a causa di come sono costruiti i nuovi stati simbolici è impossibile riottenere più volte lo stesso esatto stato.
+Ammettendo tuttavia di __dimenticare la storia__ di come si è giunti in un certo nodo (ovvero l'insieme di transizioni che hanno portato ad esso) si potrebbe sperare di __ritrovare alcuni stati__ che pur caratterizzati da storie diverse possiedono la _stessa marcatura simbolica_ e lo _stesso insieme di vincoli sugli identificatori simbolici_ presenti al suo interno.
+
+Vediamo dunque una serie di tecniche che permettono, al costo di __perdere una serie di informazioni__, di individuare le _somiglianze_ tra diversi stati simbolici in modo da raggrupparli in una serie di "super-stati" che fungano da nodi per il grafo che intendiamo costruire.
+
+### Semplificare i vincoli: l'algoritmo di Floyd
+
+Per dimenticare la storia di come si è giunti in un certo stato simbolico è innanzitutto necessario __semplificare i vincoli__: come abbiamo visto nella formula precedente, ogni nuovo stato simbolico ereditava infatti i vincoli del precedente, cosa che permette di distinguere marcature identiche a cui si è giunti in modo diverso.
+
+È dunque necessario __esprimere i vincoli solo in termini della marcatura corrente__: possiamo infatti considerare i vincoli sugli identificatori simbolici non più presenti nella rete come sostanzialmente inutili.
+Tuttavia, non basta cancellarli per risolvere la questione: sebbene il simbolo a cui fanno riferimento sia scomparso, essi potrebbero ancora esprimere __vincoli indiretti__ sugli identificatori ancora presenti nella marcatura, che vanno ovviamente mantenuti. \\
+Si immagini per esempio di avere i vincoli \\(B - A \leq 5\\) e \\(C - B \leq 6\\) e che l'identificatore \\(B\\) sia ormai scomparso dalla rete.
+Sebbene si riferiscano a un simbolo ormai non più presente, tali vincoli contengono ancora delle informazioni: sommando le due disequazioni membro a membro si ottiene infatti che \\(C - A \leq 11\\), un vincolo su variabili presenti che era espresso _indirettamente_.
+
+![](/assets/16_Floyd-grafo-cut.png)
+
+Per rimappare in modo semplice gli effetti dei vincoli sulle variabili non più presenti nella marcatura su quelle presenti si utilizza spesso l'__algoritmo di Floyd__, che funziona come segue:
+
+1. Si riconducono tutti i vincoli alla __forma__ \\(\bf{A - B \leq k}\\), dove \\(A\\) e \\(B\\) sono identificatori simbolici e \\(k\\) è una costante numerica; per esempio:
+
+    $$ A+2 \leq B \leq A+5 \\: \\: \\: \longrightarrow \\: \\: \\: A - B \leq -2 \\: \text{ e } \\: B - A \leq 5 $$
+
+    $$ B \leq C \leq B+6 \\: \\: \\: \longrightarrow \\: \\: \\: B - C \leq 0 \\: \text{ e } \\: C- B \leq 6 $$
+
+2. Si costruisce una __matrice__ in cui ad ogni riga e colonna corrisponde un identificatore simbolico e l'intersezione tra la riga \\(A\\) e la colonna \\(B\\) corrisponde al __valore \\(\bf{k}\\) tale per cui__ \\(\bf{A - B \leq k}\\) in base ai vincoli ricavati al punto precedente, mentre per i valori non noti si scrive semplicemente un __punto di domanda__;
+
+3. Si __riempiono tutti i posti contrassegnati da punti di domanda__ utilizzando la seguente formula:  
+
+    $$ \boxed{m[i,j] = m[i,k] + m[k,j]} $$
+
+    che mima la somma membro a membro delle disequazioni che rappresentano i vincoli.
+    In questo modo è possibile scoprire i __vincoli indiretti__ tra variabili;
+
+4. Si __esplicitano i vincoli indiretti__ relativi agli identificatori simbolici presenti nella marcatura corrente e __si eliminano__ i vincoli che contengono gli identificatori non inclusi.
+
+![](/assets/16_Floyd-matrici.png)
+
+Applicato l'algoritmo di Floyd, ottenuti i vincoli impliciti ed eliminati i vincoli contenenti identificatori simbolici è possibile semplificare di molto l'insieme dei vincoli di nodi del grafo, identificando anche le prime _somiglianze_ tra nodi.
+
+### Inclusione tra stati
+
+Il raggruppamento offerto dalla semplificazione dei vincoli tramite l'algoritmo di Floyd non è però sufficiente a ottenere grafi aciclici soddisfacenti.
+Si consideri per esempio la rete in figura:
+
+![](/assets/16_esempio-inclusione.png)
+
+Come si vede, essa genera una serie di nodi nel grafo di raggiungibilità tutti diversi nonostante essi abbiano la __stessa marcatura__ e l'unica differenza sia data dalla costante nel vincolo.
+Per semplificare situazioni come queste viene introdotto il concetto di __inclusione__ (o _contenimento_) __tra stati__: sapendo infatti che gli stati simbolici rappresentano _insiemi_ di marcature è opportuno chiedersi se alcuni di essi possano essere _sottoinsiemi_ di altri.
+
+Ecco dunque che si dice che _uno stato \\(A\\) è __contenuto__ in un altro stato \\(B\\) se e solo se __tutte__ le marcature rappresentate da \\(A\\) sono rappresentate anche da \\(B\\)_.
+Ciò avviene quando:
+
+- \\(A\\) e \\(B\\) hanno lo __stesso assegnamento di timestamp__;
+- __i vincoli di \\(A\\) implicano i vincoli di \\(B\\)__, ovvero \\(C_A \rightarrow C_B\\).
+
+Decidiamo quindi di rappresentare nel grafo __solo gli stati contenuti__, introducendo però una distinzione grafica: la punta bianca della freccia indica che spostandosi lungo di essa si arriva ad un __sottoinsieme__ del "super-stato" di arrivo, ovvero uno stato avente _vincoli più stringenti_ di quelli mostrati.
+
+![](/assets/16_esempio-inclusione-2.png)
+
+### Tempi relativi
+
+Osservando l'evoluzione di una rete Time Basic ci si può poi accorgere di un ulteriore fatto: se le funzioni temporali delle transizioni __non fanno riferimento a tempi assoluti__, ovvero a specifici istanti di esecuzione della rete, per comprendere come la rete può evolvere a partire da una certa marcatura è __sufficiente osservare i vincoli relativi tra i timestamp__.
+
+Si prenda per esempio in considerazione la rete in figura:
+
+![](/assets/16_esempio-tempi-relativi-crop.png)
+
+ci si accorge che mantenere il riferimento ai tempi assoluti \\(0\\) e \\(10\\) introdotti dai vincoli iniziali farebbe sì che vengano generati __infiniti stati__ anche considerando la possibilità di inclusione.
+Poiché _l'unica transizione presente nella rete non fa alcun riferimento a tempi assoluti_, si può quindi __eliminare i vincoli legati a tempi assoluti__ ottenendo il secondo grafo in figura: esso rappresenta alla perfezione l'evoluzione della rete (che può far scattare la transizione \\(T1\\) un numero infinito di volte) pur ignorando i vincoli sul valore iniziale del timestamp dell'unico gettone presente.
+
+### Tempi anonimi
+
+Si può infine introdurre un'ulteriore astrazione: ci si rende infatti conto che _se il timestamp associato ad un gettone in una marcatura M non verrà __mai più utilizzato__ per stabilire come evolverà la rete a partire da quella marcatura_, allora è possibile __anonimizzare__ il tempo di tale gettone.
+L'identificatore simbolico del gettone viene cioè sostituito da un __identificatore anonimo__ \\(\tau_A\\) e i vincoli che lo coinvolgono cancellati: questo permette di riconoscere la somiglianza tra stati simbolici che, pur diversi a livello di timestamp dei gettoni, __evolvono nello stesso modo__.
+
+Si consideri per esempio la rete in figura:
+
+![](/assets/16_esempio-time-anonymous-crop.png)
+
+All'interno di questa rete, il timestamp del gettone in \\(P2\\) non ha alcuna influenza sull'evoluzione della rete: esso funge infatti unicamente da _zero relativo_ per determinare il timestamp del gettone in \\(P1\\), che sarà maggiore di esso di tante unità quanto il numero di volte che è scattata la transizione \\(T1\\).
+Il gettone può dunque essere reso anonimo, eliminando l'unico vincolo che, a conti fatti, non aggiunge nulla alla nostra conoscenza della rete.
+
+Non esiste una vera e propria regola formale per capire quali gettoni siano anonimizzabili, ma esistono una serie di __euristiche__ che possono suggerire tale eventualità: così, per esempio, è molto probabile che i __gettoni morti__ (gettoni in posti che non appartengono al preset di alcuna transizione) possano essere resi anonimi.
+
+### Conclusioni
+
+L'utilizzo delle tecniche di __raggruppamento degli stati simbolici__ appena viste permette di costruire dei grafi di raggiungibilità più coincisi per le reti Time Basic, ma non senza __sacrificare__ una serie di __informazioni__.
+Infatti:
+
+- la tecnica di __inclusione__ introduce la possibilità che nel grafo esistano dei __cammini non percorribili__ dovuti al fatto che muovendosi tra _sottoinsiemi_ degli stati rappresentati è possibile che lo stato simbolico reale in cui ci si trova non permetta una certa transizione che è invece possibile a parte degli stati rappresentati dal nodo;
+- con l'abolizione dei __vincoli assoluti__ si perdono informazioni sulle __relazioni precise__ tra gli stati (anche se è possibile arricchire le informazioni sugli archi per non perderne troppe);
+- __anonimizzando__ alcuni gettoni potrebbe non essere sempre possibile verificare la __raggiungibilità__ di una marcatura definita da vincoli sui timestamp.
+
+Si tratta di un equo prezzo da pagare per una rappresentazione semplice ed efficace dell'evoluzione della rete.
+
+## Albero di copertura temporale?
+
+Avevamo detto che sulle reti TB non era possibile utilizzare la tecnica di analisi di copertura vista per le normali reti di Petri a causa della __distinguibilità__ tra gettoni dovuta ai rispettivi timestamp.
+
+Tuttavia, l'introduzione della possibilità di __anonimizzare i gettoni__ è in grado di far riconsiderare tale conclusione: i gettoni anonimi sono infatti tutti __equivalenti__ e indistinguibili, motivo per cui potrebbero essere rappresentati globalmente solo dal loro __numero__ \\(\omega_{\tau A}\\).
+
+Non approfondiamo la questione, ma esiste un'ipotesi non dimostrata che suppone che _le reti TB non limitate siano non limitate __solo__ sul numero di gettoni anonimi_ in quanto in caso contrario bisognerebbe avere una rete che si interessi del passato all'infinito.
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
new file mode 100644
index 0000000000000000000000000000000000000000..d023b56965b307155d49601ea98885b3d9fc4546
--- /dev/null
+++ b/src/SUMMARY.md
@@ -0,0 +1,216 @@
+# Sommario
+
+[Introduzione](./00_informazioni/00_index.md)
+[Autori](./00_informazioni/01_contributori.md)
+[Come contribuire](./00_informazioni/01_contribuire.md)
+[Convenzioni di stile e contenuto](./00_informazioni/03_convenzioni.md)
+
+# Prodotto e processo
+
+- [Ingegneria, qualità e processi](./01_introduzione/00_index.md)
+    - [Informazioni logistiche](./01_introduzione/01_logistica.md)
+    - [Ingegneria del software](./01_introduzione/02_ingegneria.md)
+    - [Qualità del software](./01_introduzione/03_qualita.md)
+    - [Processo di sviluppo](./01_introduzione/04_processo.md)
+
+- [Modelli di ciclo di vita del software](./02_ciclo-vita/00_index.md)
+    - [Modelli sequenziali](./02_ciclo-vita/01_modelli-sequenziali.md)
+    - [Modelli iterativi](./02_ciclo-vita/02_modelli-iterativi.md)
+    - [Modelli incrementali](./02_ciclo-vita/03_modelli-incrementali.md)
+    - [Metodologie Agile](./02_ciclo-vita/04_metodologie-agile.md)
+
+- [eXtreme Programming](./03_extreme-programming/00_index.md)
+    - [Test Driven Development](./03_extreme-programming/01_tdd.md)
+    - [Fondamenti](./03_extreme-programming/02_fondamenti.md)
+    - [Tecniche](./03_extreme-programming/03_tecniche/00_index.md)
+        - [Planning game](./03_extreme-programming/03_tecniche/01_planning-game.md)
+        - [Brevi cicli di rilascio](./03_extreme-programming/03_tecniche/02_brevi-cicli.md)
+        - [Uso di una metafora](./03_extreme-programming/03_tecniche/03_metafora.md)
+        - [Presumere la semplicità](./03_extreme-programming/03_tecniche/04_semplicita.md)
+        - [Testing](./03_extreme-programming/03_tecniche/05_testing.md)
+        - [Refactoring](./03_extreme-programming/03_tecniche/06_refactoring.md)
+        - [Pair programming](./03_extreme-programming/03_tecniche/07_pair-programming.md)
+        - [Proprietà collettiva](./03_extreme-programming/03_tecniche/08_proprieta-collettiva.md)
+        - [Integrazione continua](./03_extreme-programming/03_tecniche/09_integrazione-continua.md)
+        - [Settimana da 40 ore](./03_extreme-programming/03_tecniche/10_settimana.md)
+        - [Cliente sul posto](./03_extreme-programming/03_tecniche/11_cliente-posto.md)
+        - [Standard di codifica](./03_extreme-programming/03_tecniche/12_standard-codifica.md)
+        - [They're just rules](./03_extreme-programming/03_tecniche/13_just-rules.md)
+    - [Relazione con il modello a cascata](./03_extreme-programming/04_cascata.md)
+    - [Documentazione](./03_extreme-programming/05_documentazione.md)
+    - [Criticità](./03_extreme-programming/06_criticita.md)
+
+- [Open source](./04_open-source/00_index.md)
+    - [Letteratura](./04_open-source/01_letteratura/00_index.md)
+        - [The Cathedral and the Bazaar](./04_open-source/01_letteratura/01_cathedral-bazaar.md)
+        - [Care and Feeding of FOSS](./04_open-source/01_letteratura/02_care-feeding.md)
+        - [The Emerging Economic Paradigm Of Open Source](./04_open-source/01_letteratura/03_emerging-economic-paradigm.md)
+        - [An empirical study of open-source and closed-source software products](./04_open-source/01_letteratura/04_empirical-study.md)
+    - [Sfide](./04_open-source/02_sfide.md)
+
+- [Software Configuration Management](./05_scm/00_index.md)
+    - [Storia](./05_scm/01_storia.md)
+    - [Meccanismo di base](./05_scm/02_meccanismo.md)
+    - [git](./05_scm/03_git.md)
+
+- [Git workflow e strumenti](./06_git-workflow/00_index.md)
+    - [GitFlow](./06_git-workflow/01_gitflow.md)
+    - [Hosting centralizzato](./06_git-workflow/02_hosting-centralizzato.md)
+    - [Gerrit](./06_git-workflow/03_gerrit.md)
+    - [Strumenti dell'opensource](./06_git-workflow/04_strumenti-opensource/00_index.md)
+        - [Build automation](./06_git-workflow/04_strumenti-opensource/01_build-automation.md)
+        - [Bug tracking](./06_git-workflow/04_strumenti-opensource/02_bug-tracking.md)
+
+# Progettazione e implementazione
+
+- [Progettazione](./07_progettazione/00_index.md)
+    - [Refactoring](./07_progettazione/01_refactoring.md)
+    - [Design knowledge](./07_progettazione/02_design-knowledge.md)
+    - [Conoscenze preliminari](./07_progettazione/03_conoscenze-preliminari.md)
+    - [Principio Tell-Don't-Ask](./07_progettazione/04_tell-dont-ask.md)
+    - [Interface segregation](./07_progettazione/05_interface-segregation.md)
+    - [Esempio](./07_progettazione/06_esempio/00_index.md)
+        - [Interface segregation all'opera](./07_progettazione/06_esempio/01_interface-segregation.md)
+        - [Collegamento statico e dinamico](./07_progettazione/06_esempio/02_collegamento-statico-dinamico.md)
+        - [Loose coupling](./07_progettazione/06_esempio/03_loose-coupling.md)
+        - [Interfacce multiple](./07_progettazione/06_esempio/04_interfacce-multiple.md)
+        - [Contract-based design vs programmazione difensiva](./07_progettazione/06_esempio/05_contract-based.md)
+        - [Classi astratte](./07_progettazione/06_esempio/06_classi-astratte.md)
+    - [Analisi del testo naturale](./07_progettazione/07_analisi-testo-naturale.md)
+
+- [Patterns](./08_patterns/00_index.md)
+    - [Discutere di pattern: i meta-patterns](./08_patterns/01_meta-patterns.md)
+    - [Singleton](./08_patterns/02_singleton.md)
+    - [Iterator](./08_patterns/03_iterator.md)
+    - [Chain of responsibility](./08_patterns/04_chain-of-responsibility.md)
+    - [Flyweight](./08_patterns/05_flyweight.md)
+    - [NullObject](./08_patterns/06_nullobject.md)
+    - [Strategy](./08_patterns/07_strategy.md)
+    - [Observer](./08_patterns/08_observer.md)
+    - [Adapter](./08_patterns/09_adapter.md)
+    - [Facade](./08_patterns/10_facade.md)
+    - [Composite](./08_patterns/11_composite.md)
+    - [Decorator](./08_patterns/12_decorator.md)
+    - [State](./08_patterns/13_state.md)
+    - [Factory method](./08_patterns/14_factory.md)
+    - [Abstract factory](./08_patterns/15_abstract-factory.md)
+    - [Model view controller](./08_patterns/16_mvc.md)
+    - [Model view presenter](./08_patterns/17_mvp.md)
+    - [Builder](./08_patterns/18_builder.md)
+
+- [UML](./09_uml/00_index.md)
+    - [Class diagram](./09_uml/01_class-diagram.md)
+    - [Sequence diagram](./09_uml/02_sequence-diagram.md)
+    - [State diagram](./09_uml/03_state-diagram.md)
+    - [Superstate](./09_uml/04_superstate.md)
+    - [Use cases diagram](./09_uml/05_use-cases.md)
+    - [Activity diagram](./09_uml/06_activity-diagram.md)
+    - [Component diagram](./09_uml/07_component-diagram.md)
+    - [Deployment diagram](./09_uml/08_deployment-diagram.md)
+
+- [Mocking](./10_mocking/00_index.md)
+    - [Test Double](./10_mocking/01_test-double/00_index.md)
+        - [Dummy objects](./10_mocking/01_test-double/01_dummy-objects.md)
+        - [Stub objects](./10_mocking/01_test-double/02_stub-objects.md)
+        - [Mock objects](./10_mocking/01_test-double/03_mock-objects.md)
+        - [Spy objects](./10_mocking/01_test-double/04_spy-objects.md)
+        - [Fake objects](./10_mocking/01_test-double/05_fake-objects.md)
+        - [Riepilogo](./10_mocking/01_test-double/06_riepilogo.md)
+    - [Mockito](./10_mocking/01_mockito.md)
+
+# Verifica e convalida
+
+- [Verifica e convalida](./11_verifica-convalida/00_index.md)
+    - [Terminologia](./11_verifica-convalida/01_terminologia.md)
+    - [Tecniche](./11_verifica-convalida/02_tecniche.md)
+
+- [Testing e criteri di selezione](./12_testing/00_index.md)
+    - [Definizioni](./12_testing/01_definizioni.md)
+    - [Proprietà](./12_testing/02_proprieta.md)
+    - [Utilità di un test](./12_testing/03_utilita.md)
+    - [Criteri di selezione](./12_testing/04_criteri-noti/00_index.md)
+        - [Criterio di copertura dei comandi](./12_testing/04_criteri-noti/01_comandi.md)
+        - [Criterio di copertura delle decisioni](./12_testing/04_criteri-noti/02_decisioni.md)
+        - [Criterio di copertura delle condizioni](./12_testing/04_criteri-noti/03_condizioni.md)
+        - [Criterio di copertura delle decisioni e condizioni](./12_testing/04_criteri-noti/04_decisioni-condizioni.md)
+        - [Criterio di copertura delle condizioni composte](./12_testing/04_criteri-noti/05_condizioni-composte.md)
+        - [Criterio di copertura delle condizioni e decisioni modificate](./12_testing/04_criteri-noti/06_condizioni-decisioni-modificate.md)
+        - [Implicazioni tra criteri di copertura](./12_testing/04_criteri-noti/07_implicazioni.md)
+    - [Altri criteri](./12_testing/05_altri-criteri/00_index.md)
+        - [Criterio di copertura dei cammini](./12_testing/05_altri-criteri/01_cammini.md)
+        - [Criterio di \\(n\\)-copertura dei cicli](./12_testing/05_altri-criteri/02_cicli.md)
+    - [Mappa finale implicazioni tra criteri di copertura](./12_testing/06_mappa.md)
+
+- [Analisi statica](./13_analisi-statica/00_index.md)
+    - [Compilatori](./13_analisi-statica/01_compilatori.md)
+    - [Analisi Data Flow](./13_analisi-statica/02_data-flow/00_index.md)
+        - [Regole](./13_analisi-statica/02_data-flow/01_regole.md)
+        - [Sequenze](./13_analisi-statica/02_data-flow/02_sequenze.md)
+    - [Testing](./13_analisi-statica/03_testing.md)
+    - [Criteri di copertura](./13_analisi-statica/04_criteri/00_index.md)
+        - [Criterio di copertura delle definizioni](./13_analisi-statica/04_criteri/01_definizioni.md)
+        - [Criterio di copertura degli usi](./13_analisi-statica/04_criteri/02_usi.md)
+        - [Criterio di copertura dei cammini DU](./13_analisi-statica/04_criteri/03_cammini-du.md)
+    - [Oltre le variabili](./13_analisi-statica/05_oltre-variabili.md)
+
+- [Processi di review](./14_review/00_index.md)
+    - [Bebugging](./14_review/01_bebugging.md)
+    - [Analisi mutazionale](./14_review/02_analisi-mutazionale/00_index.md)
+        - [Criterio di copertura dei mutanti](./14_review/02_analisi-mutazionale/01_copertura-mutanti.md)
+        - [Generazione dei mutanti](./14_review/02_analisi-mutazionale/02_generazione.md)
+        - [Automazione](./14_review/02_analisi-mutazionale/03_automazione.md)
+    - [Object oriented testing](./14_review/03_object-oriented.md)
+    - [Testing funzionale](./14_review/04_testing-funzionale/00_index.md)
+        - [Testing delle interfacce](./14_review/04_testing-funzionale/01_interfacce.md)
+        - [Classi di equivalenza](./14_review/04_testing-funzionale/02_classi-equivalenza.md)
+        - [Test di frontiera](./14_review/04_testing-funzionale/03_test-frontiera.md)
+        - [Category partition](./14_review/04_testing-funzionale/04_category-partition.md)
+        - [Object orientation e testing funzionale](./14_review/04_testing-funzionale/05_object-orientation.md)
+    - [Software inspection](./14_review/05_software-inspection/00_index.md)
+        - [Fagan code inspection](./14_review/05_software-inspection/01_fagan.md)
+        - [Automazione](./14_review/05_software-inspection/02_automazione.md)
+        - [Pro e contro](./14_review/05_software-inspection/03_pro-contro.md)
+        - [Confronto tra tecniche di verifica e convalida](./14_review/05_software-inspection/04_verifica-convalida.md)
+        - [Gruppi di test autonomi](./14_review/05_software-inspection/05_gruppi-test.md)
+    - [Modelli statistici](./14_review/06_modelli-statistici.md)
+    - [Debugging](./14_review/07_debugging.md)
+
+# Reti di Petri
+
+- [Reti di Petri](./15_reti-petri/00_index.md)
+    - [Definizioni](./15_reti-petri/01_definizioni.md)
+    - [Macchine a stati finiti](./15_reti-petri/02_fsm.md)
+    - [Relazioni tra transizioni](./15_reti-petri/03_relazioni/00_index.md)
+        - [Sequenza](./15_reti-petri/03_relazioni/01_sequenza.md)
+        - [Conflitto](./15_reti-petri/03_relazioni/02_conflitto.md)
+        - [Concorrenza](./15_reti-petri/03_relazioni/03_concorrenza.md)
+    - [Insieme di raggiungibilità](./15_reti-petri/04_insieme-raggiungibilita.md)
+    - [Limitatezza](./15_reti-petri/05_limitatezza.md)
+    - [Vitalità di una transizione](./15_reti-petri/06_vitalita.md)
+    - [Capacità dei posti](./15_reti-petri/07_capacita.md)
+    - [Archi inibitori](./15_reti-petri/08_archi-inibitori.md)
+    - [Eliminazione pesi sugli archi](./15_reti-petri/09_pesi-archi.md)
+    - [Conservatività](./15_reti-petri/10_conservativita.md)
+    - [Stato base e rete revertibile](./15_reti-petri/11_stato-base.md)
+
+- [Analisi di reti di Petri](./16_reti-petri-analisi/00_index.md)
+    - [Albero di raggiungibilità](./16_reti-petri-analisi/01_albero-raggiungibilita.md)
+    - [Albero di copertura](./16_reti-petri-analisi/02_albero-copertura.md)
+    - [Rappresentazione matriciale](./16_reti-petri-analisi/03_rappresentazione-matriciale.md)
+    - [Analisi statica](./16_reti-petri-analisi/04_analisi-statica/00_index.md)
+        - [\\(P\\)-invarianti](./16_reti-petri-analisi/04_analisi-statica/01_p-invarianti.md)
+        - [\\(T\\)-invarianti](./16_reti-petri-analisi/04_analisi-statica/02_t-invarianti.md)
+    - [Controllori con specifica a stati proibiti](./16_reti-petri-analisi/05_controllori.md)
+    - [Reti con priorità](./16_reti-petri-analisi/06_reti-priorita.md)
+
+- [Reti di Petri temporizzate](./17_reti-petri-temporizzate/00_index.md)
+    - [Modelli temporali](./17_reti-petri-temporizzate/01_modelli-temporali.md)
+    - [Reti Time Basic](./17_reti-petri-temporizzate/02_reti-tb.md)
+    - [Evoluzione](./17_reti-petri-temporizzate/03_evoluzione/00_index.md)
+        - [Semantica temporale debole](./17_reti-petri-temporizzate/03_evoluzione/01_wts.md)
+        - [Semantica temporale monotonica debole](./17_reti-petri-temporizzate/03_evoluzione/02_mwts.md)
+        - [Semantica temporale forte](./17_reti-petri-temporizzate/03_evoluzione/03_sts.md)
+        - [Semantica temporale mista](./17_reti-petri-temporizzate/03_evoluzione/04_mista.md)
+    - [Analisi delle reti Time Basic](./17_reti-petri-temporizzate/04_analisi/00_index.md)
+        - [Reti TB come reti ad alto livello?](./17_reti-petri-temporizzate/04_analisi/01_alto-livello.md)
+        - [Analisi di raggiungibilità temporale](./17_reti-petri-temporizzate/04_analisi/02_raggiungibilita.md)
diff --git a/src/assets b/src/assets
new file mode 120000
index 0000000000000000000000000000000000000000..ec2e4be2f839d0372d9c88d9a8b826596a361e64
--- /dev/null
+++ b/src/assets
@@ -0,0 +1 @@
+../assets
\ No newline at end of file