From 8fa918a1774d4072b3ec1c3d4f8cd6995c579e72 Mon Sep 17 00:00:00 2001
From: Adrien Bonnin <adbonnin@ippon.fr>
Date: Thu, 18 Aug 2022 10:01:50 +0200
Subject: [PATCH] :bookmark: Version 1.0

---
 README.md                                | 664 +++++++++++++++++++++--
 docs/images/change_notifier_provider.png | Bin 0 -> 2450 bytes
 docs/images/declaration_provider.png     | Bin 0 -> 2910 bytes
 docs/images/future_provider.png          | Bin 0 -> 3192 bytes
 docs/images/listener.png                 | Bin 0 -> 9733 bytes
 docs/images/provider.png                 | Bin 0 -> 1502 bytes
 docs/images/schemas.vsdx                 | Bin 0 -> 33089 bytes
 docs/images/select.png                   | Bin 0 -> 5505 bytes
 docs/images/state_notifier_provider.png  | Bin 0 -> 3203 bytes
 docs/images/state_provider.png           | Bin 0 -> 2185 bytes
 docs/images/stream_provider.png          | Bin 0 -> 3878 bytes
 docs/images/test_overrides.png           | Bin 0 -> 3232 bytes
 docs/images/watch.png                    | Bin 0 -> 6505 bytes
 test/change_notifier_provider.dart       |  36 ++
 test/dependance_cyclique.dart            |  19 +
 test/future_provider.dart                |  26 +
 test/listener.dart                       |  40 ++
 test/provider.dart                       |  15 +
 test/select.dart                         |  36 ++
 test/state_notifier_provider.dart        |  40 ++
 test/state_provider.dart                 |  22 +
 test/stream_provider.dart                |  38 ++
 test/test_overrides.dart                 |  23 +
 test/utils.dart                          |  16 +
 test/watch.dart                          |  22 +
 test/widget_test.dart                    |  30 -
 26 files changed, 940 insertions(+), 87 deletions(-)
 create mode 100644 docs/images/change_notifier_provider.png
 create mode 100644 docs/images/declaration_provider.png
 create mode 100644 docs/images/future_provider.png
 create mode 100644 docs/images/listener.png
 create mode 100644 docs/images/provider.png
 create mode 100644 docs/images/schemas.vsdx
 create mode 100644 docs/images/select.png
 create mode 100644 docs/images/state_notifier_provider.png
 create mode 100644 docs/images/state_provider.png
 create mode 100644 docs/images/stream_provider.png
 create mode 100644 docs/images/test_overrides.png
 create mode 100644 docs/images/watch.png
 create mode 100644 test/change_notifier_provider.dart
 create mode 100644 test/dependance_cyclique.dart
 create mode 100644 test/future_provider.dart
 create mode 100644 test/listener.dart
 create mode 100644 test/provider.dart
 create mode 100644 test/select.dart
 create mode 100644 test/state_notifier_provider.dart
 create mode 100644 test/state_provider.dart
 create mode 100644 test/stream_provider.dart
 create mode 100644 test/test_overrides.dart
 create mode 100644 test/utils.dart
 create mode 100644 test/watch.dart
 delete mode 100644 test/widget_test.dart

diff --git a/README.md b/README.md
index f20313d..1cbae49 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,642 @@
-# article_flutter_riverpod
+# Découvrir Riverpod par la pratique
 
+Cet article a pour objectif de faire découvrir pas à pas la bibliothèque Riverpod à partir d'exemples d'utilisation.
 
+## Introduction
 
-## Getting started
+Riverpod est une bibliothèque de state management pour Flutter.
+Le state management, ou gestion d'état en français, a pour responsabilité de mettre à disposition les différents objets qui constituent l'état de l'application.
+Le terme _"état"_, employé tout au long de cet article, fait précisément référence à ces états.
 
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
+La principale particularité de Flutter est que tout est widget.
+Le state management ne faisant pas exception, de nombreuses bibliothèques sont apparues proposant chacune leurs propres patterns et méthodes de fonctionnement.
+Parmi les plus utilisées on peut citer [BLoC](https://pub.dev/packages/bloc), [GetX](https://pub.dev/packages/get), [Provider](https://pub.dev/packages/provider) et aujourd'hui c'est [Riverpod](https://pub.dev/packages/riverpod) qui nous intéresse.
 
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
+Le créateur et principal contributeur de Riverpod, Remi Rousselet, n'en est pas à son premier ballon d'essai.
+Reconnu au sein de la communauté Dart et Flutter, il est entre autre l'auteur de [freezed](https://pub.dev/packages/freezed) (un générateur de code pour les data-classes et unions), [flutter_hooks](https://pub.dev/packages/flutter_hooks) (une implémentation des hooks React) et [Provider](https://pub.dev/packages/provider) (un autre state management).
+Cette dernière est le point de départ d'une réflexion plus ambitieuse sur le state management qui impliquera une réécriture complète pour aboutir à Riverpod.
+Cet article fait référence à la bibliothèque Provider sous ces termes uniquement pour ne pas la confondre avec la classe `Provider` présente dans Riverpod.
 
-## Add your files
+## Installation
+
+Dès son installation, Riverpod se distingue de ses concurrentes par son découpage en plusieurs bibliothèques :
+
+- [`riverpod`](https://pub.dev/packages/riverpod) contient le code principale, sans aucune adhérence.
+- [`riverpod_flutter`](https://pub.dev/packages/flutter_riverpod) contient le code spécifique pour le framework Flutter.
+- [`hooks_riverpod`](https://pub.dev/packages/hooks_riverpod) contient le code spécifique pour la bibliothèque [`flutter_books`](https://pub.dev/packages/flutter_hooks).
 
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
+La bibliothèque `flutter_riverpod` sera utilisée pour aborder l'ensemble des fondamentaux de Riverpod.
+La dépendance est à ajouter dans le fichier pubspec.yaml :
 
+```yaml
+dependencies:
+  flutter_riverpod: ^1.0.3
 ```
-cd existing_repo
-git remote add origin https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod.git
-git branch -M main
-git push -uf origin main
+
+## Une histoire de Provider
+
+L'opération la plus élémentaire est de récupérer un état stocké dans un `ProviderContainer` par l'intermédiaire d'un `Provider`.
+
+Le `ProviderContainer` est un conteneur d'états.
+Pour simplifier, c'est une `Map` avec comme clefs les __instances des providers__ et comme valeurs les états correspondants.
+Utiliser des instances comme clef corrige la limitation de la bibliothèque Provider qui ne supporte qu'une valeur par classe.
+
+Le `Provider` est un moyen de __récupérer__ un état présent dans un `ProviderContainer`.
+La déclaration d'un provider permet d'indiquer __le type__ et __la valeur d'initialisation__ de l'état auquel il correspond.
+
+![Déclaration d'un Provider](docs/images/declaration_provider.png)
+
+> Déclarer un provider permet de __typer__, __initialiser__ et __récupérer__ un état.
+
+La récupération d'un état est réalisée de la manière suivante :
+
+[_source : provider.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/provider.dart)
+```dart
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+final intProvider = Provider<int>((_) => 13); // <1>
+
+void main() {
+  test('doit récupérer un état', () {
+    // given:
+    final container = ProviderContainer(); // <2>
+    addTearDown(container.dispose);
+
+    // expect:
+    expect(container.read(intProvider), equals(13)); // <3>
+  });
+}
 ```
 
-## Integrate with your tools
+![Provider](docs/images/provider.png)
 
-- [ ] [Set up project integrations](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/settings/integrations)
+Le `Provider` est instancié en tant que variable globale et son état est initialisé avec la valeur 13 `<1>`.
+L'instance du `ProviderContainer` contient les états `<2>` et permet de les récupérer en passant l'instance du `Provider` en paramètre de sa méthode `read` `<3>`.
 
-## Collaborate with your team
+Déclarer le `Provider` en tant que variable globale `<1>` peut sembler une erreur de conception mais il n'en est rien.
+Il est avant tout immutable et ne contient pas un état, mais constitue un __moyen de le récupérer__.
+La visibilité globale est alors un choix judicieux pour le rendre disponible n'importe où dans le code.
 
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
+Une autre particularité du `Provider` est de résoudre la principale erreur rencontrée par la bibliothèque `Provider`, à savoir le `ProviderNotFoundException`.
+Cette erreur est levée quand un état est accédé alors qu'il n'a pas été encore initialisé.
+Le provider étant responsable de l'initialisation de l'état, ce dernier sera systématiquement initialisé avant d'être récupéré.
 
-## Test and Deploy
+> Le contrat du `Provider` se limite à la récupération d'un état.  
+> Il est __trés fortement recommandé__ d'utiliser des objets immutables pour définir les états, les modifications internes sont à proscrire et ne seront pas notifiées.
 
-Use the built-in continuous integration in GitLab.
+Plus généralement, l'immutabilité des objets échangés est, entre autres, une bonne pratique dans le développement événementiel car elle limite les effets de bord.
+La déclaration de ces objets peut se révéler fastidieuse en Dart et c'est ce que propose de simplifier la bibliothèque [freezed](https://pub.dev/packages/freezed).
 
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
+## Test, test, test and test
 
-***
+Étant donné que le `ProviderContainer` contient les états, en instancier un nouveau pour chaque test permet de garantir leur isolation.
 
-# Editing this README
+En plus de contenir les états, le `ProviderContainer` gère aussi leurs cycles de vie et sa méthode `dispose` libère ces ressources à la fin des tests.
 
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!).  Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
+Pour homogénéiser leurs écritures et éviter d'éventuels oublis de libération de ressources, la déclaration des `ProviderContainer` peut être factorisée comme le fait Riverpod avec [la méthode createContainer](https://github.com/rrousselGit/riverpod/blob/v1.0.4/packages/flutter_riverpod/test/utils.dart#L11).
+Cette méthode sera par la suite utilisée dans les prochains tests de cet article.
 
-## Suggestions for a good README
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
+[_source : utils.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/utils.dart)
+```dart
+import 'package:flutter_test/flutter_test.dart';
+import 'package:riverpod/riverpod.dart';
 
-## Name
-Choose a self-explaining name for your project.
+ProviderContainer createContainer({
+  ProviderContainer? parent,
+  List<Override> overrides = const [],
+  List<ProviderObserver>? observers,
+}) {
+  final container = ProviderContainer(
+    parent: parent,
+    overrides: overrides,
+    observers: observers,
+  );
+  addTearDown(container.dispose);
+  return container;
+}
+```
 
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
+La variable `overrides` présente dans cette factory de `ProviderContainer` va se révéler particulièrement utile pour préparer la situation initiale des tests.
+La liste d'`Override`s qu'elle contient vient surcharger le comportement du `ProviderContainer`.
+Ces `Override`s sont retournés par les méthodes idoines du `Provider`, `overrideWithProvider` pour le remplacer et `overrideWithValue` pour remplacer l'état récupéré. 
+
+[_source : test_overrides.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/test_overrides.dart)
+```dart
+final intProvider = Provider<int>((_) => 13); // <1>
+final otherIntProvider = Provider<int>((_) => 13);
+
+void main() {
+  test('doit surcharger le comportement des Provider', () {
+    // given:
+    final container = createContainer(
+      overrides: [
+        intProvider.overrideWithProvider(Provider<int>((_) => 42)), // <2>
+        otherIntProvider.overrideWithValue(42),
+      ],
+    );
+
+    // expect:
+    expect(container.read(intProvider), 42); // <3>
+    expect(container.read(otherIntProvider), 42);
+  });
+}
+```
 
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
+![Provider](docs/images/test_overrides.png)
 
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
+Deux `Providers` sont déclarés pour retourner la valeur 13 `<1>`.
+Le comportement du `ProviderContainer` est surchargé pour remplacer le premier `Provider` et pour remplacer la valeur retournée par le second `<2>`.
+La valeur récupérée par les deux est maintenant de 42 `<3>`.
 
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
+## Changer d'état avec le StateProvider
+
+Le `Provider` est un moyen de récupérer un état, mais en aucun cas de le modifier.
+Ce comportement a été confié au `StateProvider` de la manière suivante :
+
+[_source : state_provider.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/state_provider.dart)
+```dart
+final intProvider = StateProvider((_) => 13); // <1>
+
+void main() {
+  test('doit modifier un état', () {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(intProvider), equals(13));
+
+    // when:
+    container.read(intProvider.notifier).state = 42; // <2>
+
+    // expect:
+    expect(container.read(intProvider), equals(42)); // <3>
+  });
+}
+```
+
+![Provider](docs/images/state_provider.png)
+
+Le `StateProvider` est instancié de la même manière qu'un `Provider`, en initialisant son état avec la valeur 13 `<1>`.
+Le notifier du `StateProvider` est récupéré par l'intermédiaire du `ProviderContainer` et son état est remplacé par la valeur 42 `<2>`.
+L'état du `StateProvider` a bien été mis à jour avec cette nouvelle valeur `<3>`.
+
+> Le contrat du `StateProvider` va de la récupération à la modification d'un état.  
+> Les modifications sont réalisées en affectant un nouvel objet à la variable `state` du `notifier`.  
+> Chacune de ces modifications est ensuite notifiée aux objets qui le surveillent.
+
+Ce fonctionnement implique la création d'un nouvel objet pour indiquer un changement d'état et conforte l'utilisation d'objets immutables.
+
+La mise à jour de l'état par le `StateProvider` met en évidence un pattern récurrent chez Riverpod.
+Étant donné que le `ProviderContainer` propose la méthode `read`, on aurait pu s'attendre qu'il propose son pendant, la méthode `write`.
+D'un point de vue conceptuel, l'unique interaction que partagent toutes les classes de `Provider` avec le `ProviderContainer` est de récupérer un état, d'où l'unique présence de la méthode read.
+Cependant, chaque classe de `Provider` dispose de son propre contrat et c'est par l'intermédiaire de providers additionnels que les comportements sont adaptés.
+Dans le cas du `StateProvider`, c'est le provider additionnel `notifier` qui ajoute le changement de valeur.
+
+## Surveillance des Providers
+
+En plus de lire l'état d'un `Provider`, il est possible de surveiller ses changements.
+Par souci de simplicité, le paramètre `ProviderRef`, présent dans l'initialisation de chaque `Provider`, avait été jusque-là ignoré.
+C'est par son intermédiaire qu'un `Provider` peut lire et/ou surveiller d'autres `Provider`s et ainsi former un graphe de dépendances.
+
+[_source : watch.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/watch.dart)
+```dart
+final intProvider = StateProvider<int>((_) => 13); // <1>
+final readProvider = Provider<int>((ref) => ref.read(intProvider));
+final watchProvider = Provider<int>((ref) => ref.watch(intProvider));
+
+void main() {
+  test('doit surveiller un état', () {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(readProvider), equals(13)); // <2>
+    expect(container.read(watchProvider), equals(13));
+
+    // when:
+    container.read(intProvider.notifier).state = 42; // <3>
+
+    // expect:
+    expect(container.read(readProvider), equals(13)); // <4>
+    expect(container.read(watchProvider), equals(42));
+  });
+}
+```
+
+![Provider](docs/images/watch.png)
+
+Le `StateProvider` `intProvider` est initialisé avec la valeur 13, `readProvider` vient lire son état et `watchProvider` le surveiller `<1>`.
+Lors de leurs première lecture, l'état des `readProvider` et `watchProvider` sont identiques à celui du `intProvider` `<2>`.
+L'état du `intProvider` est modifié avec la valeur 42 `<3>` et seulement `watchProvider` prend en compte ce changement `<4>`.
+
+Le comportement des méthodes `read` et `watch` est identique lors de la première initialisation, l'état du `intProvider` est récupéré pour être ensuite retourné.
+C'est lors de la modification du intProvider `<3>` que les comportements divergent.
+Le `readProvider` ne se préoccupe pas de cette nouvelle valeur alors que la `watchProvider` vient appeler de nouveau sa méthode d'initialisation `<1>` pour mettre à jour son état en adéquation avec celui du `intProvider`.
+
+> Le `ProviderRef` peut être concidéré comme une façade au `ProviderContainer`.  
+> Le comportement attendu de ses méthodes `read` et `watch` est identique selon que l'on l'utilise `ProviderContainer` dans le corps du test ou `ProviderRef` dans l'initialisation du `Provider`.
+
+Ce fonctionnement est illustré par le code ci-dessous tiré de Riverpod :
+
+[_source : provider_base.dart_](https://github.com/rrousselGit/riverpod/blob/v1.0.4/packages/riverpod/lib/src/framework/provider_base.dart#L657)
+```dart
+@override
+T read<T>(ProviderBase<T> provider) {
+  _assertNotOutdated();
+  assert(!_debugIsRunningSelector, 'Cannot call ref.read inside a selector');
+  assert(_debugAssertCanDependOn(provider), '');
+  return _container.read(provider);
+}
+```
+
+A noter que la surveillance vient créer un lien de dépendance entre les `Provider`s et peut mener à l'apparition de dépendances cycliques :
+
+[_source : dependance_cyclique.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/dependance_cyclique.dart)
+```dart
+final Matcher throwsProviderException = throwsA(const TypeMatcher<ProviderException>());
+
+final Provider<int> provider = Provider<int>((ref) => ref.watch(otherProvider)); // <1>
+final Provider<int> otherProvider = Provider<int>((ref) => ref.watch(provider));
+
+void main() {
+  test('doit lever une exception suite à une dépendance cyclique', () {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(() => container.read(provider), throwsProviderException); // <2>
+  });
+}
+```
+
+Une interdépendance est déclarée entre deux `Provider` `<1>` et à la lecture de l'un d'entre eux une exception est levée `<2>`.
+
+> Riverpod dispose d'un mécanisme qui vient lever une exception quand une dépendance cyclique est détectée.
+
+## Le listener qui écoutait à l'oreille des Providers
+
+Un autre moyen d'être notifié d'un changement d'état est de l'écouter avec la méthode `listen` proposée par le `ProviderContainer`.
+Cette méthode prend en paramètre une callback qui sera appelée lors de chaque changement d'état en passant en paramètres l'ancienne et la nouvelle valeur.
+
+[_source : listener.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/listener.dart)
+```dart
+final intProvider = StateProvider<int>((_) => 13); // <1>
+final watchProvider = Provider<int>((ref) => ref.watch(intProvider));
+
+void main() {
+  test('doit écouter un état', () {
+    const defaultValue = -1;
+
+    // given:
+    final container = createContainer();
+
+    var intValue = defaultValue; // <2>
+    container.listen<int>(intProvider, (_, next) => intValue = next);
+
+    var watchValue = defaultValue;
+    container.listen<int>(watchProvider, (_, next) => watchValue = next);
+
+    // expect:
+    expect(intValue, equals(defaultValue)); // <3>
+    expect(watchValue, equals(defaultValue));
+
+    // when:
+    container.read(intProvider.notifier).state = 42; // <4>
+
+    // then:
+    expect(intValue, equals(42)); // <5>
+    expect(watchValue, equals(defaultValue));
+
+    // when:
+    container.read(watchProvider); // <6>
+
+    // then:
+    expect(intValue, equals(42)); // <7>
+    expect(watchValue, equals(42));
+  });
+}
+```
+
+![Provider](docs/images/listener.png)
+
+Le `StateProvider` `intProvider` est initialisé avec la valeur 13 et `watchProvider` surveille ses modifications `<1>`.
+Des listeners écoutent leurs changements d'état respectif pour stocker les nouvelles valeurs `<2>`.
+Sans aucune modification, ces valeurs écoutées conservent leurs valeurs par défaut `<3>`.
+Après la modification de l'état du `intProvider` avec la valeur 42 `<4>` seulement son listener a été notifié `<5>`.
+Ce n'est qu'après la lecture du `watchProvider` que son listener est notifié `<6>`.
+
+> Écouter n'est pas surveiller.
+
+Écouter un provider avec la méthode `listen` permet d'être notifié lors d'un changement d'état.
+Ce changement d'état ne sera effectif qu'à partir du moment où il sera lue et non à partir du moment où il a été modifié, c'est un __fonctionnement passif__.
+C'est pour cette raison que la valeur de `watchValue` reste à -1 `<6>`.
+
+Surveiller un provider avec la méthode `watch` vient lire le nouvel état à la suite d'un changement.
+Ce changement d'état est effectif dès sa modification, c'est un __fonctionnement actif__.
+
+> Riverpod intialise les états de manière paresseuse (lazy).  
+> Un état ne sera initialisé qu'à partir du moment où il sera lu, par un `read` ou un `watch`.
+
+## Devenir sélectif dans les changements d'états
+
+Être notifié par tous les changements d'états peut mener à des pertes de performances.
+Ce problème est résolu par la méthode `select` des `Provider` qui permet d'agréger leur état pour ne conserver que les valeurs utiles.
+
+[_source : select.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/select.dart)
+```dart
+final intProvider = StateProvider((_) => 13); // <1>
+final moduloProvider = Provider<int>((ref) => ref.watch(intProvider.select((state) => state % 10)));
+
+void main() {
+  test('doit écouter le modulo 10', () {
+    // given:
+    final container = createContainer();
+
+    // and:
+    var called = 0;
+    container.listen(moduloProvider, (_, __) => called++); // <2>
 
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
+    // expect:
+    expect(container.read(moduloProvider), equals(3)); // <3>
+    expect(called, equals(0));
 
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
+    // when:
+    container.read(intProvider.notifier).state = 42; // <4>
 
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
+    // then:
+    expect(container.read(moduloProvider), equals(2)); // <5>
+    expect(called, equals(1));
 
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
+    // when:
+    container.read(intProvider.notifier).state = 22; // <6>
+
+    // expect:
+    expect(container.read(moduloProvider), equals(2)); // <7>
+    expect(called, equals(1));
+  });
+}
+```
+
+![Provider](docs/images/select.png)
+
+Le `StateProvider` `intProvider` est initialisé avec la valeur 13 et le `moduloProvider` vient l'écouter en sélectionnant uniquement le modulo de 10 de l'état `<1>`.
+Un compteur écoute le nombre de changements réalisés par `moduloProvider` `<2>`.
+Initialement le modulo de 13 vaut 3 et aucun changement n'est encore réalisé `<3>`.
+Suite à la modification de la valeur de `intProvider` par 42 `<4>`, le modulo vaut 2 et un changement est ajouté au compteur `<5>`.
+La valeur du `intProvider` est à nouveau modifiée avec la valeur 22 `<6>` mais étant donné que son modulo est identique à celui de 42 aucune modification n'est apportée à l'état du `moduloProvider` `<7>`.
+
+A noter que cet exemple n'a vocation qu'à présenter la théorie : l'utilisation du `select` dans l'initialisation d'un provider ne présente aucun intérêt étant donné que le comportement est identique à celui d'un `Provider` effectuant lui même l'opération :
+
+```dart
+final moduloProvider = Provider<int>((ref) => ref.watch(intProvider) % 10);
+```
+
+Le `select` prendra tout son intérêt lors de l'intégration avec Flutter afin de ne conserver que les données utiles à surveiller pour économiser les rebuild et gagner en performances.
+
+## Accéder au notifier avec le StateNotifierProvider
+
+Jusqu'à présent les `Provider`s ne donnaient accès qu'à un état, qu'il soit non modifiable avec un `Provider` ou modifiable avec un `StateProvider` par l'intermédiaire de son `notifier`.
+Le `StateNotifierProvider` donne accès à ce `notifier` pour permettre au développeur de l'enrichir avec de nouvelles méthodes :
+
+```dart
+final incrementProvider = StateNotifierProvider<IncrementNotifier, int>(
+      (ref) => IncrementNotifier(ref.watch(intProvider)),
+);
+
+class IncrementNotifier extends StateNotifier<int> {
+  IncrementNotifier(int value) : super(value);
+
+  void increment() {
+    state++;
+  }
+}
+```
+
+Le `StateNotifierProvider` est formé de deux composants : le __contenant__ avec la classe `StateNotifier` et le __contenu__ avec sa variable `state`.
+Chacun dispose de son propre cycle de vie, celui de l'état étant dépendant de celui du notifier :
+
+[_source : state_notifier_provider.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/state_notifier_provider.dart)
+```dart
+final intProvider = StateProvider<int>((ref) => 13); // <1>
+
+final incrementProvider = StateNotifierProvider<IncrementNotifier, int>(
+      (ref) => IncrementNotifier(ref.watch(intProvider)),
+);
+
+class IncrementNotifier extends StateNotifier<int> {
+  IncrementNotifier(int value) : super(value);
+
+  void increment() { // <2>
+    state++;
+  }
+}
+
+void main() {
+  test('doit incrémenter un état', () {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(incrementProvider), equals(13)); // <3>
+
+    // when:
+    container.read(incrementProvider.notifier).increment(); // <4>
+
+    // then:
+    expect(container.read(incrementProvider), equals(14)); // <5>
+
+    // when:
+    container.read(intProvider.notifier).state = 42; // <6>
+
+    // then:
+    expect(container.read(incrementProvider), equals(42)); // <7>
+  });
+}
+```
+
+![Provider](docs/images/state_notifier_provider.png)
+
+Le `StateProvider` `intProvider` est initialisé avec la valeur 13 `<1>`. 
+Il est écouté par le `StateNotifierProvider` `incrementProvider` dont le notifier `IncrementNotifier` dispose d'une méthode pour incrémenté son état `<2>`.
+L'état récupéré par le `incrementProvider` est bien celui du `intProvider` `<3>`.
+L'appel à la méthode `increment` `<4>` fait passer la valeur du `incrementProvider` de 13 à 14 `<5>`.
+Après avoir modifier l'état du `intProvider`, le `incrementProvider` est initialisé à nouveau et prend la valeur 42.
+
+> Le contrat du `StateNotifierProvider` va de la récupération à la modification de l'état.  
+> La classe `StateNotifier` est à étendre en indiquant le type du state en générique et de passer sa valeur initiale au constructeur parent.  
+> L'état est stocké dans la variable `state` avec une visibilité limitée en `protected` pour conserver une implémentation étanche.  
+> Les modifications sont réalisées en affectant un nouvel objet à la variable `state`.  
+> Chacune de ces modifications est ensuite notifiée aux objets qui le surveillent.
+
+## ChangeNotifierProvider, le vilain petit canard
+
+Jusqu'à présent les états se devaient d'être immutables mais pour des questions de performances ou de conception il est parfois nécessaire d'abandonner cette bonne pratique.
+Le `ChangeNotifierProvider` répond à ce cas de figure en laissant à la charge du développeur de notifier les changements apportés à l'état :
+
+```dart
+final incrementProvider = ChangeNotifierProvider<IncrementNotifier>(
+      (ref) => IncrementNotifier(13),
+);
+
+class IncrementNotifier extends ChangeNotifier {
+  IncrementNotifier(this.value);
+
+  int value;
+
+  void increment() {
+    value++; // <1> Modification de l'état
+    notifyListeners(); // <2> Notification de l'état
+  }
+}
+```
+
+Comme le `StateNotifierProvider`, il se compose d'un __contenant__ avec le `ChangeNotifier` mais cette fois-ci qui déclare lui-même son propre __contenu__.
+
+> Le contrat du `ChangeNotifierProvider` va de la récupération à la modification de l'état.  
+> La classe `ChangeNotifier` est à étendre et les changements internes sont à notifier manuellement en appelant la méthode `notifyListeners`.  
+> Les notifications sont ensuite transmises aux objets qui les surveillent.
+
+[_source : change_notifier_provider.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/change_notifier_provider.dart)
+```dart
+final incrementProvider = ChangeNotifierProvider<IncrementNotifier>(
+      (ref) => IncrementNotifier(13), // <1>
+);
+
+class IncrementNotifier extends ChangeNotifier {
+  IncrementNotifier(this.number);
+
+  int number;
+
+  void increment() {
+    number++;
+    notifyListeners();
+  }
+}
+
+void main() {
+  test('doit incrémenter un état', () {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(incrementProvider.notifier).number, equals(13)); // <2>
+
+    // when:
+    container.read(incrementProvider).increment(); // <3>
+
+    // then:
+    expect(container.read(incrementProvider).number, equals(14)); // <4>
+  });
+}
+```
+
+![Provider](docs/images/change_notifier_provider.png)
+
+La classe `IncrementNotifier`, un `ChangeNotifier` avec une méthode `increment` pour incrémenter sa propriété `number`, est initialisé avec la valeur 13 `<1>`.
+La propriété `number` de son `notifier` dispose bien de la valeur 13.
+Après avoir été incrémentée `<3>`, la valeur prend la valeur 14 `<4>`.
+
+Aucune séparation n'étant faite entre le __contenant__ et le __contenu__ du `ChangeNotifier`, il est retourné par le `ProviderContainer` aussi bien en tant qu'état `<2>`, qu'en tant que provider additionnel `notifier` `<4>`.
+
+A noter que la classe `ChangeNotifier` est initialement proposée par Flutter pour fournir un mécanisme d'écoute et de notification.
+Le `ChangeNotifierProvider` est donc parfois utile pour migrer d'anciennes applications utilisant le `ChangeNotifier` comme state management.
+
+## Détour sur le Future avec le FutureProvider
+
+La classe `Future` et Riverpod partagent un même monde que tout oppose.
+Le `Future` est mutable et utilise des callbacks alors que Riverpod prône l'immutabilité et utilise des états.
+Le `FutureProvider` est là pour les réconcilier autour de l'`AsyncValue`.
+
+L'interface[^1] `AsyncValue` proposée par Riverpod reflète par ses différentes implémentations les états que prend un `Future`.
+L'état de chargement, de retour de la donnée et d'erreur sont respectivement représentés par les factories `AsyncValue.loading`, `AsyncValue.data` et `AsyncValue.error`.
+À cela vient s'ajouter la méthode statique `AsyncValue.guard` pour transformer un `Future` en `AsyncValue`.
+
+Pour faciliter son utilisation, `AsyncValue` dispose des mêmes méthodes que [les unions de Freezed](https://pub.dev/packages/freezed#union-types-and-sealed-classes) avec un équivalent au pattern matching avec [l'extension `AsyncValueX`](https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValueX.html).
+
+[_source : future_provider.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/future_provider.dart)
+```dart
+const duration = Duration(milliseconds: 100);
+
+final asyncIntProvider = FutureProvider<int>( // <1>
+      (ref) => Future.delayed(duration, () => 13),
+);
+
+void main() {
+  test('doit consommer un futur', () async {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(asyncIntProvider), equals(const AsyncValue<int>.loading())); // <2>
+
+    // when:
+    await Future.delayed(duration + const Duration(milliseconds: 50)); // <3>
+
+    // then:
+    expect(container.read(asyncIntProvider), equals(const AsyncValue.data(13))); // <4>
+  });
+}
+```
+
+![Provider](docs/images/future_provider.png)
+
+Un `FutureProvider` est instancié pour que son état prenne la valeur 13 après 100ms `<1>`.
+Tant que le `Futur` n'a pas été résolu, aucune valeur n'est attribuée à l'état et il conserve la valeur `AsyncLoading` `<2>`.
+Après avoir attendu 150ms `<3>`, l'état devient un `AsynData` avec pour valeur 13 `<4>`.
+
+## Le flux et le StreamProvider
+
+Le fonctionnement d'une `Stream` est similaire à celui d'un `Future`, à ceci près qu'elle peut retourner plusieurs valeurs durant son cycle de vie : 
+
+[_source : stream_provider.dart_](https://gitlab.ippon.fr/adbonnin/article_flutter_riverpod/-/blob/main/test/stream_provider.dart)
+```dart
+const duration = Duration(milliseconds: 100);
+
+final asyncIntProvider = StreamProvider<int>( // <1>
+  (ref) async* {
+    await Future.delayed(duration);
+    yield 13;
+
+    await Future.delayed(duration);
+    throw Error();
+  },
+);
+
+void main() {
+  test('doit consommer une stream', () async {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(asyncIntProvider), equals(const AsyncValue<int>.loading())); // <2>
+
+    // when:
+    await Future.delayed(duration + const Duration(milliseconds: 50)); // <3>
+
+    // then:
+    expect(container.read(asyncIntProvider), equals(const AsyncValue.data(13))); // <4>
+
+    // when:
+    await Future.delayed(duration); // <5>
+
+    // then:
+    expect(container.read(asyncIntProvider), isInstanceOf<AsyncError>()); // <6>
+  });
+}
+```
 
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
+![Provider](docs/images/stream_provider.png)
 
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
+Une `Stream` est déclarée pour retourner la valeur 13 suivie d'une erreur, le tout entrecoupé par un délai de 100 millisecondes `<1>`.
+Tant que la première valeur de la `Stream` n'est pas retournée, l'état conserve la valeur `AsyncLoading` `<2>`.
+Après avoir attendu 150ms `<3>`, l'état devient un `AsynData` avec pour valeur 13 `<4>`.
+Une exception est levée 100ms plus tard `<5>` et l'état retourne une `AsyncError` `<6>`.
 
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
+## Conclusion
 
-## License
-For open source projects, say how it is licensed.
+Ainsi se termine ce premier article sur Riverpod.
+Vous disposez maintenant des bases pour en comprendre les principaux mécanismes.
+Le prochain article sera plus court et portera sur son intégration avec Flutter.
 
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+[^1] Les interfaces n'existent pas à proprement parler en Dart, ce sont des classes abstraites avec des méthodes abstraites dont l'interface implicite est implementée.
\ No newline at end of file
diff --git a/docs/images/change_notifier_provider.png b/docs/images/change_notifier_provider.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa8186e1d772a983f830ec17b2f7587938bbc882
GIT binary patch
literal 2450
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
QV*mgE07*qoM6N<$g693j#Q*>R

literal 0
HcmV?d00001

diff --git a/docs/images/declaration_provider.png b/docs/images/declaration_provider.png
new file mode 100644
index 0000000000000000000000000000000000000000..9100e74f7499eab23195885bb14797f3e9789bc1
GIT binary patch
literal 2910
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

literal 0
HcmV?d00001

diff --git a/docs/images/future_provider.png b/docs/images/future_provider.png
new file mode 100644
index 0000000000000000000000000000000000000000..ee44d69a42723e4287a0bfde0ff9f0543621c0b9
GIT binary patch
literal 3192
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
WbJ-r|MDYIv04+6r)mjzn=>Gsc%=Wkd

literal 0
HcmV?d00001

diff --git a/docs/images/listener.png b/docs/images/listener.png
new file mode 100644
index 0000000000000000000000000000000000000000..0caf619a8af637099cb1717af965d00d8b3c07ea
GIT binary patch
literal 9733
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
Xhn150kW%2)C(vsdW$6-0qrm?G(~A4<

literal 0
HcmV?d00001

diff --git a/docs/images/provider.png b/docs/images/provider.png
new file mode 100644
index 0000000000000000000000000000000000000000..ea37a272df4647a9a3f37c9c3edda7dbaf0d4309
GIT binary patch
literal 1502
zcmV<41tI#0P)<h;3K|Lk000e1NJLTq00MFV001Hg0ssI2R9&MS00001b5ch_0Itp)
z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1#?M6K~#8N?cFg<
z+gKdO@hsOn#0^o8UP0ncZUar#Q%lH2w3qaB(Ln);szsHmgQ_~v4N*@pWCMvI3sRR#
zj&vt=WX#Bxm9b++h72AvX7tEC+t0>!oLrLHAuivK{=>_&{ruFSKb~Vdi3<P#5E5b)
zS3}b@RaGmMN_l^;xRetOp;oK4+ikfA0K^cnijfkj`TqDglb%<mZkDrim0U_Kq|aXF
zL_^fp!@J4J36ZiVCnxd%0EiW06(geSy87XRa(n7<DcyMe%f;WT-AbhF?m|kLxh)p2
z@?Zdn31Sta&}y}cxy5qsUi+`#eIeSdw70*C#p~9K=OT~gC;%|_h*d<c(P%umKmX~~
zV_$IgQ?>Q1$YZ{61rQg+Dx#%nT1J@_OHW^3_Fp28i`hpcD}Y!aRuLgx*E92}^X-B!
zDF?07jojwyvK$lu#u%}R2yMN1UR%%lGIF@86n?549>{S4U|bQah|1a7S#j<MUpfw5
zdTB+u^<AsglA{B_SRz&tl}viR_k%Qp*2%Ml-9KK-(E(ssnx>V@<%s@FCsq-OaF?o1
zB_}7icL~7fQ&m+_lqkZLSnaJ|N+c5bs{Dk$mG}45LfRLKPBjZj<KMOLw6oJRTX*%l
zYj+{V3zz_e!gxnb1mPNftcoDYSDW%fAFBCMT{a!fYF_^TYRZ=e8E_zd%(h-4RtKld
z-0t#FO;-G+k}qVd)4o{EmZsB9vt56XNIVF>(dl$Ov$^(6o&x|rTj=3>^{dsOSQSLx
zIsMJ5NVceg^?wno18cY2$;k;{B6{m-N333!v23*?)>k5`MPSR*06<7W4A)R&)x2VA
ztS;RdXm#dGC2Ild4pDo5hwDIhtQxCVYm*$T+7wymKf+93v-5I!d%5f_)!l7lTi|Rh
z&hW-`U9sv~MY~tfJ!f?tou8lIpSk5rL~r$oRckpLoVa>v<(nH{$<qKps6q+X@K>u&
z1YR;?Cts}_ot<0zTCKVdGDA!p<UNOXA0y!GPoim$)Oq%R*;)t4J~5CncdK1>UyK%8
z+!GI;RSbxgrS+q1A==s5c``eAtZvMjf0T5+0&LZ{U*9-5IFM%ofUq4M9j&dcC6meJ
z<ze1JGpbm163E>)b&pktH?3TWc}bkrJB({DXIsZkU1sa)G53%SIUk2>w7^-#!5GD=
zUFN+@UGG{VRslrn3y#&m{w@L5k(lni<dj5L$B==u_4Jr~cp8CX8>><Hyo<oA7jM$}
zM6&v(qo0ew0HPGDi@;8v1;nb{y;(PwgZ9P&W7T(B|0J3sQA-{35&~vxoFMQburbxH
zS9yt5mtk&M5>aYj`|Yt><(4G?Q5er#mPA6yYoj(Z7mZx9KiO?h0*OG9y(R1WSMN~n
zeVwr+Z}0}(vg9yE^z7Ybwif5!BjfsW)i~+p*Q@qnB4TC6_MX)>)%yV(H#=O7ptS`r
zRYmcD4S;Zcfe+Z=i`4sd5JMNQg8&eg&;!*%tRfP;yb!=Riy}}h#44h~tM&nmCtkIW
zSmKA_09-@FDk8+sm;<<Oh*d;L*Y(VN>U_K4%g90NbR)O9I?Qv~05D?2Dx#%nT1J`e
z@~}-`_372)V)hYtDglTEVil2VG#Z(?J7+I*zP#+GKCUblbBkmufVd!5F$y9Vi%Yq8
zKi_NZ{N@YJZgsw0*nDv3-JfrDU6-Q(z}O>JF*+ar{YRPp{%|SX`@tKruH9Wo-B*4*
zJw2620zga<s~9P<^n8DOoJr3sQ#Z@mxk@gj7SbX|MMKoq!@J4J39+u_RZ0M2ide<f
z&@@d|)k>vO-ro~BYW*Hltybe}Q~<F?tO5WKl8cM~02kZ7a-!XsRR91007*qoM6N<$
Ef=5fx4gdfE

literal 0
HcmV?d00001

diff --git a/docs/images/schemas.vsdx b/docs/images/schemas.vsdx
new file mode 100644
index 0000000000000000000000000000000000000000..a799ef7d8bc7a7119c6cdfb33219d806720b966f
GIT binary patch
literal 33089
zcmeFYW0NS|vNhVPZQHhO+qP}n=4#vSUTxd9ZQFMDUHdur?l?EjKEL37D=KDH#GGGp
z)Eqf7XJ*Mu0RbZefC7L6000mG1o!y&`2PX`pa2B`Kmq^<)Dp6@bvChe)>HPdH*wOT
zb+@s`F8~H4&jSGbyZ--P{|6(`m^5ZPfR8NllKc*bx-m{FQCwcWZ#-wPW*YAc5O@N-
z1P#de<%1V<!Ky&2Y+^JK>NRup%Z^>Qf>SH-Vg5AXF$#B#--)OwC;8CnJ%uk}608KI
zB04m|x;OFe^x2esOZ^t3yoqj%8~8)G?8Hfa5^XqeIDVXc2++43qv^L_9mcFc%?p3q
z+%M-*p-23}Jt$>sg|JJP=$;k{eidS`?dhmL3F0VE7u+$kL6~4oJ4g&xR%05yOLN7j
zUSs#HDgooBF}YF~H}vlag4k;^PiV8}ytP@fkfWrNA#Nq~Tso54s3@Uy$h4j8S4csW
zxojDN?9KgC<j9HdWNQw9s(ibq^Uv9UmLz4reL0Cx7+QJvzcIn1z?KijM$968h}iWC
z^<Ii-!t0SAiuUzPV@@>ER1{@2$I5oy+eoL?W4DI-K%Ta36m_V=&o>JYTJiI(K*OrF
z#Rf^^V3}1&IGkyI8Qvh_E3SF(1u6Ut+u6YOS}dH3?fl(yKR-YK^8Y{Lma6wRk^CjD
zE)W0!)L-K2Iht5I(b4{E{J(_#KbWQe&GgE|3E2U9c##+J&!8ipgCg!=`9xcIMK|ye
zIP^;iLj*o!x$@_mI!F*g5|cSv9^4%-)9Oym8|j&5^a9uBLy;7xP*u)i8?F16caOVX
z4dPlzPqVs1uixG}I(k*QbzqXQhNV8n5<|lYZo=OR;pAM*@JuIjC7Ht@KuIzAD7c4U
zjpQjFzI6S1FuKvqYof{A){8J^PupZCPK_Y~fFQtv?fy`Bh#L6WrUop*sAY%aW2wq9
zVKLMSP7E-Ooz(CyK^`r69}r2o9ndmNBTAFE=rlo5)%nt#M9rKeS<SkK2UStbFZ#3d
zHD78Mb-tD;ZcNKaZ5<_Zh`HTUr2A-&;fIraYuKK$1Y5d@5N^S$`J6By+jtR!r)tW%
z|B+Slo;KyHXaE3SSpWcte^<C#I9b@y8QU4T*!&g6f2rZ6&W$4$Tf&d8$_p&@2~c9Y
z`aYgQl43#&`FFfaym1xz#Iz4qJdwk)5Ud@0QZe`3w$tHIHCEa$%%I`PS&2*YRM#yV
z>CahnfN0UeIUe@+jRo#Xp7%S-aMY2fW7$I82ed?UOlsVii;KhWCxNc#7Pg-^Jh7Hv
z{GfBjJTGVxFqSl`BTpI$)PCz*Ix?o;n_5?sK@!+i1s}#{h*NHbA0PVrH@t>JTdIX?
z>)Bp?1+*96$XZQ>#XSJ+FACf*2}`obd_~0dssZ9ger(S|biZuX8uZ%`7kxEpx+46v
z^@hL4WAVP;GYc!nSLF~-$ID%jXlgpa0Irs-fPS{M!p5S1l71(i!24QP;LQYu*nP1>
z_RqfGIpat(o#HZkU{-%swy}kYbqx2!4nx_MNxpD_d2JQy9IyqT^OAf6mKr{lbdmi!
zB>QzngDdFQ1f}Dx>rTI0>OhY}+u1Rj+iih+#n^+ZGo-LDXRN1Yhbh)^3|H=bOyg6i
z{T-LA=UxE}90EV;I|fgdi;JoRDeec*tk|a@WWN~O4<0`DgF~C}+UELv*Sa0}*2c@j
zQSG;av$w<fsLT&hlqev&4+plk68AOG<pVpv6!#4Q_iHV+voWm8^e4E`F--tgGsrg6
z>JN;Rq$0=WP4v5V?D`b6BjY(-m`q8&D^<{MQJB{O0scr6p)T0(BYaC|tZ3W98AXM{
zsHt0+vN;>(^{YhIl{n0;;a~quYfTM&AxMct+cs=iKo0?5ng%ijviO}%2^hxK|5Wbv
z?BK0@=%Q~9{r%9s2>!GWHC;nNaOdYw+usE6mdqVZD^sE^TQ)7vu$)Z;n9>S<$3ytp
z{^yM^aP3oFs#t#A_o#D_%)%ARmy4WDDX$%BRs%o%>M?>hzbqTI3~L{(ANPBqY)IpL
zPj+|n(i!KhSu?J<kpg*k^S8>nwr}`Rf|5;bdYV%igQ%11=VO;0c1N0|ew;``*SDh4
z&-?wcA${$`pP_x9?XTS$>H^jfGn}~}3~JB#EX{%rIDflyhNUXV09-URsQ7hB7I<g^
zSb_XpwDLZqR;)v!v_=HjKrcG%zzwV)Ip~~=DqrM1*$=c1IWAIy0;qTs0JuSc?b28S
z7m{Dor0zB3(XFUR;$?pCg26Qir(7{-M0Xu`e=iho;%kbt%bku(@q-PJX50+9H0th_
z*r;LAmUOYl@Wuw|8RX^ZMm4+b^8rICTvuD9g41>t>*S5yO_g154bpqLe)%%%8Oqt_
zq$X$>kvk;l=lQIf2&)+3*2!P8v8ph%RmWq^l;lz<6pBz68QCHux?*oBaC3}HY{&vz
zkAokG8UNN|M(ye7(R&*c>$u?<q!S76pnu(IqNDx|xaz(i!drPl90@X>2C}p%(V8+e
z99On~M-3<gJa3Mi`|&asf)*scZnDxEtR14i-4-cZ0Gt$7@wgnD2*9}r6$ECgu~+T}
zu3{?U>2^RYnc6RVQag9XoC%h<pmp_?Qnwhlj33^I2m~~9oohKx0C6&RxDckFF?m_8
z{+RLyy}h^YlF;)JV{SAOJ-xSJ+Y+Mlz6$1ZRxS-C@+la0w<3UcAF8@=&}r@>X#3b|
zi&SpFVpktwWI+d#3Rf0`du@STlS>nL!tj2NM$ZP?Xdjbqv(Nrz*D7Y;oEDR2{~UB$
z-O)<_ku;_Q0Q%-8Ac9bsPm>w|aF}i8H$GD&YKZVOs?twHjo%fW5dG9^zZRS|q3$<b
zp_|)}`vpyjeP<n&o@i7ByZWv{4Amu>fq+U~s~lnI-pd5VXm$s7iz_m5hYY)Uw6~w<
zi45;ri2L&a$}<?<r!&gOi!bzuwP~FG-R5B5Ku|8l%=&37Y%{#%Fz4gxc<7Ka3MA1)
z1JDOe&WQ*d`BlRbK#>cyD$&THi8C(u`<X&wI(3|AZLi-G-REn-*?|eL6hXULDI#iG
zPnxI3O+>aUtmx+&zr82kUtgo-I@EmM@bk$LS(<>S8L8vZ(tFUe+71k1v;Y{7=N^$d
zUFbFVODwoA8jH6-4feX<7oGf!T)+=oM|eRIl-Qt3Ofp>8Kb5F=I~CFQB1~#kC@6w|
zzVAY8!VG?N(X*K3WV-=R@AaWWS=C}~<ih221jyNxc%C#Ah&p{zPjcMk%Wan4403e6
zdOr3PF<)k4!OSw|M^T;fJf<&u5VG{o1vQF`j&wwMb-$7wZ|b~RIEnc=F=%O2Ru_&(
zo?M3m;j!^v&HRyBO221#UuKFd7lzw5Wl&#aAWDN`2u1y)ycz=;R@+{{=6D9^O^`mw
zMsEWpv#FkCDi;0YXQPuc>$K`AI#Ds`UV}<=T?af6I%}i*djozJ;KegJxQoNlh!ftI
z>2DKbK=hAMVC_4s?WVJ`Nvw{*VRHBT(l2Xp7$fy&1kqfm$i3B6ak8R{)KN{(xM?PU
zdzD%4q<xF4o|OlZ8Gv7{Sfk?pdlSEz2z~*aup8lt8pM?`5YABiVt^L1%|s1i*A2}|
z{xgA?1mp#*M8Nxk=W}~-7<RL+*M}Rn5vfDk1^dVHvfg?pm?%?cRc^nwg;R@qWpezK
zN3;(ai#x*S2C3l=QovbSH2zPxCy?J54sCT7V%k&{BAijaX)&y8Efz*FS`2nzJZz;O
zl1~4*go#zDgM01Dxj)}s-hw$^^9x*=;oNPKD#Ir+a3SUvI);NtGt~A%P_@0ai7qk7
zv^ZLTmp)#v$uEK}&dbJj93RgjYb$5b4saT-h#x+qj{Y=!ZX#Vsv1t6*9}TGWRFEH&
zwgzTis{-_$%x@rBDR<KPR4_)$2|L0L<#2`?^4bC|sk}cHHofqo22*dOu0;!MNC3nR
z*=`E-X<4O#0;{l5*y8GSj+fCWWWK3J)}JwzRp_0s*?lj4o=+MsG?4QeMTC@9R;HV`
zqr+AL4jDqQz2t!P@A7FzK4&9*U}YP<>QidFZ;@jVLvbBFHGTJ}8DM0(T*{eeA)=Nd
z7U-Vml)e1oC9MPlTgj(YnYic#DEgyYA*q9XN)R0SHITGC-)aH-2tAN7eC+4(RP6Ic
zH?l@{kx0;-+bM96v$iA0vZWiRxYsROA6o-FM)M9CkQ9piB#}uWkGM<6iHa_PMQ~IH
z*FtfV!IJSvQfd<)D8C_F(-{A3Z9`~c=TW@Y%}C;G_g1bRAs`<qjv>#c`WKKA7u2E{
zD8`xaiwV>c0GyZi())>Uthzz46EH)N75RgUx5cVCfOPSQO~IITL&DRjfYKR0_tkyW
z6@8{A-2YHeoNxpW{Jvcd)EZ79r8h!?>y$~)jCbn)zWk8lR_}`@HFDSecsbn&zUIhU
z!0ep~q7^f>Ms?xv6XZb={WIUL9HSf`#6o?Njww_$ZV%~ISooTlD{X3If4}w_Ba*I!
zGP=KwU0rW%7TwT6Eiz=Z4Taefs87@<bLH@L9_p&9b~VG@x|ou6#SEjR95@(b>Q7Ek
zS1^p;a=_)?=14^m+<Tr>eJF@*%Nzo*=iq9e?~2xEk0E~f3Ofsrtzv*)X?l9=tgpDk
z;<*rVO{y#4cuGvZ4}18FHr7`XsLRS+z|@Q*>n_plW}2X}_vl$9Got*+uqZ!gOa240
z4wAJ2cuU<_l}iUpuz-#h(vvN`YbGzO=MQXFX}GI1lgP#&<vnW#sOv!BEyXfQd!!NE
z!q2o7e3TOuD%j=Ss<hLMT1b_hTm$EA9RS^!6Gc#`)UoRv@buc6;3%r_2%^*h`XgBh
z*%N_x1xPYOYBs#zCjeb&16xe5Sv%M|V!0x->Oq5EdCdq$0BjJ1;Ng{0VHkV2@{c^z
zB%UZHR>|-wl(5yb!V|*~qBk^L%%?<t@}V1nFZQLVVYO66O5&$ILEtjo#?WS9t5K$R
z>(wmb%e%{p;pSiT4FV_d1QEkPUM6IqlXW%EssSzXg*K~#=tBtaat9LF)QOPrETT2h
z`3JV%t_ITtS>!0-;dt{24j_Sd@Lm(?LX7+;v4Lp~ym`QKFVDOD0qhGKBnKqdJNqvy
zQ#b=1Ji0lrFYv|vD(72;hnm%cEef9%t-;75!}wX1Laml96{9-_jv@$K>n0F<m+g}b
z$F2DR;ZVFfGEslnrWE>;Hr?H*j9B?g*4hU8lt&1B4a|nnZSv9T8B|dR3EGjtvSB8n
z&H}$@o$}`BaAZX-{8U)oLNJjWTvXL{Fqy!HL8k4lwg7>av>ja7<i=!ej%o%t!f`+h
zcC}&5BpZnfN*wqfd*WK(Q*jY3NR33q0J4Gcs|5YyWLl?^AXn4gq6Ke*yEss=kS;Hs
z&+ubC=OZ1MsVAd>a|>f8oCbDj`S6OuK1^eHaCB0uc|j8u9EpU<l%$5I;%%iYB31X|
z&YF%%Ekj<~^2uTJxhvFr42xpsD2G)tnH#;!o1#Z8nI-(ne<Sycl$QHL^epYZr3t>|
zMHt1^0!#QCn6z@^D+iohO+vcdmzai7j<?DGk|IP-H%aM%$<rKc2h9uN_~D~TfNeUD
zj|`m(8CR$QEc~NcM7SVP1mUY?Vc3QeX{ia7%lzSV4uvAu%wu}3{~|2k_#Dx8*J@a<
zRC-FeNWr#vr4*fO$r22u5FK~M@7glpnP~HxiZ%XQKMj-#E-#cRXcTLfdXT<I1DNFx
zhQEb6Rz-iUZF+gVHpZ$fFhe60gC$mo5G)Y#FpQZyle(pU;(q6zrZGalZZ#s!Ew<1V
zH$9S4(BIraOKurnKq#ukRNwU^N;2U3JJj2ThOfQltC*V4C@1`mxS3tc#dM*I<#}>J
zahmlLjrI8(1P09rM&2rXof7m^jzzAXrQ`rozU{n$9aJ-}YC6;ijnzNsQZoR1=&`S+
z^Ow&dFPe^mH16sSupOA^=pBd8P6w{{&TGKAE>DShh4sATG(~R135$-46dCiM+fY;t
zJR3uAeoWXwRmG3M47}8|l0>SR4Lp0f$i!UEoLxP0$uhsM<W2cEaK=#-rXPB??qvhV
zQpu`KiUviTk5`Q{%v6Dq@ODn(_L=yd_bg4~dM`=iAve5DM5q_vo-d^AALXJ4LMSNz
zaO%&faBD2=iFD7u>1|qi=Z+F!+_KtTR$xrPl0_(Sn&*4RHP3hPxmc}Sy`2{05+6+8
zM#h+PWwWW{mW^Lwnw?KU7Df^Eo!te}R>l~7-mS{yVduwMmlBiK9VjV?5|^2#{FR9o
zeB&Z3@^7mLG<|&I4aUUsNoZwWleN~`&fk?uCMm(;kh5hI(4jI0ruJ>hrl1C;%uIRO
zmP|qVN?912E8;Gu86h;PrQA$M(&tP;hDv!D*uu^pZ_4JNrp3ZcY1)>|LA8LU=)YGf
zCzUNghf1ZH_yuTFB`v_)A>w!o0R3f)nfbLTi=#5VWecHFrL3L$dihzDsCD?M-eP_U
z4qAge&sPp>pOeVJR#^c@0@Zw2i-;#!cNiuBBs~nZ>mCjsD>7{X57EP_M3Mp_16!1v
z5`>mmGxC$VB<78psP|ZM=Woz1*%B~8Yx!nC&Wl2opQ8))>o(7vstc9C`*3hdr*tVP
zs>w!Ym!ww>2V*hH9QMOv1VhqlcZkg3gpR^D4j^E}q2DG<m@;wK>4UDS4@J7O^nPaR
z#`A6;*28*ho|$CtPCU}cyRA)Kw)jm;dBe4)HFZFLi+IB^$352^d*LfNKDfFwbY5lq
z*|l=WhzXOhcXd)#?cOWjZW=qNOA4=^Qs9^z>GENaun9hH#+>eBEXbfXl!PkLHO>=I
zI_lkS=8<y>R&Am<XGu|r^|Od($rYnORs(s2LfV47K^9G5LDMWda`C;Ui0iSMOI!~;
zCOV;jM!!LCl{8g;2&t9UH$xf@OO~UyN=RBdA{djXNCJ*UW|MN;XlH6vB4Kq6stI9?
zmoxlQKT2jbkE$21L6#~GWNcFyii|<)PBl?b_O7B{Qr${Sex|L+XKJNXBPi2eIv02Q
zy&v7Q55kA|M}txbQFc?F>Lr!oQ262vTOSQJhTfFczDYs6qHL;KlZMKyRg;A(*1f?J
ztCB98p(DU@CAl0fth!LqO^U1dLU|>XGFEMsqgaOg6?BVDF+fVIb|Gk@;!r6RLnCFa
z4a<U6BcupP%^28)UV>pe4>c>Y;c`E+N#4dzQ3?!>Tu4c}^CJp#mH#b_t=F>-kV~92
z+ZJ_Z2IZ8%tt}2B2fJ1c#0<=~)yrDUs#XTg%6-d-Nit|@(~$puZ2k4iVW6R}G*pXz
zw$t0QH6*DUnh9eI5JDh$^x$!zP_^wm=TsOa?=~F;0EgnB{AlM)bl(P&Ch>?W_JN+`
zk1vZ@;++H8D<*Ow;7@dyT+AAUlc36<64%F_n<zp@nX0>JMqH^7Hm>kL-qSLy_m72w
z>MP>A&drotHX8lxKKQ-+g#}*%>hMy}RP{TA1cG#NKA3xtfNLWozKn2RP{utE^=mtR
zYnN_%tRG->nO_5`W8ZimW56f4XUdj(4ptl2lglahC}MscOnwt{JJ*vtj*l%|4=vbV
zXgy45B(eTT#L+wQv<z<0BOKl|E7U%1YedONIp}0_c$meV3f_%4!<`B)T+`_=(~Jk%
zJsoI%!7bA4UdDvE_aB{{0b!xIhMyU6x2&D-dp#MVj}tAjIpe=a=%S)cv*$L@&)!)@
zH9s_Ut$#Om2Y6_x=`ec;z5L?oe=LX+V_4DVrq4~7EF48a^8zWk_MOM)0Cdy~Jf2UV
z8Y3t*20a1Co+Qvg2;xvj-7V#QEwX9)g+8u3a;xH`YDS|h6ctcA0O6!<g<Jkm6`IDg
zaEwU~Nh+OxSO9pPQ{jV_0$shN{~X}JCK|TZ!EiWG_gm*d4Ag~fiqHA#6KkhVV`1DU
zx>^7I)Fdn6YR9ia)me~zmJFB<n&4oN``0Y`&BM<3O<$OnJyJX$QD;^xVHyKvG>*5@
z_GcnV_JN_|`)%^_MUAiZW#e<TEi_7*a`ddnPI2to25}L~*TK`-*h@6t-VGdTGN0NC
zmW8ZlX~_qAAs@8P^!k)uWe<$*BEYvmyQdfbY{?~S1mmDxn_VKg5Cg2a;sP=F1crYb
zZH;g+4rp%|;(Gbg*=O2!Zdn6-DHE~#RH=CrF@xw6K~cszkp(Eocna__#|HIi@u<!D
zs1B#G%3~sCBBlO(uI263i8F(9>{kKn<U3-{a;7isSfGCxo#g(^oz3t1UZ=s?k?Zs;
zrXXbFX2K$}93{t_{&0-mpqukKPcbD!F*;HpEKdf0QdVT6_hYJ=wxz($?Y~D8WuBl*
zeYBh;6AHDr$LHzfU?g|_8W1zO>~FWt=uDvuIsPor8jYP*Ks*-%PVujAg@(${?@RTN
zFZ<5;(n4=_9%dVWX`Eb<8*&mQ1a&H-1y)6nvSPKIYm;4~Mt+*Q;^AO5Me!~0Y3a!e
z`MNHqI2#t{O@MU~v6D!%(oT~~v(%JGS_cy5pl9xw83OZUZRv*6lngn?ru36YThNfm
z#2)3Dzy3f&x;jpkcBG#hf-&XZ9?LW3cDjW4Py7jmGQsg&tN;-FZ6rgSJu(({e$&s(
zZSokA2R5K^^Yu1$>+0@dWO@^ci6@570<0^%seKDF8s(A({3;No*l*Y{?lT#P<AS7K
zo1Dh~<p7m({!D~+S=*~Q&~W<qZP^Qd2(N7vcJs|n!~x-L*@Oyg-0v-7j_7%bLhLlk
zR$OE4b-5+SF9j%ruPOhW=2`8lX=~wmgs4UL+&X#RChW^JgC5gBtPzG4=m*TREJE1s
zH4k%uSiR)XfCU#{8E!4ZNi6k>3*lI7MFFfACE<%7i>Y1jUKY5mSiOq_Q#g^umqv!0
zDg}fgK)(Ox_zS2je|P-h1?kUJUkK4-GW)tg&tgww>EjlsM)e+yfa7kFTZjF7N95N?
z?@yK_JjJC<>21n%>C-%IdkADT<0&R?s3RHaX|8)}@@&{`(x?{MT4v;Vwk)88MHv~-
zox`m3B1WWz+IM%T+;CYcH$+>bH+Mt|28Lo}w7~N98h@kxzs_z7&ty@Vy$|udSdltw
z9+nPF7PZJ`VI0Uw>h4-}TT^Jz)d1~BbLfii=#&#IoVE0r0Ye*adk6ep#VwM)UXOqN
z*3kZEb#2w_i>C?<0AL#%0086PbpRU!Cub8!C%XUl`A<1OeZ#Rr6xGKq`6qzQlrjPH
zSHf=6k+PI{Jn1q=q>0(|A&d|dgD5|MMqTM~&?m3oF5bjBs6nmqh^u8n21SeHqs_~D
zhnt@IWul#*2G~4|lp-JGhhs<aC?Zt$=#x+lon5`jLR_((Tx_hYscVLSbf9<I^N~u*
zP%gMcHj37Uh1C_8M3U%4<&m%zB28-dz-Tm_asj1Ha-|Jl(nDQtLf4$aq~I=Xp|Opb
z1(jqcV1H!RR7MB}e-f>Py1rR0lg^t1<t%S;-BoQjHW{_a7)+uZ6jyQCQmTr^yLHW)
ziC(j<5EiMVEOO}NSb=a#$(enk>0Eo9`ANOc+oW0kA>>;wTO}<R*w{RG#InP3D%~tt
zXNVsd(5x)b4ira*Ja+?=8B6Qb#pzK&YMqtYX=p5+5Z>wLs2@e7<Ww`GIi>j90Qy<y
zqRQ_*btO)qr=5D{+68UjO(mI-^Xy~sLe++?=0#GRGb4i7U&pONGNUtB&=)8%KpO*&
z0!M>xI-xA040;n$8|cH%kF1@nEj-QY3@H+3zN4sXHgOvq&q2F&u6@2^<LhT*aUcPY
zZFCQPelJ_rBvO3OCRHYuJR)ZpJUSL`p6vvQ?We(S+ms(RYeVB(z8bVjDLyFuk`-I5
z>oy`w?d9dCJSsJWe3Cyx;%s`TXh^P?094SK$teXqO`7eO6Ac<FA<3&+Sy4k=VFrcv
zsZb#-3l*)!!HP@VFlyh)xio@Y(^pll_hnfk(@z{xKv|)J{+WEQ_eYm;MNJVvJAJy|
z0?&@`L@_tXc&}?$-LltL10|6zt&p$JCHXB6c1w`71m5h;4~5|%=4v32epwBT=Zt%C
zAhL9}0NoeUms%<M7dc{D7VpV77b7;qvDx$)xyO``^pLN6VWLe7Wv<kWTpPQJdzty`
zv{~|#1*V7GcLUx?D;X4*13Zmk18C+wP1>`V!*0KN+;codw3YMyh&)4&L{vGn4IR-U
za8!>4c5@bx6klPA?~8JCZn+@TxQd<m&%$NH9>J30E5kjLV(v<XzIRzPonu;S#c{Jy
zizP{SaW90B-|*n!QRxZsBJ07DolWOx(9*Epay5gkY5FrqpI^a?IRs#;fDwD>l9%ho
zB+caN2A#7!E1uqLUERzJoVom6SoUb7H>dA4t~p6@E7?i}@m4tYaD*mFiJddOOiu{F
z><4nI=Xx3h4r?vl14IqBXz{>P4fZTKUT&cMa+JoakVvW*GPb6eKW}FBR`G-H!&KJg
znIWfow>A7tVacok0!OXd1L9Vkwn9wtlThuA&2wDzfgEAks2lIRzmS^l+h-$COK%hY
z@Oj2L($zc~{u#}Cdkz1<P*q--PO;2P>$>G{O17GAI;q;^%z16Ux*XHmenPhQ23tvF
z6X-JA&)1Ub?qU(_&d*gi(M*9n&ydhDL~PbhC}!Pzijjzkoef;5U*xv5AZ36_%)@gj
z*h+RW^KX~zDcHd}2?ur|XM8Im_9qq&8b2U1gqE)^wdwH_97J(&oCc(LJM8e`NN?XS
zC*%nEGgoCR(K7u8rHe_tZ}W&fR}g~)(mYW_0(1rSHt~m2^FY&RLoV$Q#1}GytT<B`
z(wbt}tA=0*i!YfKV_T_{O26!IRpQ=0SMsNkjiB5C_>KkrYW@VUdC-E?sw<}}fL>({
zYg9YA3nO%!!5QgMN=dJ8yWLdnDzKfoV=I4YaZ0x%BC~mg8MK^*wWFQk`qWO}?%Xf4
z<EkN#WAmFZWCQPhbn5#jO*@*#o4##@z72Ctqw+G%XU4WgVgI;uh?X`Kl$YYpBb{ZR
z+_^sb_0h`Tc~58t7I3*YmGp=8P)#Ukhk$zHHUW=SJ+1x;MR<PWNhKINRcILVwN;yE
z^I&YuV*NJ39EcMZ@JS$Y`$?6H+x+F6d-LOX8LpMBqZ0d;Z?^-7qTPcSFL~eSz%I4d
z&=n#Jl-NwoOAoeH7edL9M=Vm=AsQQBq$>fL#@jx-Ld}sdoD=S64ENd(jKXnH%$;zJ
zZSogGLUn;msGJ#F#OEc3DsdauZ290bBA0Gm%A;)p@U$|4M7hBqqJ{H7ye1AM!K{lR
z_?6L9g(jvFnR4f+65dPa%#t;x3>+kF3w@3Dxw&udghktQokArDbc1R+ArU$8d8^RQ
zKw=cnA_u+i>x3LFzd6%}9@RpWSo9gNzGwanIfd4{8yH8Q!C~WWa6fzN%Hco4#*sC9
zslan|gH7=C8*n^R0v!oAU$a{`>!rqTI*a7#8ln93>PrJ%BZ8?c)q$gBb9MBBEg$x9
zjK@w-(UUX1Q|=9eb^=^S8qipLrz=o&X923+<5VGLng~{VXB3-#r$f4*L9v@dPVQ~e
z{y_c4c`(h_%S)SrQn*5t#gY92z>y3{yvuzLuYZJhufXO7ybJ<@a{0?*JUBHTahd~W
z0<-|2tAJM@1YuvO1vzS|F9~jQ9KEO7>zlAYmjz+2tAbvRKsIB1mpJCxxEY0oa|t}%
zTf;Z+#1!<j<wkWnYa`!{nR$32Bd%j<!4dk`cSN1$JD$lp5Q5GUHa%7zbh={FtdNO3
z!onqvLe?KR-iwz`)ey2I<@`YYlg7)1p_haG?VgE-002Pwx5nEWn3*`y{WJcf?<z8O
z`}FW#HH|+6Em;|GWCXuhlxhBSh&SpWtlPv|pocg0jd6mGf4}2!<+})LmGPuP{r=*{
zvopmlHIuvOUj?kO7&BaG!d?JKNroC%t!qP=NE~IJ4&CX={+3je#|k%fu|5`52HL1@
zz+Sensj?WJXxE0MqFQln)1HUEq!Gzwb<j4fQ5v5-zVgTGqNUP5b<?<ap|q?xtXLju
zon*Dr2h3*Y$rOr*oUEU=P@%F5YL{dRpNbTM@rF8<H?fc&u8VNdF12cnmPvn&*hq^u
z$#W4iNHw+yn9-Sd$Ou9J1@gtq%s~O^KH)&PyQ`?#;T<$JkFUjxllRpQYcxp2=0{f=
z$4BrwLsM!k61g*++LPec8dyL;4sqBjdd#F1jxjJ~suzdbdIpp;fzNP7dqX;<M1wqU
zSFMqIuRakGAl5ruCDyyNO_{(P>;mLUxe%o-xp)G1_>6n4Da1p5KlEumRIXcTckS#(
z<CH}Y={^9>j};1<)YCa580OyEQ}%hr_yT+I0g>r#`E*5al2xPHO*p{NuW3-wNW$J=
z2|60uPGeilyz0cCasAj2xr3NEBSL|YsyG-;2(d{2N*7Ss#cu}CcpI9UQl7c<$F?gE
zG%a(7^Wth7nDQtBLHRgHtFPLkgfl9_{8pds!D2V7*oPOy!i$i9)GJBnCp>Iz^USb`
zGL^rucEc}!@X!<pOl~wH*CCD@J4(`)Ti_v2naF17GjWW&6H0I-Q`w{dDG9UHBw`{I
z$kQVxM8Vp0>vVW@5>eDLVH1)~Pgy0n=Qs{SbT|}ay?uRb+rwGmi<CLYcPe)dovXTM
zq{I-SjMIsBSJ})em97}bN#@FGO=0_7qL0EPJZYeR^`|vVltZOVX<s^=x*5kh8^zMh
z=xkbUXhw)~kBRD#6hv(80^QgwXa-T8x#3bcd?B29=xLm)+g@x|a}h2prwO|@Ipr(x
z#&a#VvosTU{G&7U8cxgl<b77~dGY?6w!3Xv7*e=n`Md)6`)>^VKciy;!%MThzl`};
zPbkj6ne$&AX#c%e=AUk~qWC_`0ea}ro1m_sIUj}jKJ-FayC7Yu7l2_`q=i++6pNLX
z8gvY+;niX?A`HJ=U#qUS--(=W{Q(J~)fNJ8#$zGO=C7+gd|iS2mI+KqBsF{c7_46h
z1D+>jN=FE>1PzFihA>7|Z!qy!r-gf=^>U6*d5AK}_0Vb2JQ;ll<u-b?n~ZsS^h?VY
z<XSJ#mVRS~-$OBdsD_-2!-|7SaR7i<>m<lC1RR_V{L&cSOZ04*=}@<JN9VjIDc)@T
zHHwZ>4}2DGxys!g<EVa)(=b^xb}Y2d%X<GYbC{B@VV(Mm$;01S;(ufF|78yUh0%Y@
zAO3-;F<vIBpZ_mJ{oi2(FE;CtQQ-^Qz%@}HK%o}aU|e|8pc+42zcgy=-5*{N@44+p
ztACyv+T~5rf<jBm1r%x>><t!goi_b!`5hPx=$)X8_9UTwUh39;7OW$w?+y{E@`0D>
zlM|Z5t;y~~E7HhE50M+#L=15mFuYia4767C((o{5Y0}Q=%}2uwV9woT41Eo~%$yG0
zDU+jd>11Ky&6Vi#$yp#x4AQt2=j>-LBrSh^+ga}zSnFYAx}j(C5z*={ZT&U&e{Pi}
z$7YTGzcU_x;lTel9RKBH{|k(NyV`%CsEnI{?EhEKTHp`7sojDr0b$7&gsli4evuKT
zHLh{s=?)hyFcqziZYYP@@%4L0?jQ_P8#*$x3JhRSL=co}-&>=e4Zr-H!K@PWpWOgp
zpBC-Q81}P%Ddu+JK#Bn5-^E2$Nyeg&8Rcp8w}*%gD<MYBj22Eys3uw~R_S~gvr}pN
z^yH&q`5+FS#H@aGT}&K`ok`=ubapX#`sPYA`Iil&z}YmYAT^7s-iV_+&BuRlo5Y6x
zcC!Cyzf^!I<KthEaf1Bc6G?`D3C(ZS4aa>^cpo{HZ-F&#XzN;A|2myvElv1P%3fH-
z<=do$A%D^o=JI%bnQ!u)c)SU1VrIbfO58l=1)q26mowZgKaW^J6F7-T`%SRWav+dl
zUfn|X4X_StCl{b3Iyk-`XTFDd=!9P}Oz381f?NBIaDmiP*UU6eK<FN2m;|}I$aaiN
zBk`eZScgJ3vE;3$=b>dHxRC+3T|{Qv>;VizTl$C|8s&!dQv{)_J0i@~AagY=fI@VI
z&I<q}6k;7xNHbwRaqww^^<lev+Yp_=C2u{97|#M|<Z{3^3YQ_5$+(L=#ETJHfF_ZK
zr^aD%)sOy+f(Gi{ZMQsJ-8`Fc`skDoyzZiRh~zu!>z+IZ?Hkvw>o#4bJM{j%?w<pi
z&{)wmal_i*rXv?7N=%vj6#$jK@cKgls-*yQ=Gh<|feuFK?Q7kvGF=NyT!<j3?~=2B
zg7JhJVHmG{P5z*LsrCD*+~penqyg$&ZJ%#dD-?(Wvro+|77gnV@17U*;Cq7V4{g9Z
zzBi_g1YZbMAetf<2q#H2V`?ak@HQYB({9k{M}CJgb<zq-fuKly=i!&S5~h{mN-fXX
zab;_1;8!|gU;R`<VkDoT5yly|-)Lyi<j{?{(zqGKVpdi_z$zs}d|{M^H>r_u^mEjx
zbg9x7mZ_Y{cPe%?J};eQEM#g-gj&&UrH;+wux$b`x=yLSJmBAkpU)CzDJB6|RhX8(
z+e?SCHIu~}wE$S7D9iShpR;-;pNQrimFkZtO^6bNxK)!a(O1ar=|FxA>ZCL)3q`(@
z;>^c&t#0>1eWljT=614$8tm@_zj<F^N&YxnFToY)`4%Iote7nPl7)4aK6cuBEs+-z
zN=JFn!O&08??0(6Z*@<lv<gJ%LLcq<sl|}?Q^9bJse-pTsCoU$+Pa00#n<lf{LX~$
zxO_#w#>ZMb;gYNITe6$HL%t#qc{=qGwgYF)HxcK(%O)1qIPN+|Dl(*hf{mNhe}sg1
zIfih|n^_Np`R>#^WohW$Sqt1`b8c$I7rs%Q|1f}(P|;O9_t0itw(L;iohh&V(T`jK
z{6mFdC}HC+?kP1Nm#A=HF=xLSVl;Ib%G8`=v&<nf!>pCd%c5n}NUK?2tk!O(lBW{G
zduymV$yg|okpmHf=m1BfpAd@|a+IX7XdZKBm*eYMnCjvl%pQor0bo*Z%;uT?#$n3F
z4yRH^ohYg;WDt`L;Kx@gx?)6EruE|!_Sr&k*L*~Z6tn@x)EP#Lngt!P5N5fnfjBZr
zfIbG;|A`QuF^;FC7PQ7Yz@2-RfKM0k+{lf2L#tZ*2_;?O%fb8kA5~SyxxQ`=7yzIX
z;eU4(jQ?^Ki|RXeh3xRYd}rTawl@LIE0{pP1ectXNzdTQ#*Q<XWpY%h9k8q*L_?ih
zURH8pK&AIxmu%$LC!qRsFk;QR?Mz3<Ms6;|y@Z*Eh_6pc{o6zL<ER}zXXn_~z7(`p
zcyqqLH^%72nS{NFr{%MXF_oegV!Frc4_E2u?L(FYAeY&Th)G^Bz{l3{UtZXShlxyw
zVW2Wn>R|0F(b`9x#WIP>O<ivwbigzq!F}LK--$OX*Xkz0onayi@QtoZsIrRMnhuyD
z1Q+L!A{B-4;(a|CH9#OFhm<<b-JHc%O5`9C$6K7QFh|T2ctos+mYbZgMV>a0eP+sA
z77}qI32heVM%Z&M)_;$TxoyBU`fLQvT6s-A+lb-8x)c(0%0spw_q(~LS2<dMQ&!NJ
zbLvKs=#M;;^utw1tDi8=vTAih=}f$!?5_}4YKQe&zw5RYX1hIZ#sYz3zmbM`I$(gW
zx`Y_L<u5I@dG4(=`?C3l%R6waU3vs35@y+ZX=IVm?c^c<1ce%x)jqy}hUq<c-$9>{
zY1G?x@j;PK_a#EL)x(br=-{Y<<2SbWdYF$Wg{H*tc40$<HC#V!hiTpo5CE%Mac`ww
z!Qx^#e&`kG4rU=M?1J(}gB70X^LBM$Ua=rMddnt&M*&X^(<&rMO9T#V&O(>wj<7v|
zfU8~beLLsdc+}g+63aCLN<Nibg?wO^7P3;Gl|O;YfKL$tF;`9O=1`Sn=r1DL4<I2s
z?mJN43i3+~rnw4r-+?^8TGy{6P!TrNh-pb=q@1m#EH!J{PB?5qE^|AZ<=TpUbwOHx
z6L53i=*oeH4P1N6xu*bL$Ah4Tg>23}zjv>i5BhHV8G=|Y&uQRLtB=eeRmK^0$U~;k
zjwLv`+S|@2si7TN0)2GhcZah=2FFrpkb&T~CndN?EFj+{jM`vcmPB)_M{;wkshoeX
zd{GtL5^l82?<!mP|18YLNJKjM)3L3iAurdl0N=Mzz@r7jQ>Ps_lCLyYaD>fqG4|if
zY}j(yOgwQfxEKes_Sysv#p{bbZKAI7)jn%v+a7ktmKESOsEBY>L)rlKY{`hy%8%i<
zVJyE+Rh)#IpHxoS739G((h*0(cN;0Zr5`_8fr<>>s=c_xtIDIT0U9vYp6^rGX9o_|
z0AsQ+sr`ZxWUYoYv}JPWCxuYhk?x@RiDVtn^j1+#2HUJ>P+B#uiiK%yFbqst<%Jp-
zv}FyYC`<U+je`<iCNGqGmnFeK;H|!9O-9&j5O-j*?YtZ!V$_JY8-HRKSH5@~TrBMq
z>Y_O1AdWScgv>==OsXri%<Ruu)IQ45aAQ;j2SvbwXTk<mNDV!Tzk&Sp?o|Cf-AM+y
zn<}Bt(!@aH8Wjbw@XWna$31eFe~^~4|J`c~uq$nTe*<a}XUnGSCd+3f>#B(U5d_7t
z^5q?cL3M&E1l+1KAXAhORQ$VwM&Kp7aAQKHc`kg_{kcGmCPeM05C4HO7du3bPscM8
z{WkaJj4v|j!&f(bH)O5@fl@Rw`;kK9;>xoWKN9+&fGvGDJ$54_A1vDTQB||PBWlKc
zq7l1iD`5+nX-UsfW>-i#XuGRLp~03-^c!ci<m0{uy6_Ec@~r1ShD`ipeqXA8|4zFh
z_}~2?(?7X6=9c67@cZ{<mYe)SIyg3b0zWtj0ZuyJfdxJ<zjf>QA48Z7@G44(5!;i_
znKP%yq4%q5;0}D?YloZtDBvQyo}ZtiFS%GNJx_jx%e_1rt4K;Ia{Ie}s1zHI#7#A=
z`p1PZzn_b**$5<L7yGz8{0I_u)Il4MWa7OIO&V7<^Q*z}btfY1gAZYcyY|qV8y>VJ
zTyB2d1r?c^Djd4FwY8SHJ`3yufZOc@^DE-Iz(SMAf~c)2%uztZCXJ}L8{%Hu&ino}
zLockbn5cC}^n7#?IPvdW-+D(u<kBChm96H@q~bFraQVk%YM)0yW{DhccR<Exo}C?^
z%z_c)mn)~;uXX|8k(-RQ*2`1VuJz01D@&n<t8yJS+6tK_t0gbX8n?BUAE-mF(U)Qc
zt?s+Pbn2~U6BUNvsukOnT2Cn!Y0)X9Qn?%V_T($Tryh_tO_=TZ8dE9x{Rm88uhUw@
z70Bz>=`bop=1c9%wfDVXqgvGo$i89fDr0Ndk9c(x407)JdB3Gy`OjNS#z#TGN2B^<
z%;vER#bTI-X_>zpaj1QSJ(iFMlw~S3Rx(SLYRR{3Zz932KXgZvsCV&WmEO4@(TBF}
z%)`;3d&YY*c}dO2y`R=_-K2;y2`@*+(fvLOlHn8>-c?k}GG%m@Xd31gwKB>uI=}Dj
zDIR>RJKJFH;JC50&rBh8H&Za0XW+pg!=2m&_|^pWWwb}cKGZ>KX0f6}K~h*XHKnXh
zcTu#w7q+cAPj&QQN{Y`Q<8S}KUk-vye6Wkm%*pJ*3EJeYKqENhduwa4&AW8XHTEJY
zOR=zIo82-<;81`~Sh%fAWLQk2s-N-jzn^{YyLr!ijR%G!{x~mSA#82x#7g!D#GdkE
zaLy$hc4#4fmzy&MwE>_<NBuBB%$en8?BA}L(K;mTJ~hM!SoFAJ?{s0+?jEM&;qw8j
za;J8;Kkd^c=ImE5^L>KL7PEQG!oubj*l@1<CQ90B5V{Crnz{&|?~)H9Oua-Buf?gH
z^{GoLeF^R%$f-uzvB?64P^xg95R~iZN&%PD6@vR6RE`;n0bhbdIrV-w{2Iw4IarS1
z9;XwjM0zG2B>{;-2F~=2%opaL#)mUM-=!Re2f^}80LDlVL>5rdcE7eV!|C35jX@*O
zH4KeSG?>qPma1vi<(9|-?kW#wbWt7sibQ=xUL**2MChh59;!y;>UpJLe*^>I?N`Jr
zXRaN1U=*F+SK8%s*gOQ%1uG&0$RZ;sORpyEgpdn-{vzv?;G~){3q*0?bV(sHn;Jy}
zcWw(Hhj^dWl0OHDL&7HEBO>*HRfnmhR@bJhZAIefmYe!I`QaDQ_GMpKH6+=Oo^q}?
zv(C5lWqY2$>c}y>q+`=Jvph0645lLfdINhz)V+uH+Tmep#rS;LHLZH%L)(nz_Cj((
zl2S?xYFl}Q0P!1GJppyIyu9g?IjQxVUKNe6>zn?y=(D5c1u=2z_X(4DA$a1*om)y;
zN+ZVV?>losmo#H#fkfu)bOr!UYhRRU<U}#)2lCKR0vqOKgn2`JG8G42qkza`F}~^U
zv_%&|(ybVR5fv@QlI@$Hs3qfEsqsRAr5d@~wVsdxf%L=8<!iW!5@u>LJ;522+PGBy
zAa}B+M3C;1wL89JOhddvvL)<}LyA0w9>VKv7k@}Zk^jXkIeLO#BcKy3YGCFX^Cav5
z+)M$L2cx2W%uM6^UUMF)VE)52MR-e4<X>fw_raykVZ%au>RRFOYt92x&%L&s=wi_S
z%tvpfEkO9w1x>`5sJD1Y%goAbwE)7e|2*j7qjISX9W6T<i2`Z$O&NB_3Pt7!AoRI%
zu;lI9IgW;N98g+A+s(e)wfqV!|57_YrmA%`Z>Y|C!M4VcrP;2g^Z{|pLV0w*&P`>`
z`CiZChQv!vMlYe!-h(IG2Q&3_xKa?!_(*c8fc&)QC)GZwM;ju<;ogJNDAZe_Qs~6t
zlDZQHqrZW1tV4aS-VxjjH-!reB}G1D{S$i4)_xPi{Eb>(4bTaKp{naS(M{z`(ESh9
z17zlFORU*?g(R$cdg~8rxGjz@U80+E8rz8h52#k?Q$GU>zz}<c{uQ1S6U89IR?x23
z>c#9*8MA#L2=O7F#XCX*g9<z!nG@@{3jArHc+oYLadqO?NWd#_mU&Xjc$M5C&NdK-
z(xyjzD{~Fx{``}5HCHVROr?%p7Rh@)7RS!6V`$(?ec0ckZ=4>O+)>GuZ>#?=dpxg2
zlWdS;k>o1#c7=RO;~D-!n$fh)LZcjqNHTSVt$MFA?~>r-gRI^O8tygqW<bKCu#TL*
z`V=De-Z{JG8yXF*eh&-U=1R5%xZB_KZ3YE60rD1NX`4cGCxR*1dt;UznTNk2XMj!~
z8LflQ1QD}L()W3_HxL|3?Lk8DW|5g<-n^4o`8arhF2q%e$rVb_pajD}2E%JH7<ilI
zkf>}6f(Jq4X_{U6S7SDR)Aq!Sadw^<%}|P!|HfunlYWRSZ_(uR6G-htOjLg12-AY!
zny&)-<vw)>6)Fw5#pt|1Pu*%ZyZdhH)g>NoY+FXbbTGx+H;|HigXG2`sZ7ZCY)XwR
z0|Tf+0NDH>lMfU8P!_<1L@$yk+9AKdMYHPM$xf=72=yb~7+Ne;u<>-)3E}2$HXOha
z)%1~ocnfQI96oE^MBSjlCUZ_+=@5EhwvSg?i&pkJI}vM9lPvVU`t+FM8j~U`Eh93S
zgW}FrapVqEBXv;>>?zy6se<MR^SIZARe}P7XxaK9eG|Srg24i+QpjiHpU?X7BqZ~>
zG#vJygON(Grlq#OO#nOkzy4#9;GeUbOC2kvjnRznZj}!_b|w`oMuGyn_9;+V1X>JB
zq)?!oVDB}W5%AlHI`RWzriY7{D_foSWRkZ)dPa+qlEAt}ONp=T+>@)7pU(%^);k)^
z9k~5D*)Yzc288JMyCZ$)u7x+wHcu31*6+5D+@HJmMmL<RkwOXPj7_^<iNcTU=_zB!
zmy73TpC8ipt1YgUpl|l>*cwt<zV5A<nrhH5^N$UH&~4M;`m2p?{l&SHZ_XS}uI?yN
z`fC;L?kkz=Y<KT$Xb1S0p6z}U1NeQL*g^w4z{IW|jLofzIp40Hi>Jcu=`Hs5*x{Xm
z>FbM!%xxX#+|LdNZvZ#Z&1un>8ZLHOpS3l2Hgu7b#GcSwwCy!^Oz5z;QrpK9;*Z7;
zPd-doHd$BB(x5pJvx5QI5hrqkO(R+-kx>vh1OM#l<IGdc7<Ui$_D=V@Db9`4Nx5_B
zz1B&$t+;r9M!qC9S-DSQxld*GyMtXv$HM8Nn2RWX`*Dj?u+68%vXdXq3tQW9s_uC;
zzq_8x;?GYBzi;4Ny)>pb8cgjS>BaED&ySAQ+$~&(&tA0P_8crwf+qu*Ug{-fUxFAe
zpNwucbuRm~ZvMO0uDK_AMKvE1yWrRSk@S-=LU13KbzQfc;B=%NM!WIO!#R8VQazyd
zUvDSQxWkEjQP*I8VmAj;J}%CYzJ`{nLu}=_*LM;xx0j#xJzjMlzDOcSVQoKgauySx
zj}$*e*NzqR%Y7X*D|^bpxOYARWpaCTYXIj-sRSJC-cj3$8JXR={cFNKG1j)Zs=RD(
zrj$7ucZWxW%3=;;G^9I7zwdk9;*G@>ydI7`oy%aq-Xy>Npv-+;-G1$D@I6n%vM$x&
zJ#X85KbEZgIP%2w#L&JR7;t}|4M|1fZuE4+zUw8OO1}B-Wc~*8D~~U03%)Nb+MMA%
zzdffsm%BE@a|gz(KPTtNj}3j~0L}@0L&{J}63cxiO*Dt*d>D`g<6(Ah^g3|({K8IY
zepgK^E!iozYyCXNHihF<I<IX}@WzrE_H$v4p>yQ~PBJOqk~MszFTq=upgt-1jww1J
z=Ix2-N&WU&?4bJS@Vo>&*mn6o()jc#>{#%?^NVV)%Ryo}P8x5^_nYKmIrPC2UH8d`
zCL_y&9VrQ=S}D@($~R$T`E<R<chj?@qB7J(msJ9Kc)sAb#Nvn;3<p=AZ3_sH3k^UR
z{$zy7iuC3Wxq0xfXi4iPXP2Cz2KEsaq61I6P?D#~d9!w$Kw!_a@HubWM9D(h;kLwe
z!p9!>*acl9m*mD`1T&Z5(Qaz-Eyjzj1}*F(o@XR6v5$DtH8M*!m+88T=5yr)yIJ(&
z;$2s+x$dbZ`;lp0tkD`I;6CIfh}e38+a{=bCq+;<LbpC{dR&SGa|c`hvE=7Y6XK>b
z-W3UuJG}4Jo=bO@w^vD8L$SwK$9G=L5C?lsjti)*PoN#&WMf$oDDPa6S4D3fj2?@;
zsr84F3kK{5CG|d}H10=a&k+N8^5M(VR}^eqbTr6cjCV}k0D6d+F_O|I^KWaF^gJS8
zpHW344~6C=Og~^T%@0XV5o@7dfa<M<kTcdqr!E*C9Fjt9o$-mCcwN-q3vD%b8wIL~
zgzuYGP}N>0I8=`yT9$Nhz(PSwf-qWcN5_Z*QB-8LjhBc~jf{d`QQ2<|=pAy=mh2k{
zq^1p9Gz+nrJFAZigMSaAGsQKriE5(B=0SODR8-H)n@g(&Co@j+2o}5x2fr@t588`d
z8zqt3Pf6ygw6UnNF^G<glD<a#Lmmu#?h3dDBc$!k=d8hx??wt3iyt<niDsp<Zmp9+
zU1zK!u>(~GHDtap#W*N}!V8E=e?E5E>XC$kMn!9bno24~yP4k)lfN!VYzve}Rf3Pc
zPuMDj0iexpMEw{qui;R)KX2%eFQbWe><(E?0zP=FuP;BxgM`M{7cby`z(fF!NJ&Mj
zs=ZE4`>!?NvF=#_{Y>%}HHsIAf_L(Sy1)QrK6vnH#|jsOw5YfD;Qpzs$Nl}FMqWD#
zvwEov{L_NBx;c;!IIsbd;>Ol;1a6@*nrLseBA<Bm;c$7!!-yk;0gtJnqJph}&Har?
zp%2BzlKb^MsDZ6%Vj328(`r%KHI<X|0O><6;Wr+-y=ef0Q1;Tu($2VyQhj)d$XLDm
zXG~_b8o@~LxdIVP##x97as0R;Q-IwhdpQ8onPNJr#4n}5pL=l)YS_1ead+UYC!n<Y
zO>cWtwH@~y+zf>vgz~va<o{25=ipu0vi<wmwrx8d+qP}9W3%IqZQJSCwr$%<$Nh58
z{hfEt?R(z)2i{A@$i`UnlRek2TB~MN&F{>q<p)7hfKVF!LQI(*j1)hW$5-fu_Ehjp
z6CCT;5uoQKY9=vNQe3|sUx*%4lr*cM%Ep=_Wu6A;Quk&oBg<1i?PYu_^wQtE?{DEa
z#m#=*+rYsPi?S56n;~3P7giv5JVR#4!GTE&Zepxx7*)7298d;jjGnWRQ0`S38he!}
z=+gt7Izc^5RRXInn7Ws=Nnj7^Mc{%Y4k5|Ii=!R$=<kOWr3YBJjn2B4OsR)NrqMel
za0{Jf!Z!KZb8x_*xM$w%%rs4|gt$LR?f^g@bTd2nxi<Xu!vDm0!OAJ;JLrl#C{!}P
zb)~P_THoy*13Za03dctMj<c8tphUJtq;&C9z=ngJbM`e@Bsis*C)cG8+%_$Kv`2Pm
zY!UJ92ymd)p@7*<0sF|{%a@dOk~1dMlzT7|{BsICF|fNC=-`k(?D3)cA9ifH8Fip<
z2ejGk{Evcg28tlD2$*|n>kZt3jP%r<YG)rh&a5-$#El`puHH9Se&J-<7|s?F5}qzQ
za*UoY2~LU`R1{{wvv$=-{;U|NJydP2Eux>~mY1q;J<3zdpPIPjXk5Ab0;(0YahLAK
z6lO=gwUyURKmu2u6O0GvKkp5%Jqnej-6Bb)ViKl6$(Jt}Rtm0`7XUjN*ohX`9z>(&
zSeOx{=F{`OA20{S$ya}W2zQub;V8knkf)Idw?%g_eQ2Qdt|@2->Sz&{W6?bg6^a}j
zm{`KAsUKtpiA8V?S|vTve2sq(T|z;Y+)g0Wc(zvM!8_=`C>ov918#pxJ?`Ha8?vX*
zYPQRkL|xZG<?fl4rc|Rjky^`vJ(Kah7<dWy3kZz(gn+x{E2g>ZfvsvqRpfOTPFyH*
zp3>GE2N?SM)Itv^v6o6uuu6tJT1bK5z^JXVoePYOmQP0!?qnRb>T5c4gQ+TlHNTZ0
z7b>AJ0tEjxkPjlTSVh;0JXhsWF`N5*{`-`qJ~RhrjEzE!t^c96gzR9M%$c1aaP`I#
z6?b+nM^CRgg$QsOvN}>6-9Ge|5wH9W!ml1n*>aArX41rK71}A;z%D03y`rt0pc4`t
zD?sL}Nz@gG36)pY(ae;Bl|(}t)f~A|QiX>J&t4QMB(U$>@$>y(c??UD`Qz2L$-t*Y
z27?zRB{6Ol`>7L0bV{O!?+DQ<hy~ZkRPK=L_DHTB<u$a1z$A`)azGQFjTT8A!chq5
zr<uY5akS)*O#p0=_?M;T=3d%qfMeQpEt?hGz3Sqa@-zg2rMVW>K~&Zi0sK5vDNcN!
zU?oGrvqN8nj`_;)3$adr&Iuh8dd|GNdDVr5q|K%rWXjFGWLE(8{+gq5GQFC=+j@k9
zV~-lM>3H3H@41<Yol#NoNxxUx81<|)^)=z^@WXc^9j+cf3dtIHo;Yy58^eTe<gC?p
zLl^VBnuISdVyq`gXZL)c#)enEGk*v`@YH`cET+3(P)5G%HS$LR$^3GFYs%VNK_@8_
zgs76O_zlr(Hm8D=QS_8Iy~NH^Ct-r8+>ZBg{fOQo>oI5UyxG+i7*^FpZ`rVGZEN>t
z>(rrSA?M``?aWYgxzQK6?*R~4kumN3tsHDMrYjnJ#%+(}#a2=}1d|93)OYm>5EP>5
zs=*4c1Al81HlJ_mb+2cm!16e4Wpd#F-9%1JVUi~zq#o-X3$&$QcX%l8ipAm*I$u0d
zVQy>5E6yppH;S$u{gj6&u}F;3QRBdg(ewFKVVXJ13B}ctjx@i74^_>NZu9Xft`Sb|
zlJ1u`Ls~}kp51i1o3>ijwhU1j)Uu(TRl9r5fIl!UHKcq;cRc_GwZDdcl{T0bOccX@
zX?ZuA&?~ch>5A-kPV1y8rGB(m+F-P{;W(_QGw3S>h^7!XwQk#7Mu{B?T2?n2s1pJs
znwCp5PQezVyFQj!x%Y+fr<{A47n@61%tu=~=m6bgDw<)MI|K3T7T$X#CgnqI?_Fp=
z6=58HS6)^x8)ED03@quX)Nc#jVs-I_M>Xd)RB0`sv&!elw{K9`YHGBA>2zfd-^m-G
zqKxL8sHLYCgb+B~)Kt>$a!)uynrBtgkUbejC+2DcWU3QwYe-O!fKmzezY{=P%~^3w
z7-xR&Mf3P^l7g8|SGnG>;v9JjO$8j>3NnXWY?Ri&fj-OVKdCbnAAOU7z=mK@bD>W`
z@pBv*-N>Wbi4Ba(9ZHC-M9S}SW6>F=7X<qxWtR(P0-=QoHxP&TizIkkaIGJe#T+Al
zSmo9n{ezt~XA$6seV({;6)J7lz$~Lo-W{U@efI@@eNG+$S2Mdlp7OGDq|6XE>d&7)
zr+y{Hy68eM<2<)8^-;T;+GG{+xMP)=^J!**anL4Gq2eZ%D?<lTZ9%LZTFf8LVs4)l
zqw`_FjLKiJ$r;_TaAi{Bx8htI8ZdXXneRR67ZcaH48zvYg}Y<b*KQ6-VU~3*)6qY8
zL{qn2^@&b$I|JVs9a_kXbvEUh%Ckc!6!zF5*wAXG77D<{#1;w|t<Xe4nOi4`#=9s6
z?dAuV3IZek@JbS;sw?|0NEGX28X~+ju5XIs^05OUX&Qt==L(wKmMOoiGA8uG22W|7
zG-zt^`DgqiyFc8)0pQrP()J)Z?A5%UrE+BU8&<{-XKbfLs;^ftiJAm$fFbJ?;Hgxo
z9L2+pBPwMRv&%FxtJrgK3!;)?RQLm?7NXeXT$wIt1T<-flrt=QC9WLsZ4yl1v7kx9
zEwDDO^m)I)p8F82HMb<VC5RiJ!4KO8q#t#zPmVq0a3I@IW`T+MP$&iJ=J7iis}z9=
zeT$NY#e%*PwK|b$!v6)nV($yf#Ofn+^Rp*4&Dbb082i?<W<X~1Ix9F$x@>q0nr*h`
z1TX#4LZ+4cLao*7F5Hkx8CrdDf$&1SM_vv5o$^8)Fxy!yyjOj?6h8uY>Jd^>j&g-u
zd_!`RQD~{!GR=x+ZZwa9wos*BOBP$B;wkCK&T3}zc!=y#Gk!VnP&ScpG7#DG8^0~d
z<M*H8JO=5@@e&dB$3EAtQ5Ov?E!i}0P>W%~dKyoO!FmTe5uHED-qYQgJmba})lKET
z)ZjA&`uqrD0!p7h9?x==MPG%aJ8LHdx>Q^yQ07RVqP<pTXDv@hGz3}hz^c;$E?l6t
z23d;hz{11RO^Q%JvohHsWyi*2dZ2`+cSq9Qtpx#zS8wj+ZkLvI&%3T;s0_b$Z2V!}
zHzvJ_WbEYN*~E#Plmk}Ww9E3iBrh;Y53&77#!*?5t~G*(SyNsTmb^^QhgerU)+o+4
zCDqW7W^VDb`vq<UkmMWQ*f5#({XkU3z7klF68@tw6c9aOh$gXBlr`D?1$&)R<k(=P
z9g&`l##X?5<G>4A(L%`sZ6wc6TCKBm$h^i*cH|2(8YA!Y3(&nXT6GT4E;5K*%7O|y
zdL!@nb^5*jgZ^ZZP2&tRWvxml_zRaH$F2K;M@~D9Z?6!GOJ7I#tyW*4P+rUAG|s8x
zmVYpyVPrLgU{aLkw@^xc`uh1!CDd8ls#eqDlBS-LH$CnvFPXEl)`WXbew9$}I|g7&
zDD8aM5R34xm{nvKS>-Z9-+*5<n(nUlu);FkAi;w1@Jz2fih>=D<2;er5-0~2O9ak9
z)J^QUadTq8dH081NYFtuD)dVQV3k)S*b=+PSw3dJ$BK#g1=Aw#Ptl*8BR0JD&+p?K
z?Z#wwe~#NUX_zb|jNVw@*-=w5NEo0&gN!9}-RhRiYrz(@N2%9eVb!b%hT@amO_Qun
zxemc&AVFC~en{pM4UwspB<)yXc2XYqFnQ7TsoB_Udi0rMcFK)hXF9$1UNgSdSUhcV
zYhdlT2{Hj`Q*kdPcCh78)G5b_>fK?U>n>~h(C@NfNTls@(LAcpz1Mq*iC>3}VcZCz
zDPMa+8|~!((a{%4rHEVVRUm4_4AD`ILkT$=1cFV_ZB}_MyFTOeNpfG<{5YOV*rC<B
z2%5Ixx(G)XL=aSbkG^V!jgaG-1?R$nz-nVbO^HoeF}yGN3c9r>2=m-^-8r50_4ek&
zf}<OLFSh(aE66WHD*;wAnqVrSD95C&$IYWj0u&Kxs6Nop0r^PDu$0!~wQPhUu%-Jw
zgrJu7S&5WTb%6)dAt-DGHH5*IyVgSC&UVWeB;l*xHbF`kG~H{Q14@gOhqmenY$J!=
z=jM0>DEg+4B%6>-^TH5VVDecEQco=lR?O=oAh&3-_(`ERZ*CZsYE3a)PZhQ!>`*RL
zfOx3F6&4jrBdEzDN1>k=c%D$8?oFc_&p3z{eg%&r={XFbCYI~qvGP$Q)pS*(%q(n|
zhKmZeav9dKMx2oq8s`S#AC<9eHdql?Avwfh>(UV;UYz^w=w?{OU-h>!+ot<C_$}Re
z*44bvP{*cTsv};i0o(3dE+-cMHdQjU*|}1XVa<4I+p;?V%{&^rfK|UhcDiij3|X2(
zF-*Ma)q<y^>Q#;Q&~_|&M`(QOSgbVHq5d;YNfyVoBn2(bqLo3w+2DaPA3DAQxt=5G
zybUG~IGFe_cpf(O?8td%KY=_CddiDGsf{b5O{-i13l@G`QmH>ktjRpWflzm5mB|(s
zU68jwG{FHmo(-OX<t&a4;{fp7E6+JdB;?fb@m~{9Hls*K)Uf^QaUu%uub`^?eLb0A
z`m(G^Zw1L6rH^N=^ySbexhZ?k8T?0b_H;mpo)8wM1VYgCGeQ?@RAbE)8%c={&_<B`
z0u3hGT7x>tOmw8I)34x54zU!b94P_U`U2xZKx6FXlRYV$HJb**_JxqMEjjg%gL_YH
zv(>{+yl-H*esH|-KSTY5r01*ey?yz3xKHqOo%!&xt@CnW)U<Fv?h;=V2B(`loev1R
zniAjmF2<<M+HFCgVH>BXkK67+!(o>+vtjDLu9H{qO@f3920nq!<PMMk1l-GD@+@cI
zt<LRTKsu!_9kvSDOk2+BTcv|pbx3-@S=wC8E@s8fNfY0&P7QT$R>N9O5`Bm)xhGXK
zBE*0YhB}v~im1aMlzEwJ8S-1?hO&TIdMdDApfq@0<Z8PWS;3E8Xv8k!ac9?kAGYj&
zmO+uhSw0f7!LhJ{Z_|kT^*J3Sv1`@fvvZZdse`1B%`+-%2s+1u@w4iVerop$dn0pl
zZ>^P6X0D`E%ljaAKYWZR9>8`bCzo0<-Wk%yK`7aDa*_R8iIz88h&oG1bF0UPOO>`u
z(M3jt<@u?Ss*!6vmZt$tjb*8@`}Rsm)_M+At<wnQ;-m&w@{8q^gB^vrsE*AMTGcP(
zTM?e+jj++f8cK6%8~6`NP$T-S0q@}mofHMlByuO*9VI%iWzQLS$cDp_P<4AVs8fnn
z5a3OaJE4`yk?S7klEf#j@gcrL{}XK!Rb)S8!tUjqS!r&(m(Hy8)?!03DQ`{1c^3c{
zKcB2ssMXsU_;x#ovdJWPo`=?I|6xi4wcZ1cEK9%bK9>rD=_=V9X)QZ=4Z8=*`c2&#
z59*8FZdD@_1L4vUN|nVn?6Vr{!zlB_{7VqM2tW;~)24G-UK_ILKFcd(pNfqS8N2l}
zxjYB6pN%kVG4%YoJy$1=U&6((09x>UapCiB2E(el3%9cSWRJr-gE}pWdO>L6G3w{4
zO4)wyt+g54AG!N-H>WGZ+C+HXk}~X(nhPVB&0=>`ytl_m1EOG=TwU0%D^$l#F^40<
zCdUvJi3(uC@+7C`H{V6(fzed~KxVFbZ%lc1MCNar;PQV*QukJ=oG{k>MF`Q;-T%D6
z?y&>+M5TX_o0)Uu0U)BZYCsv3qZ(_q!sW=r_S8L~7K9*7kH~ITDhxqn!HN)IPGgA}
zuo_4Ps)r%auUMgIYK5Rh@~)~uxS(q`GfV2BLS8$cy;_i3R6gQ-`TC?)lxq>Dnm{Lv
z#FwH}GW%TAo6UyZ3-|ymoylN~WA8nZ)av2pe5BNO<c-ucW#{3FcrW$+mo*OlxpT?7
zaSbCZbq`Xm7`Fg`C@m1gRb&ngG!XP^LcU3kEyay+QFz^;IYoat7ec68aAx*b3%4u`
zc=_R+pDoioK@aXtd7*Eiq00B?X!wJxwq_QkE)!}J6+Lp43M!}^?9Ga87PJpqUi|Da
zmYGz6v+GvDa`mgoisbqv*W6H-$@ZX&Nr{}OJ@by4mI_N~Uu|5UYhc~GfFC&>s%?H&
zWJb%3qaZO?U)_>SWVCt7FZ1WNi04fwu+O)wyDw*qJE-7>1_vM>W9GA;sZ*)t%jt}1
zN39=%^rB9|YKtE>xn#rd7h$xd^-2YW0EKvJr<M!&8z+@hMw&RWhE>}-vDOurslKO~
zB<}-`H?k#4OE8_u#g+R<1yQq}2kt?UgllLkAmGt_W2>ssako*^T}xyyB}E_ejySwh
zU~)ix_8dF(l4Luw@p}G>k^LF41&*0(Yk`4H$`$U!Ta0k$f@PbM&Ik3W2u8VQKngl^
zFfe<`7i`z1^yHYJf=iM!#kA~LF&8SKmfA!Cl_rMHQ>;c0+IDm{-lNMk6^>eAOLXrL
zNbU)^(++UcBU-BH(rW5})*)7?X4jD^vGS|rE6eLW(ZL6y6VV74$q|0nCnOl-mW4=Q
z{z3(-<;^KaV4!u!Ynea1#rI1Kh%n*4biv|*QeOz5S8+!$p`3CaU;d1p<P?>33+du<
zI0{CpO1)CftVHSLYwzsJ35C9lnUHDdE$lIxlN|FT(tRuN5^~y<2@Ui{`aLRlir##D
zWuYbtYLzGpwfa+las=CJ^XzN2c5aem^Ou8~3;qn0nojPnT<vbAl3yZF55?aaa(GP&
zK-0dXK^XyB<Px}W958SchBthVr*bYW5Am&q>B?oGt!fl-nZxWdXk_Q%>&nes<K*$-
zS1RAaYV4+9pk#l39RV#CNEi!d8CFZdoV$McUL+yfb=GXDMkWiWfi?WT&@)UCRsS7Q
z)=6AG$dm;Hz=AiLwZ~#-g5n%c0N5aZJE`oYRkmk#&O_G$F~+0emqulR;F8QAQ#g#h
z#p$+b8FKq}Qb%{6vu+Epx1U@6=lHvnBbn0UF*W^RDqj*XHDfVWd*$_2p?edtS3mE!
zObx*nDp;!vn+>cDn8-{D43iCr<!~fEG(uyNc{&p~E#QI{+*l&-5+7@WGhaGY&ebJc
zl(P$VWzS6{YZJjU9|z+zqiGKKrAW=$Nms~kg&dos7h7w2rH$P02gaquLm>udk0|;P
zFyg$N{j6EJIBAl-oWBiVW9#In+}1wSi{rtN=;GaKvpkMqXdQauUr$p4UrEB5wXZnm
zC%jQzKh{SKBcrtgk50F-uG8M3=0udNX_`NmL}LVVNeEWIiR$A$M-9VaMbIip>PH<d
zi5u8FKOHg|O5&llho0O}2kVF3pfWzj>bdyY8f3Xv>W&IdNqj}uIOav`3dsZ7vzn(P
z2aVIQ=1A1ebErJ+iyK!v=dLyiIkq`+F?{`8HS8IJywC62diVXujaOy>WZ)#9`_%a)
z{y8D)kIkp&+GmbyY-k@{%&%~1C(^05Ln!=6?Ia4Enq#uuMr~E3<R{f|GIL19D5bd+
zLnqh=zFP$JP2lZ#-*afFZ*18MJtTwy!NI$GTD<TJlKD31KZi8g6@g!{D3wV^yJOm)
z$i5vCo5XM8Est&&tNnT#8iRJd#2-{R6+7_&$jo~X*C;qSeYS6YMEjW4DIe>W+WqR4
zUZ&fQGx+0&{}KEZ5CYFU#6pM^`?rMltGj{)F5PFu;f|>I!t|<zFRAE6WVJsudTR)R
zQu_MgH&7CE=mQow;~%{`9C>^TDv*3H$Xz>cPY;}Ts@}fd9Bmr|F)M-9$55LmdKbXR
z1`cNz;S0F|;p2iEsCT75|FpP~1vd!w!GN0-8x=w5Py@3Q3op_HtAz<wuv`m~NX(r+
zUaCM;DitcVwQ3T;UF)&g1Z6)VvNA5=By2TG!(z0K$0D<R9E3lNV6hTY2G)fO?2#VY
zwQ=a1TNY|n5Y^|;M4FfmE9~1Bm1NuP`Zl_{;b;?9)-zT^leru+kz=(;ua>*wfHNV)
zw*)=L1sAB&$zSl?i~+*edB4<y|DoYU9LsWJw;_`<xqfDWRUM9s@HO<J&3<lPt~*a(
zDhTm;vr~3G9U$_M!m(QDSJB2dyJy)b%|2=G7;FOeu)g5*W;}C1Q%6i+jEwf9viU`O
z7;rM|7lus||7@7acX&5MxIG5hI<PH&+~XzA4+iF*rd=iVrngebRMT8^oCRuzIZQN1
zjL#40%l8ncpU7J<S&BU&N0w^O$Jw0bC?J%g#>3INv>&u_lCqfFmZaV*0%O+R(&dGv
zG(~GI|HUUpD`VuV8|ucB1O(U|-l3U&#I0PB1=8DPafI>QC2JVFzP(C>8}n35YYkX2
zFZ;&0T_dTnD5KQ6IMc%or+vO9w%N98i%lP7b_WM1mj@TG_ha8?8-0po<9G1h7hY?^
zRb8}O+}dY8^`D!vqND8A<X`Gk!QN}q=Sz$>FL%j2eH9OBe{FsAAHPc^`l~7w#&A`R
z2VIzu3o2g{PqO1WjCar!Oi6#(Vpc4yN#Hx6D4(p8fe$5LXf9t~6#PK8CdW+)tIy7q
zk3js@TKx@sd6V5Q!}$rhmELh#h};+cn~gfbRJYYU!aXR#^j*IWZTHwP!7;nT?a&vP
zL!^wit#pQaX8C5p*Kd+CJ0>P6w<LWDP88~816F*xFBZ2776RKxjAE;66i4pk1|BPS
zCqy3v-gqsb=zf{I?r&pc*FM?ZRP&ing5#p2h4@(mt{*g$3%%QoK_&boBCsAuip^z(
zC@}ViiXcJGVw4ykrwMV8n?=xIVw`68IVjqku972E2SuRvMdu4rb}HwbC<o;)S3d;y
zLjrG=ss^aSu7JAN%0scZaa69pRJbazP_|)SpT9DAD|8;w$RiXUXiWxs^U3LPd#du*
zZjaa<V8c=TV!*gTTcF4Y&-YjX%AjL$2Lw4{$R1}Ph5$W9Xs&T`Jpul~1bP9!#Q*HK
zb1Oz{a_;VM+^h7o^!jG@@~jkS&h4}W@DPKdt)gt5S;fguvMOA^4X-l?5)G|+Fxanb
zxWdnYGzb%+8Le-}nkud=W&{w!*$P`h{5@Wf%U_0M9MC|SxLcZO7s8Fo6xrXEh4m0%
z<OE}W8p8;4#U6=Pf1zxj{z?I(b2V<_Buq304$Q$NTUAO4cA^P3^XG6VFF{#VdQ;Vk
ztU&k*DIu8xpa+yxo<j&~hcR+6@2)nbnaoUiz7o#{a2eCG)zCcaoFk0bqCZ!<YsxF=
z>Y6}WkKXfXK1R3uGYMP-1VB5<Q`Z*BX4Sfkg9I})mL(tFbUv`BW4NnJcCa_!vwA<)
zvpuZRQ=@g~+T+4zO^WFn?v*LW!n~yCz}=<;oQ&(YrhTX?qWM?A5N1)8ZY}op{oHCB
z_-zw1BWtwY$f@;LDS{?;t5Oys4lnIZ3$4v)g?5Mn9R(IW;gy<))ar*&^;J~y^ng_;
z^t4FiYMA~D1WoFI*JN&p3M#WW6+zmG+U@pq(Mtm3GbNbNheQyq1p-cHXPx9{3WHs7
zJBj9vivr`9KE_fS)!oHgiCfW_6jDXFzycAv9Hyu=X|wI>R-<m+z1@X@n_8S@wjEiR
z#+@V?#Hs0^HdXX|ros$WZDB{rvt?6%lXUt<f=CdrFk6*n(YxWLeBMRw^c@lju1rRf
zlSs*k;<`w(SNUe12Yy8%88N34Sr}ZRTDqE-7&*&rH^hkLA`=EJ_HXug65qBDYD-j$
z6Y~<RveuX(z{L1z<F>w9_LDMB|1i!^tgaC9p{0^jz!I`r3z_s(1}u}a%t>2|n@4G*
zq&t}-Wu4u@8oWc1n2_CT5(MD{rYKz@D?br`DJY_en@&Z~gNHNvg1dpH2m(m)@qrqL
zHR>i)hMXF6I;)j5Z(UzEYoV%JbzifQy0#=^$r5oxwESJBPWe(I4hxscy|Y(@8=2~~
zY9W!7wX*r!czs4U8#K)tw}@hW%>TG;6j~t4j9mlG7r){i&2#B`1`u!yB3jRaD{0cJ
zPzH5gf`lQ|2)j|hxh_f0St#6XYpE=%1o*9oo<@3DFQ~2EPk%cEBf($l1)x7i&BeG5
zF`d0}fCS7oAG*3iuS&4qC`uiI-MDiT^(vpMIRo~m9lQ{L9rSjR-P;tp*in7{&w5w#
zqg@r%&fHxU>rL*q$CnpNFAjInp!8}SXCL9alikBU;xs3d{p2BRN!%E@Vlmo*$MZD6
zjZ}rMuki%0Xu?g7m|mg1_me+nhX_dfO+(!Myswhf1k*#&Etcm%3xCpbvuuI%JOh9A
zp9^=<B=Bo%NjRTjqeKD$zWRt(B+2chHDPZh2(I2IS*vk{eIn1jePimn+_#6_Yozp&
z0xPF|dVmr8f$7C6hQ4lpv6!y^n$+s7)}_0XM7o@{RM=r;_dF%$cDh+>wxx51>p2C?
zhk@6bX9S-|5WG$VfM#6o>1wQApvs+S82ctfNMt=g<ZnBN3-y6lMhDej^1VQfu8T~)
zbkaKM&dMZckQZN)odUx8$%Qn~&X>ub9o6x<F0C>gOIcuB8;?RN0sz|pLmr6}tv)BZ
zv@~83lrhR~B<cQmcuRz)4g-s9j>kDh#B}R!JyAcA&cakcPOwHY^p##emMf}_7_CKS
zF$pP;TzyOmRd6sSBE$|b0>oH^ndPagUknmi45N%BI?>jpyn5^``xju3rFrz^wu=m-
zM2Y(<<F-VN_*@9Vp}HyR)O5E_IdvbAmILAlWuSUzYg^4y3~_bsz!=1$w{v+fTbkG$
z`d6e1&bN?|@3~$Q6Ht(mxhq9|+k9>sj!cdHM5TRx(+wJ8Gpk0iv)Sp#wZn_>CiZP&
z%a+)K=e5H?4WeQ1?(2Fwc$6*KzBS8lV+OG!M{=8{56?<O)EakIEY}f&LT_B+)gPaU
zj{ltkO34+m)crZ&!~*=krGWl1xcnmpRC{V7{4*OCV7sm6iebMq-USr#q=CJs{1_N!
zZW+$SCj}<Yu8YI1CQA_Mmh>dVOnp8O>J<gHp%qYMrf_Cxr2E0~Z6k2ka?d;&Wu!X-
zBjm$uF4Lk`CB*G9HIX6~cD(Trn5Zo^z$BAN<YZkDvPn{tY<zL!LilNkg^MN|?4DXF
zQxy1sr74H5nkK#Ld|O8%!&apY<$C7;P+-k8DlbE!^qaw$@Pgms6;jup>R7O!cW8Oc
z%RdjQV`6I47Ct?f`8+qDiCzCwQNY#0*4WO~>0en4)`~JQ>7NP%YNx*2hf0ik65__{
zjU!Z*^Fj?3t!-lTF#4$0#_7gCw_N@FW^L({w$j=6+?h>X?QHfk1h8|B1-+Jt;fA$Q
z1yvzWY@SKUCUst(kv);><k9!eu>og!!+IA>w!l&t6t)DE8)d|CY{CLn3MIsCY<}d=
z@3S_nH&+ffGL<#hR%w=vD(>@70c{oeAAvU<2<keyz6dp)@*8d_o+N4QQR(PN;V5tZ
zSVlp+SyUG4qitnuJRVoKG2_e<66AyhyT=sw_`3J@g#-jvdJ}Jp3k8>@E9W_NoaV>y
z%O=^nun&%~(>8g6xeLY=tJYTtA>YBVpxy^*TI6s>tLg%gwwzDBgm$1^7&F1^C_NxL
zjY(|bo}fo_gnMQ~b_kwxayYi0(V^Wym+(#!<AbXFuMJUs(=j131vKRg2f23k1Eo{P
zuvo-mwBf1vSHxWRH!{G#I`-EYlyvE#)boZQqYzoh2=UqM6$MvC?)=S$ib7#31{~6z
z$By+<OyXRnoIAqvVO@O3qQYO<p&g&v$zby(?1iG@s_QJfrxS2DNr&jaEVn1WHTusy
z`>H<<>(y5wqLKS3l0B@W=Kr#fl=YN5ijesAkAaoakHgg6Pn8J}IRF5(&m%r_<mDah
z?49VH&A-|h+8S6`)0x<qo@?t#V~b&J9ec6R!eU^rojt*Wo$G&K3c-Ah^e-6*A_JV=
zLxN!Oe+<Bu)PyL7D%O^~j0@MkqN-@}Kb=KUkqDfpx;>Hg7vhB65TYl4KzXr8B)|DC
zhIf}zjPsLu&H8b%ZLii<rXn8Q{rNH7t}}JZi+Rh3nX8QpBjlOKM=l!S8?qeTb=Mn5
z$n)AA%pSzo2F7tX(YXNzK0se^pXX26X?hp!pbWh}hF=1L5;O|-Zoh7D?)orcog&>6
zPX^(sQdCVd+yhhfLsDH+fA}QuME)w~%r-??0!7uA-96I(cvk+_vV42LOWD!V(lWps
zT#wy_G3e`Re~-+=h2bJaHi(Wt{OdC<wMPke`e1Bsu})WSDSYHGMl?U}B{?Yx#<u$E
z4pnPt`>DU6Kw5EpVPPRL5!G11&q7~cziV6}=LeAd^>l07^Aeg>Z9zhQLBl<I7nJA{
zV0pK7VuD2Y$=x#xmJCjjh#yQSFd&&9Fi0N=(N6awepPRd18(#U+tSk2R#%KmEH|BY
zbB<a0So+bu=uxB95{XIo5<17&pqcbnnS8S$Qj<7C-B#z)jt(9;WDHB(V@<nucdU2+
z9E4Khd?Uxq`0ue91vyucs9PPLuBN7@Mn*!zC^J@8R!T}rUm~7*$3o%pCnqLoX=or)
zM`kVcU+(KU;up>;u$k-NIMk0t+`*ch%_Ysg<^b-OS^TII6B_7g-MET`85tSAQF%b5
zZ&zP-^QyFQt1t2BKXI0Xv(DexdA85Jc$LWzkQ#O4&eHsG`C6UU^-9!C%}$O1BN;xz
zXyO%A1KAr95o-_PB7ID(Ys+I?=~kZma20I5vQVx@3huHsj45<L{c7uF#OLMcwjWtA
zezEprV(=}Zc$C+@dQ`<QF@ccb^WS5D@C<=NJJWFH)~E#C=ZnRFg~%QqS~-BxN+Zqw
z=<GD8PdPm+8=e$NA<a@X;vTT_Zpxr3GF$s-C@eQ928YMH&p3DT7**-=1Of$9Wti91
zxe9g70VxDP#>ak6duzIpSGm7<GmO_J5x!R2C&9J4`Jj85O?GuXff*Yc12KT=3SEYu
z!DSdC;sNpff&e_&%lG8g?-}6*lb%2E;PX&q=00vez30+)aEVWOjri5_7V2~mJ#c0~
z#|8b8AB5DWOOGm~kfloeJQ+WOeKBbjES-i9{H>KjW#^!0$;a0EqyY!7WL-;6wNMm?
zkOmOZs@F}9A{zxaOqh(E9MY9FmJM`IqJUB+Ia@EAo}o@N5E8@$5sHBjg)~xKC2ku9
zYCNfkkH`FZOtf<)d^;=$jTdN!%znsVR|i;;2qT}dxceF6bF+kOAcLWbgy1uWhtS0_
z{tmIDhNqAaumG*#b|#mrq(NT8Ra02-7N*fy$Kp%UQmAY^^Z-j}`bqyvScV)O>Lr}5
z7+qqwSXEFJ64UWl5YjvnR)&q{ugp^=%&MF~xo93&AQEyN_-w=MHNLvqNDETV68UI0
zePNW{EL>a>EnlJqu_Flb3@+G14sWxv@S;<%VI-W4xJ<)dJ9SyrBZhDD^Ye`X3j}jj
z%SDVxdiy6}5P5@)2>XBaBXSo3>8D-93?Po^b1u*2({D!f$OWFfJ>Q)gAUyz%NIfli
z<;z+zyWn`rDDp>Ro_;Z;_`3H94C}~*la2jmjLG_vRReO|=5!SOT}VRdQ1VzTXn^$8
zQ&58gtFnStUn6~K?_mzcR2RM@)10ByfZT^$<HELr#Ast!b#v*n&)4iZ0lmM!7rne+
zBc_Pe49GfGIUP;Lf_|6WAx5gghGbOvvDtsxY$8|E*2ZP9lfkdZYiMXll&c9P?FBC(
zo^xf;h~#~h*hGBq4Ig*^lU%wGSyd>ilbi+wRay_nKLC%<hlVCvx_pOtnAF<W($q+E
z=tY1eAW>i0$%#jBg*!DW3@J#QCPHGK0MjECkE^HB1@GkDVsg@7U^d*>D_#594QwZS
z<3M&I!VwBpzt!~$?@~H$sCIKH!b()DFQ&p|Xflh3ni6@czn@;su74IdOEWz@d=GRO
zXFd657^-HMVWwK_+A8VB3M9CQ$n}nf2wJVLd4uphftD8?4W*&6r!HGWv+GrDlFkMz
zt8~B>#se<t)fr68;=-bSLB_|AdNyo0ufb}1_aUYu`#u`7A4@wXsy@Etdjh=zP!`+V
zxMz?E0bXi$HaYh-D%9A_Ne>AB0;|t4?<%u6KH%s=Y}^!j0l?hD+0udGK}d4i;}c*1
zdEe=!9oKbPMoO!fwC?^AaS$S867|6%&2x*W*TN|EV-o9Rx7hhj3^>-$A?SiY;B3;E
zu-Qcr#xFV@Jqn5&5gZT`EdDv+ypZf7IN#N#e+n0dtpn24;s#R-lTfxxQxKpO6)bdv
zz*{nL-8P)>OzQgN%qDC(8D2kgSB@OYBe7nC-iT0l_rF4jNhJQ@fr`<mvls$NbF9dD
zJ=<thk12UI9yxVXIwbmr=nWE@;lmTq_=U%2tDAUv9rxaf2E4WHp$cG;2D?j#TAwy(
zRFSOolz1BvN(L^XFMD1SqjfaPl3oz(yt387%Ku7`5}6S`w<yN1Zr=7|nADMu(rA~N
z3PY|vTAH{ng=vCl_F-gIqE&|}D!oyuNJF@lSA$F9hg6Y6y$g{BNh3mN#I(3ayqr>>
zEGCohZVz>Lf&E9&Np!9${-K=uSD7d7WYboE3)#GfZ_ZQMaUNMxft1XLa`#hC&psjq
zhV{jfDSQSrj;MsnY%3(7dz34AO)|U@7Hesm3IZBjl$M<~V;PNeO{|T10d+2l^ImI7
z8n9djiJx+UC)57KZY&)(sN^UGU`0DWhQg`9Z_NAjRU6a-<z>=H+DSuQJqbzykeisT
z4@_A&o?oM@1|OqU;hZwe+{g?izbtL9!MW5W;82xkGNww!t-|(L$p_2#$$E(}WYwSo
zVMi)@HuRh+M95G7oG%MF^{7ixwwrCB7+<BNohWOat;mcsS8Q)LZMcF9d31W^s1X96
z#M??~zMX+74qhW$b#F-4GV)hHKFBGbFM2C{zF&qs8|O?fJ*?iVnu^}r@bT*wW0v$*
zq6Nloi!ij?0|wk|?PF8$$^~uHAectEmdT`geGH$nTr6yoo?u0tJn3;p_AJ3+o4SSE
zg2!+F{)@<V6fLwZ?-IVJQ^YGAOku_D=x9QP@RF2Otq~n{X5XCmvNF;~d0{)uyD_L*
zA4=GRA(yCP!;lY8Kb<;c6L*l5*9r)l<8yP&kFV2PHGC^8>`iThzfR6^KsJ5tXquP8
zV-A(oniabV$K%+fOB#=*M9kk$PAoe0{QY`@$a=&D(GH|iv{G*E>DKcwL|=*D_5N{G
zSp21wzVI_L5cwlAfB^j8qe3G)N0Wa=2A_5O06z<Fejf7w_D^J-jNRJ56r?~O@nJS_
zR~-1F`UhGGi_s{WV6a{0NCp=yr2_irAG+{qhivQM_1P7?p1*q)E?iixk<f1kHknS`
ziVPw#%u?B$vcA@B6fDs|QbZy}vDTRKfVgONaA`@XNuKS@Xo2Ig1_g<eCCpZ?Am>qY
zz~4e@BBB*AB&=&}K^06dtwTQ3SZ<HewC&U8$OD1m`<14Lo%<;}ineyx5%EjF!8YhK
zTzkddvC{_F_31$y9*VD6p5tCPWBg31+)v3^s1}&&tvW-~K^)3?cKW#3`Eb%^T`;|t
z1S(gROe(4Mw;b4tc7}Sz=M&g<;{i|jjt8Qt*P*5xC~;ZAxZ1x<$?Wz4<89UfrR<A@
z$S?3+m#ZKd=Y$-+z*e{$++#Hp?Ca96&11QqX}c2l`&EJx%T(HA3zy8pqcv{oh_Sja
zzwm~>pv~;g968|2LvpA^^U9@G4}`|7!M=jqDGpSYp21kd=l_DgF-_M=9FTE$5I~mg
zhm;?fqDHVhdz}&I5HmI=mi|=Z(YWwxeMZUF?+ky<@_9S`ESC2VF+bxt2GHtf!zKUJ
zGeH3OTiZ6UxBvf5`>*btq10ph*|a<0m-!%C*=1X+Fn}~flJkyp2Y}`$GUHE#Q)n#W
zELmRjH-3V@*Uq>YacR{W%!Ta}XW?a2INT^>FY13=UCE#~b()-}QnPHEcouT-zV)w9
z=}ywm7|6I{yoG*#j8X?bVkC)*MGTJO*ecvl)<9c;v--vyKueei%}OHK$L6F`h^ou+
zpq?+Ya^mHawWB1cW$3;OF1X1|mww+>P^eVd*-ZJE1tO)Oe*sI9K!n{Vbrq%h-Q3x|
zSjB!uCCrA!ov@CQ;t~}@8!^A4TNq_U9f2L9Qp;+ZP;#cgB#^45JMTl(C)42(lc<T)
zag%>qANj;$?WG_hDu!~H)tWIpvWEAn*J6!j(d@RtDrY+;if4(H2bf1CEHlHmd4J<9
zxq<3c;N;N=8Y&ZJP;LQB&|@I>r;-?_LkE67xHaPf`|`|c-%G0A8)lAHR8pcRbmPa1
z_!Kih<&5`Tq2eUKgdQPnfsF4-p6J6HF^_L{94_qpSc<E@gdPEVi1~pfyo+2RVe7)9
zl-G4|n`QEiNosV&t`9h%)C(!N7tJ^9)qMSL`^2;=XKxO?1rCkCZ3DObH(UhfJ?!sY
z$q{!e8L~g_L(_%_@#%a&HSzv?4or^WK+o~BKiGa=>p#mZ|Lt1;)ydh;=Kptzzq&+4
zyqsMC1IFN$)FZC=l{h7lAV7XV*of))AosZ;LO5G4twlt-#^Z+CSB(1e2KTd9@~JEL
zxF0*mnTvyX&=OxF{5Ar+yDPRy4b~vqjhUSwU`icT4_VE=0D;r=P;_=dCkL>rQv?*|
zlBPs<%a)SW&}f`9Oqj^Uq-H5=PivnjTGQ&P6m&z<Ylf%RAp`-Jn-}iRThlpiSD>FS
z=#fh4;%tnlxR^I=e0f9T!$4Iji0l^Im5uKYt_oB1wOpOfO6lD5B*PK>MrO`=H*`YI
zkhaGJ1U6P0-Kk4^G~)60y-+T~A&G7<=A<5Nt31bcvjEA#T&IicnD0h5G`pdI4xM&G
zpAq$oS~GW_1Yc9`5$7=eYG6F*24p4n&TH59T-;mEPjKBWr;YTvjcDHF7<EX+3}U-4
z)kdV6UO-u8>7aPd!;$h{nGHm&s;%h6-@bRE%snz6gHAHow=c!Vjtw?qp1Fk|#US~9
zHpqWBRX`wGfd5lX@V~y9e?R+&3gPln{|Wfd+Qfecw0ySzf6M;;G1dFK;qT>F|1yRD
zl+60?VywU0|1R79mpwZEpZ0%BG5$@2@psJc%CLW7UdjH%{2^QTmrm^Oh~FhL|3a`*
z{fYSZiTE$U%->PJD@y)_QlS15^`}$+M*hvUe>}~<i!%O2)TjBA_;3II&HLl|zphdL
zNc&yl@NYEQKWTq{zJEXdku?9io4?DS{e_8O{AbJ`O9sDVepe6r3lq=w&zL{7gnq~T
zp7;M3hJf!+%wJ95A07IAJI!AR814T?{G&6!FP;2_Ff#p5#9v?3UkfR}`}F%##9uT6
zvp;EnTbTIW{P$VxzsyZ6{!eq-zcc<b=J|{8S#jCvZ;U@9px@2^b6ELzb2XR0ng7oq
p^LOk2yb1r^+RXiL)<*yPmMkv?3ihwVkUxJhKQ*M0y#Dp-e*w_hWr_d*

literal 0
HcmV?d00001

diff --git a/docs/images/select.png b/docs/images/select.png
new file mode 100644
index 0000000000000000000000000000000000000000..32368d0d69c23fbafac4d8ed94f90dd6cea51ca5
GIT binary patch
literal 5505
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
WPhw%n4Kttur1ex!tz6Y6@_zu$g|%Y<

literal 0
HcmV?d00001

diff --git a/docs/images/state_notifier_provider.png b/docs/images/state_notifier_provider.png
new file mode 100644
index 0000000000000000000000000000000000000000..38e43f4ed761b2d77f69419c85f3cebf8b55de00
GIT binary patch
literal 3203
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

literal 0
HcmV?d00001

diff --git a/docs/images/state_provider.png b/docs/images/state_provider.png
new file mode 100644
index 0000000000000000000000000000000000000000..343dceef40cc6762ba82eaf7ae57dd3522510a01
GIT binary patch
literal 2185
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
LNkvXXu0mjf%}o~Z

literal 0
HcmV?d00001

diff --git a/docs/images/stream_provider.png b/docs/images/stream_provider.png
new file mode 100644
index 0000000000000000000000000000000000000000..2bf25bae7f3909ce0a859e60f846a9d810b71ef9
GIT binary patch
literal 3878
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

literal 0
HcmV?d00001

diff --git a/docs/images/test_overrides.png b/docs/images/test_overrides.png
new file mode 100644
index 0000000000000000000000000000000000000000..a17557e24cbc9ee393f52389df4deebced02130c
GIT binary patch
literal 3232
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
M3``LvdUu}x6Q{aBl>h($

literal 0
HcmV?d00001

diff --git a/docs/images/watch.png b/docs/images/watch.png
new file mode 100644
index 0000000000000000000000000000000000000000..7789c1f6910372873ed9b7626b54799ed1d9fc59
GIT binary patch
literal 6505
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

literal 0
HcmV?d00001

diff --git a/test/change_notifier_provider.dart b/test/change_notifier_provider.dart
new file mode 100644
index 0000000..cc2fa95
--- /dev/null
+++ b/test/change_notifier_provider.dart
@@ -0,0 +1,36 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'utils.dart';
+
+final incrementProvider = ChangeNotifierProvider<IncrementNotifier>(
+  (ref) => IncrementNotifier(13), // <1>
+);
+
+class IncrementNotifier extends ChangeNotifier {
+  IncrementNotifier(this.number);
+
+  int number;
+
+  void increment() {
+    number++;
+    notifyListeners();
+  }
+}
+
+void main() {
+  test('doit incrémenter un état', () {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(incrementProvider.notifier).number, equals(13)); // <2>
+
+    // when:
+    container.read(incrementProvider).increment(); // <3>
+
+    // then:
+    expect(container.read(incrementProvider).number, equals(14)); // <4>
+  });
+}
diff --git a/test/dependance_cyclique.dart b/test/dependance_cyclique.dart
new file mode 100644
index 0000000..abe30b7
--- /dev/null
+++ b/test/dependance_cyclique.dart
@@ -0,0 +1,19 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'utils.dart';
+
+final Matcher throwsProviderException = throwsA(const TypeMatcher<ProviderException>());
+
+final Provider<int> provider = Provider<int>((ref) => ref.watch(otherProvider)); // <1>
+final Provider<int> otherProvider = Provider<int>((ref) => ref.watch(provider));
+
+void main() {
+  test('doit lever une exception suite à une dépendance cyclique', () {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(() => container.read(provider), throwsProviderException); // <2>
+  });
+}
diff --git a/test/future_provider.dart b/test/future_provider.dart
new file mode 100644
index 0000000..5e81976
--- /dev/null
+++ b/test/future_provider.dart
@@ -0,0 +1,26 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'utils.dart';
+
+const duration = Duration(milliseconds: 100);
+
+final asyncIntProvider = FutureProvider<int>( // <1>
+  (ref) => Future.delayed(duration, () => 13),
+);
+
+void main() {
+  test('doit consommer un futur', () async {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(asyncIntProvider), equals(const AsyncValue<int>.loading())); // <2>
+
+    // when:
+    await Future.delayed(duration + const Duration(milliseconds: 50)); // <3>
+
+    // then:
+    expect(container.read(asyncIntProvider), equals(const AsyncValue.data(13))); // <4>
+  });
+}
diff --git a/test/listener.dart b/test/listener.dart
new file mode 100644
index 0000000..934fd4f
--- /dev/null
+++ b/test/listener.dart
@@ -0,0 +1,40 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'utils.dart';
+
+final intProvider = StateProvider<int>((_) => 13); // <1>
+final watchProvider = Provider<int>((ref) => ref.watch(intProvider));
+
+void main() {
+  test('doit écouter un état', () {
+    const defaultValue = -1;
+
+    // given:
+    final container = createContainer();
+
+    var intValue = defaultValue; // <2>
+    container.listen<int>(intProvider, (_, next) => intValue = next);
+
+    var watchValue = defaultValue;
+    container.listen<int>(watchProvider, (_, next) => watchValue = next);
+
+    // expect:
+    expect(intValue, equals(defaultValue)); // <3>
+    expect(watchValue, equals(defaultValue));
+
+    // when:
+    container.read(intProvider.notifier).state = 42; // <4>
+
+    // then:
+    expect(intValue, equals(42)); // <5>
+    expect(watchValue, equals(defaultValue));
+
+    // when:
+    container.read(watchProvider); // <6>
+
+    // then:
+    expect(intValue, equals(42)); // <7>
+    expect(watchValue, equals(42));
+  });
+}
diff --git a/test/provider.dart b/test/provider.dart
new file mode 100644
index 0000000..66bc6c6
--- /dev/null
+++ b/test/provider.dart
@@ -0,0 +1,15 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+final intProvider = Provider<int>((_) => 13); // <1>
+
+void main() {
+  test('doit récupérer un état', () {
+    // given:
+    final container = ProviderContainer(); // <2>
+    addTearDown(container.dispose);
+
+    // expect:
+    expect(container.read(intProvider), equals(13)); // <3>
+  });
+}
diff --git a/test/select.dart b/test/select.dart
new file mode 100644
index 0000000..2fe9dae
--- /dev/null
+++ b/test/select.dart
@@ -0,0 +1,36 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'utils.dart';
+
+final intProvider = StateProvider((_) => 13); // <1>
+final moduloProvider = Provider<int>((ref) => ref.watch(intProvider.select((state) => state % 10)));
+
+void main() {
+  test('doit écouter le modulo 10', () {
+    // given:
+    final container = createContainer();
+
+    // and:
+    var called = 0;
+    container.listen(moduloProvider, (_, __) => called++); // <2>
+
+    // expect:
+    expect(container.read(moduloProvider), equals(3)); // <3>
+    expect(called, equals(0));
+
+    // when:
+    container.read(intProvider.notifier).state = 42; // <4>
+
+    // then:
+    expect(container.read(moduloProvider), equals(2)); // <5>
+    expect(called, equals(1));
+
+    // when:
+    container.read(intProvider.notifier).state = 22; // <6>
+
+    // expect:
+    expect(container.read(moduloProvider), equals(2)); // <7>
+    expect(called, equals(1));
+  });
+}
diff --git a/test/state_notifier_provider.dart b/test/state_notifier_provider.dart
new file mode 100644
index 0000000..b9445bd
--- /dev/null
+++ b/test/state_notifier_provider.dart
@@ -0,0 +1,40 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import 'utils.dart';
+
+final intProvider = StateProvider<int>((ref) => 13); // <1>
+
+final incrementProvider = StateNotifierProvider<IncrementNotifier, int>(
+  (ref) => IncrementNotifier(ref.watch(intProvider)),
+);
+
+class IncrementNotifier extends StateNotifier<int> {
+  IncrementNotifier(int value) : super(value);
+
+  void increment() { // <2>
+    state++;
+  }
+}
+
+void main() {
+  test('doit incrémenter un état', () {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(incrementProvider), equals(13)); // <3>
+
+    // when:
+    container.read(incrementProvider.notifier).increment(); // <4>
+
+    // then:
+    expect(container.read(incrementProvider), equals(14)); // <5>
+
+    // when:
+    container.read(intProvider.notifier).state = 42; // <6>
+
+    // then:
+    expect(container.read(incrementProvider), equals(42)); // <7>
+  });
+}
diff --git a/test/state_provider.dart b/test/state_provider.dart
new file mode 100644
index 0000000..edd0f88
--- /dev/null
+++ b/test/state_provider.dart
@@ -0,0 +1,22 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'utils.dart';
+
+final intProvider = StateProvider((_) => 13); // <1>
+
+void main() {
+  test('doit modifier un état', () {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(intProvider), equals(13));
+
+    // when:
+    container.read(intProvider.notifier).state = 42; // <2>
+
+    // expect:
+    expect(container.read(intProvider), equals(42)); // <3>
+  });
+}
diff --git a/test/stream_provider.dart b/test/stream_provider.dart
new file mode 100644
index 0000000..ad37191
--- /dev/null
+++ b/test/stream_provider.dart
@@ -0,0 +1,38 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'utils.dart';
+
+const duration = Duration(milliseconds: 100);
+
+final asyncIntProvider = StreamProvider<int>( // <1>
+  (ref) async* {
+    await Future.delayed(duration);
+    yield 13;
+
+    await Future.delayed(duration);
+    throw Error();
+  },
+);
+
+void main() {
+  test('doit consommer une stream', () async {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(asyncIntProvider), equals(const AsyncValue<int>.loading())); // <2>
+
+    // when:
+    await Future.delayed(duration + const Duration(milliseconds: 50)); // <3>
+
+    // then:
+    expect(container.read(asyncIntProvider), equals(const AsyncValue.data(13))); // <4>
+
+    // when:
+    await Future.delayed(duration); // <5>
+
+    // then:
+    expect(container.read(asyncIntProvider), isInstanceOf<AsyncError>()); // <6>
+  });
+}
diff --git a/test/test_overrides.dart b/test/test_overrides.dart
new file mode 100644
index 0000000..0d1d66e
--- /dev/null
+++ b/test/test_overrides.dart
@@ -0,0 +1,23 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'utils.dart';
+
+final intProvider = Provider<int>((_) => 13); // <1>
+final otherIntProvider = Provider<int>((_) => 13);
+
+void main() {
+  test('doit surcharger le comportement des Provider', () {
+    // given:
+    final container = createContainer(
+      overrides: [
+        intProvider.overrideWithProvider(Provider<int>((_) => 42)), // <2>
+        otherIntProvider.overrideWithValue(42),
+      ],
+    );
+
+    // expect:
+    expect(container.read(intProvider), 42); // <3>
+    expect(container.read(otherIntProvider), 42);
+  });
+}
diff --git a/test/utils.dart b/test/utils.dart
new file mode 100644
index 0000000..0d35a8d
--- /dev/null
+++ b/test/utils.dart
@@ -0,0 +1,16 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+ProviderContainer createContainer({
+  ProviderContainer? parent,
+  List<Override> overrides = const [],
+  List<ProviderObserver>? observers,
+}) {
+  final container = ProviderContainer(
+    parent: parent,
+    overrides: overrides,
+    observers: observers,
+  );
+  addTearDown(container.dispose);
+  return container;
+}
diff --git a/test/watch.dart b/test/watch.dart
new file mode 100644
index 0000000..876dfe8
--- /dev/null
+++ b/test/watch.dart
@@ -0,0 +1,22 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'utils.dart';
+
+final intProvider = StateProvider((_) => 13); // <1>
+
+void main() {
+  test('doit surveiller un état', () {
+    // given:
+    final container = createContainer();
+
+    // expect:
+    expect(container.read(intProvider), equals(13));
+
+    // when:
+    container.read(intProvider.notifier).state = 42; // <2>
+
+    // expect:
+    expect(container.read(intProvider), equals(42)); // <3>
+  });
+}
diff --git a/test/widget_test.dart b/test/widget_test.dart
deleted file mode 100644
index 35e5611..0000000
--- a/test/widget_test.dart
+++ /dev/null
@@ -1,30 +0,0 @@
-// This is a basic Flutter widget test.
-//
-// To perform an interaction with a widget in your test, use the WidgetTester
-// utility in the flutter_test package. For example, you can send tap and scroll
-// gestures. You can also use WidgetTester to find child widgets in the widget
-// tree, read text, and verify that the values of widget properties are correct.
-
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:article_flutter_riverpod/main.dart';
-
-void main() {
-  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
-    // Build our app and trigger a frame.
-    await tester.pumpWidget(const MyApp());
-
-    // Verify that our counter starts at 0.
-    expect(find.text('0'), findsOneWidget);
-    expect(find.text('1'), findsNothing);
-
-    // Tap the '+' icon and trigger a frame.
-    await tester.tap(find.byIcon(Icons.add));
-    await tester.pump();
-
-    // Verify that our counter has incremented.
-    expect(find.text('0'), findsNothing);
-    expect(find.text('1'), findsOneWidget);
-  });
-}
-- 
GitLab