From 1a53c96e17b8158f1d8746dd6a816da1b845c06a Mon Sep 17 00:00:00 2001 From: tlaudi Date: Thu, 8 May 2025 15:23:04 +0200 Subject: [PATCH 1/4] Solutions for dart challenges --- dart/challenges.dart | 49 +++++++++++++++++++--------------- dart/test/challenges_test.dart | 26 +++++++++--------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/dart/challenges.dart b/dart/challenges.dart index 1f9b01a..77d16b9 100644 --- a/dart/challenges.dart +++ b/dart/challenges.dart @@ -1,46 +1,51 @@ // Challenge 1: Null Safety String toUpperCaseOrDefault(String? input) { - // TODO: Implement logic to return the uppercase version or "DEFAULT" if null - return ""; + return input?.toUpperCase() ?? "DEFAULT"; } // Challenge 2: Typecasts String describeObject(Object obj) { - // TODO: Implement logic to handle String, int, and other types - // If the object is a String, return its length - // If the object is an int, return its square - // For other types, return "Unknown type" - return ""; + if (obj is String) { + return obj.length.toString(); + } else if (obj is int) { + return (obj * obj).toString(); + } else { + return "Unknown type"; + } } // Challenge 3: Asynchronous Programming Future processUserName( Future> userDataFuture, ) async { - // TODO: Wait for the future to complete and extract the "name" value - // If the "name" key is not present, return "Unknown" - return ""; + final userData = await userDataFuture; + return userData["name"] ?? "Unknown"; } // Challenge 4: Classes and Constructors -// Tests are commented out to avoid errors class Person { String name; int age; - // TODO: Add default constructor - - // TODO: Add named constructor with default values "Default Name" and 18 - - // TODO: Add factory constructor with caching logic - Person(this.name, this.age); + + Person.named() + : name = "Default Name", + age = 18; + + static final Map _cache = {}; + factory Person.factory(String key) { + if (_cache.containsKey(key)) { + return _cache[key]!; + } else { + final person = Person(key, 30); + _cache[key] = person; + return person; + } + } } // Challenge 5: Optional and Named Parameters -// Tests are commented out to avoid errors -String createUser() { - // TODO: Add the right parameters to the function - // return "Name: $name, Age: $age, Email: $email"; - return ""; +String createUser(String name, [String? email, int age = 18]) { + return "Name: $name, Age: $age, Email: $email"; } diff --git a/dart/test/challenges_test.dart b/dart/test/challenges_test.dart index 25b819e..978a129 100644 --- a/dart/test/challenges_test.dart +++ b/dart/test/challenges_test.dart @@ -32,23 +32,25 @@ void main() { // Test for Challenge 4: Classes and Constructors test('Person constructors work as expected', () { - // final person = Person("Alice", 30); - // expect(person.name, equals("Alice")); - // expect(person.age, equals(30)); + final person = Person("Alice", 30); + expect(person.name, equals("Alice")); + expect(person.age, equals(30)); - // final namedPerson = Person.named(); - // expect(namedPerson.name, equals("Default Name")); - // expect(namedPerson.age, equals(18)); + final namedPerson = Person.named(); + expect(namedPerson.name, equals("Default Name")); + expect(namedPerson.age, equals(18)); - // final cachedPerson = Person.factory("cached"); - // final sameCachedPerson = Person.factory("cached"); - // expect(cachedPerson, same(sameCachedPerson)); + final cachedPerson = Person.factory("cached"); + final sameCachedPerson = Person.factory("cached"); + expect(cachedPerson, same(sameCachedPerson)); }); // Test for Challenge 5: Optional and Named Parameters test('createUser handles optional and named parameters', () { - // expect(createUser("Alice"), equals("Name: Alice, Age: 18, Email: None")); - // expect(createUser("Bob", "bob@example.com"), equals("Name: Bob, Age: 18, Email: bob@example.com")); - // expect(createUser("Charlie", null, 25), equals("Name: Charlie, Age: 25, Email: None")); + expect(createUser("Alice"), equals("Name: Alice, Age: 18, Email: null")); + expect(createUser("Bob", "bob@example.com"), + equals("Name: Bob, Age: 18, Email: bob@example.com")); + expect(createUser("Charlie", null, 25), + equals("Name: Charlie, Age: 25, Email: null")); }); } From e070116f4c261ea4e3b1a2a342c3e1e7f875d15e Mon Sep 17 00:00:00 2001 From: tlaudi Date: Thu, 8 May 2025 15:26:14 +0200 Subject: [PATCH 2/4] Refactoring Readme --- README.md | 10 +++++----- dart/{dart.md => README.md} | 0 2 files changed, 5 insertions(+), 5 deletions(-) rename dart/{dart.md => README.md} (100%) diff --git a/README.md b/README.md index 3439553..975853c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Flutter Workshop -- [Dart](dart/dart.md) - - Dynamische Typen - - Keywords - - Collections - - Optional Types +- [Dart](dart/README.md) + - Typsystem + - Null Safety und Nullable Types + - Klassen + - Parameter - Asynchrone Programmierung - [Flutter](flutter/flutter.md) - Widgets diff --git a/dart/dart.md b/dart/README.md similarity index 100% rename from dart/dart.md rename to dart/README.md From 7a63b31a6fc9550a675df78baf3aeb7b91d7b7e2 Mon Sep 17 00:00:00 2001 From: tlaudi Date: Fri, 16 May 2025 14:16:45 +0200 Subject: [PATCH 3/4] Fluter documentation --- .gitignore | 1 + analysis_options.yaml | 1 + dart/README.md | 8 + dart/test/challenges_test.dart | 12 +- flutter/README.md | 418 ++++++++++++++++++++++++++++ flutter/flutter.md | 3 - flutter/images/hierarchy.png | Bin 0 -> 67531 bytes flutter/images/overflow_warning.png | Bin 0 -> 10339 bytes flutter/lib/main.dart | 40 +++ flutter/lib/styles.dart | 11 + flutter/lib/widgets.dart | 49 ++++ 11 files changed, 536 insertions(+), 7 deletions(-) create mode 100644 analysis_options.yaml create mode 100644 flutter/README.md delete mode 100644 flutter/flutter.md create mode 100644 flutter/images/hierarchy.png create mode 100644 flutter/images/overflow_warning.png create mode 100644 flutter/lib/main.dart create mode 100644 flutter/lib/styles.dart create mode 100644 flutter/lib/widgets.dart diff --git a/.gitignore b/.gitignore index dbab5eb..438ec3a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ build/ flutter_*.png pubspec.lock +.metadata # IntelliJ IDEs .idea/ diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..f9b3034 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1 @@ +include: package:flutter_lints/flutter.yaml diff --git a/dart/README.md b/dart/README.md index dd35592..45dadd5 100644 --- a/dart/README.md +++ b/dart/README.md @@ -310,6 +310,14 @@ void main() async { Für mehr Details über Streams siehe die Dokumentation zum [nutzen](https://dart.dev/libraries/async/using-streams) und [erstellen](https://dart.dev/libraries/async/creating-streams) von Streams. +## Networking + +*TODO* + +## Code Generation + +*TODO* + ## Weiterführendes - [Late/Lazy Variablen](https://dart.dev/language/variables#late-variables) diff --git a/dart/test/challenges_test.dart b/dart/test/challenges_test.dart index 978a129..08dc189 100644 --- a/dart/test/challenges_test.dart +++ b/dart/test/challenges_test.dart @@ -48,9 +48,13 @@ void main() { // Test for Challenge 5: Optional and Named Parameters test('createUser handles optional and named parameters', () { expect(createUser("Alice"), equals("Name: Alice, Age: 18, Email: null")); - expect(createUser("Bob", "bob@example.com"), - equals("Name: Bob, Age: 18, Email: bob@example.com")); - expect(createUser("Charlie", null, 25), - equals("Name: Charlie, Age: 25, Email: null")); + expect( + createUser("Bob", "bob@example.com"), + equals("Name: Bob, Age: 18, Email: bob@example.com"), + ); + expect( + createUser("Charlie", null, 25), + equals("Name: Charlie, Age: 25, Email: null"), + ); }); } diff --git a/flutter/README.md b/flutter/README.md new file mode 100644 index 0000000..15ac665 --- /dev/null +++ b/flutter/README.md @@ -0,0 +1,418 @@ +# Flutter + +Flutter ist ein Open-Source Framework für Multi-Platform anwendungen. Es basiert auf [Dart](../dart/README.md) und wird von Google entwickelt und unterstützt. Seine Stärke liegt in der schnellen Entwicklung von Mobile-First Anwendungen. Per Default benutzt Flutter das Material 3 Design. + +Flutter funktioniert sehr gut mit [VSCode](https://code.visualstudio.com/). Eine Anleitung für die Installation findet sich [hier](https://docs.flutter.dev/get-started/install). + +Für eine praktische Einführung in Flutter ist dieses [code lab](https://codelabs.developers.google.com/codelabs/flutter-codelab-first?hl=de#0) zu empfehlen. Mehr über die Grundlagen kann [hier](https://docs.flutter.dev/get-started/fundamentals) nachgelesen werden. Hier gibt es eine kurze Übersicht über die wichtigsten Konzepte und Komponenten. + +## [Widgets](https://docs.flutter.dev/get-started/fundamentals/widgets) + +Widgets sind die haupt Bausteine von Flutter-Anwendungen. Fast alles in Flutter ist ein Widget. Sie sind im Code als Dart-Objekte vertreten und werden als hierarchische Komposition verwendet. + +```dart +Center( + child: Builder( + builder: (context) { + return Column( + children: [ + const Text('Hello, World!'), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () { + print('Click!'); + }, + child: const Text('A button'), + ), + ], + ); + }, + ), +), + +``` + +![Widget Hierarchie](images/hierarchy.png) + +Gedanklich kann man Widgets grob in zwei Kategorien einteilen: + +- Sichtbare Widgets, die den Inhalt der Anwendung ausmachen (hier Text und Button) +- Layout Widgets, die die Anordnung der Inhalte definieren (hier Center und Column) + +Layout Widgets nehmen immer entweder ein `child` Objekt oder eine Liste von `children` als [named parameter](../dart/README.md#parameter), wodurch eine Hierarchie entsteht. + +Eigene Widgets können erstellt werden, indem man eine neue Klasse von `StatelessWidget` erben lässt, und eine `build()` Methode definiert. Widgets können auch einen State haben, siehe dazu [State](#state). + +```dart +class PaddedText extends StatelessWidget { + const PaddedText({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: const Text('Hello, World!'), + ); + } +} +``` + +### [Scaffold](https://api.flutter.dev/flutter/material/Scaffold-class.html) + +Das `Scaffold` Widget nimmt eine spezielle Stellung in Flutter Apps ein. Es fungiert als Gundlage für unsere Layouts. Einerseits gibt es uns Zugang zu Flutters Material Design, andererseits bringt es Support für häufig genutzte Elemente wie AppBar, Drawer und BottomSheet mit. Es wird meist als direktes Child eines `MaterialApp` Widgets verwendet. + +```dart +void main() => runApp(const ScaffoldExampleApp()); + +class ScaffoldExampleApp extends StatelessWidget { + const ScaffoldExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + home: Scaffold( + appBar: AppBar(title: const Text('Example App')), + body: Center(child: Text('This is our example App :)')), + ), + ); + } +} +``` + +Mehr über `Scaffold` findest du in der [Flutter Doku](https://api.flutter.dev/flutter/material/Scaffold-class.html) oder [unter flutter.de](https://www.flutter.de/artikel/was-du-%C3%BCber-das-flutter-scaffold-widget-wissen-solltest). + +### Eine Aufzählung von nützlichen Widgets + +**Layout:** +[AppBar](https://api.flutter.dev/flutter/material/AppBar-class.html) +[Expanded](https://api.flutter.dev/flutter/widgets/Expanded-class.html) +[Card](https://api.flutter.dev/flutter/material/Card-class.html) +[Column](https://api.flutter.dev/flutter/widgets/Column-class.html) +[ConstrainedBox](https://api.flutter.dev/flutter/widgets/ConstrainedBox-class.html) +[Container](https://api.flutter.dev/flutter/widgets/Container-class.html) +[LayoutBuilder](https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html) +[ListView](https://api.flutter.dev/flutter/widgets/ListView-class.html) +[Row](https://api.flutter.dev/flutter/widgets/Row-class.html) +[Scaffold](https://api.flutter.dev/flutter/material/Scaffold-class.html) +[Stack](https://api.flutter.dev/flutter/widgets/Stack-class.html) + +**Content & Darstellung:** +[Image](https://api.flutter.dev/flutter/widgets/Image-class.html) +[Opacity](https://api.flutter.dev/flutter/widgets/Opacity-class.html) +[Text](https://api.flutter.dev/flutter/widgets/Text-class.html) +Theme + +**Funktional:** +[Builder](https://api.flutter.dev/flutter/widgets/Builder-class.html) + +## [Layout](https://docs.flutter.dev/get-started/fundamentals/layout) + +### [Constraints](https://docs.flutter.dev/ui/layout/constraints) + +Layouting in Flutter funktioniert nach dem Motto: + +> **Constraints go down. Sizes go up. Parent sets position.** + +Sprich: Ein Widget bekommt von seinem Parent gesagt, wie groß es maximal sein darf. Dann erstellt es ein layout aus seinen Children, das mit diesen Constraints kompatibel ist und teilt seinem Parent die seine finale Größe mit. Das Parent-Widget entscheidet dann, wo es Child-Widget positionieren will. Eine anschauliche Erklärung mit vielen Beispielen findest du [hier](https://docs.flutter.dev/ui/layout/constraints). + +Wenn es darum geht, die eigene Größe festzulegen lassen sich die Widgets grob in drei Gruppen mit unterschiedlichem Verhalte, einteilen: + +- Widgets, die sich so groß machen wie möglich +- Widgets, die nur so groß sind, dass sie um ihre Children passen +- Widgets, die eine festgelegte Größe genau haben wollen (z.B. Bilder) + +#### Overflow Error + +Kann ein Widget mit den gegebenen Constraints kein sinnvolles Layout erzeugen (z.B. zu viel text, Bild zu groß), dann wird in der dev-Umgebung ein klar erkennbarer Fehler angezeigt: + +![Overflow Warning](images/overflow_warning.png) + +#### Unbounded Constraints und Unbounded Size Error + +Ein Layout-Widget kann seinen Children auch komplett freie Hand bei der Festlegung ihrer Größe lassen, und ihm [Unbounded Constraints](https://docs.flutter.dev/ui/layout/constraints#unbounded-constraints) geben. Das `Column` Widget zum Beispiel schränkt seine Children in Sachen Größe nicht ein, sondern ordnet sie nur untereinander in einer Spalte an. + +Geben wir hier ein Widget als Child mit, das sich so groß wie möglich machen will, müsste es unendlich groß sein. Da das aber wenig Sinn ergibt wirft Flutter hier einen **Unbounded Size Error**. Das Prinzip wird auch in [diesem Video](https://www.youtube.com/watch?v=jckqXR5CrPI) anschaulich erklärt. + +### Responsive Layout mit LayoutBuilder + +Da Flutter auf Multi-Platform Entwicklung ausgerichtet ist wollen wir antürlich auch Designs erstellen können, die Responsive sind. Das [`LayoutBuilder`](https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html) Widget hilft hier: Es lässt uns eine Builder-Funktion definieren, die jedes mal neu aufgerufen wird, wenn sich die relevanten Constraints ändern, und entsprechend das Widget neu rendern kann. + +```dart +Scaffold( + appBar: AppBar(title: const Text('LayoutBuilder Example')), + body: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + if (constraints.maxWidth > 600) { + return _buildWideContainers(); + } else { + return _buildNormalContainer(); + } + }, + ), +) +``` + +## [BuildContext](https://api.flutter.dev/flutter/widgets/BuildContext-class.html) + +Jedes Widget hat eine `build()` Methode, die jeweils ein Argument vom Typ `BuildContext` annimmt. Dieser Context enthält Informationen über alle Vorfahren des gerenderten Widgest im Widget-Baum. Der Context kann also genutzt werden um auf Elemente und Informationen weiter oben im Baum zuzugreifen, wie beispielsweise das [Theme](#styling) der App. Dabei wird hier *nur der nächste* Vorfahre mit einem `Theme` gesucht. + +```dart +MaterialApp( + theme: ThemeData( + textTheme: TextTheme( + titleMedium: TextStyle(color: Colors.deepOrange, fontSize: 24), + bodyMedium: TextStyle(color: Colors.deepOrange, fontSize: 16), + ), + ), + home: AppBody(), +); + +class AppBody extends StatelessWidget { + const AppBody({super.key}); + + @override + Widget build(BuildContext context) { + return Text( + "Title", + // Get the Theme defined in MaterialApp + style: Theme.of(context).textTheme.titleMedium, + ); + } +} +``` + +Im Beispiel oben wird beim Aufruf von `AppBody` ein neuer Context erzeugt, in dem die `MaterialApp` und ihr `TextTheme` vorkommen. In dem Context, der in unserer `MaterialApp` aktuell ist, kommt kein Theme vor, da kein *Ancestor* unseres Widgets einen definiert hat, sondern *erst das Widget selber*. Wollen wir nun alles in ein Widget schreiben, können wir das `Builder` Widget benutzen, um einen neuen Context zu erzeugen: + +```dart +MaterialApp( + theme: ThemeData( + textTheme: TextTheme( + titleMedium: TextStyle(color: Colors.deepOrange, fontSize: 24), + bodyMedium: TextStyle(color: Colors.deepOrange, fontSize: 16), + ), + ), + // here, the Theme is not included in the Context + home: Builder( + // Creating a new Context that includes the Theme + builder: (BuildContext context) { + return Text( + "Title", + // Get the Theme from above + style: Theme.of(context).textTheme.titleMedium, + ) + ), +); +``` + +Für eine Erklärung der `.of(context)` Funktion siehe [InheritedWidget](#inheritedwidget) weiter unten. + +Mehr Details über BuildContext fintest du [in der Flutter Dokumentation](https://api.flutter.dev/flutter/widgets/BuildContext-class.html) oder [bei Medium](https://medium.com/@dihsar/understanding-flutter-buildcontext-a-guide-for-developers-367579309f3a). + +## Styling + +Der Style einer Flutter App kann mit dem Theme definiert werden. Dieser kann für die ganze App oder einen Subtree einzeln definiert werden. + +Um der ganzen App einen Style zu geben kann dem `MaterialApp` Widget ein [`ThemeData`](https://api.flutter.dev/flutter/material/ThemeData-class.html) Objekt mitgegeben werden. Die meistgenutzten Attribute von `ThemeData` sind `colorScheme` und `textScheme`, es können aber auch viele andere Aspekte der Darstellung angepasst werden. + +```dart +return MaterialApp( + title: 'ThemeData Demo', + theme: ThemeData( + + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.purple, + brightness: Brightness.dark, + ), + + textTheme: TextTheme( + displayLarge: const TextStyle( + fontSize: 72, + fontWeight: FontWeight.bold, + ), + ), + ), + home: const Home(), +); +``` + +Das Styling kann dann in anderen Komponenten via Kontext abgerufen und angewandt werden, wie im [Abschnitt zu BuildContext](#buildcontext) demonstriert. + +Um die Darstellung in einem Subtree unserer App anzupassen können wir Widgets in ein `Theme` Widget wrappen. Hierbei können wir entweder ein komplett neues `ThemeData` Objekt erzeugen, oder unser bestehendes mit Änderungen kopieren. + +```dart +Theme( + // New ThemeData + data: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.pink)), + child: FloatingActionButton(onPressed: () {}, child: const Icon(Icons.add)), +); + +Theme( + // Copy and alter ThemeData + data: Theme.of( + context, + ).copyWith(colorScheme: ColorScheme.fromSeed(seedColor: Colors.pink)), + child: const FloatingActionButton(onPressed: null, child: Icon(Icons.add)), +); +``` + +Mehr Details dazu stehen [in der Flutter Doku](https://docs.flutter.dev/cookbook/design/themes). + +## [State](https://docs.flutter.dev/get-started/fundamentals/state-management#using-inheritedwidget) + +Es gibt verschiedene Alternativen für State Management Provider in Flutter. Hier werde ich kurz auf die Werkzeuge eingehen, welches Flutter selber mitbringt. Diese sind vor Allem für simple projekte nützlich, ***werden aber in der Produktion eher nicht benutzt***. Für komplexere Anwendungen sind externe Provider klar die bessere Option. Die zwei meist genutzten Provider [Riverpod](../riverpod/riverpod.md) und [Bloc](../bloc/bloc.md) haben ihre eigenen Readmes, in denen ich näher auf sie eingehe. + +### [StatefulWidget](https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html) + +Widgets mit Zusatand setzen sich aus einem `StatefulWidget` und einem `State<>` Objekt zusammen. Der State wird dabei vom Widget eingebunden, enthält die (typischerweise privaten) State Variablen und übernimmt die Aufgabe die `build()` Methode zu implementieren. + +```dart +class Bird extends StatefulWidget { + const Bird({ + super.key, + this.color = const Color(0xFFFFE306), + this.child, + }); + + final Color color; + final Widget? child; + + @override + State createState() => _BirdState(); +} + +class _BirdState extends State { + double _size = 1.0; + + void grow() { + setState(() { _size += 0.1; }); + } + + @override + Widget build(BuildContext context) { + return Container( + color: widget.color, + transform: Matrix4.diagonal3Values(_size, _size, 1.0), + child: widget.child, + ); + } +} +``` + +### [InheritedWidget](https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html) + +Wer schonmal mit State in Webapps zu tun hatte, kennt vermutlich das "Durchreichen" von Werten im Widget Tree via Konstruktoren. Das kann schnell unübersichtlich und sehr verbos werden. Hier kommen Flutters InheritedWidgets ins Spiel. + +Sie werden in den `context` gelegt, und Widgets weiter unten im Tree können dann darauf zugreifen. Hierfür muss das `InheritedWidget` die statischen Methoden `of(context)` und `maybeOf(context)` implementieren. + +Greift ein Widget per `.of()` auf das `InheritedWidget` zu, wird eine Abhängigkeit erzeugt, die von Flutter intern verwaltet wird. Diese führt dazu, dass das abhängige Widget neu gebaut wird, sobald sich der State ändert. Um unnötige Updates zu vermeiden kann das `InheritedWidget` ide Methode `updateShouldNotify(oldWidget)` überschreiben. + +```dart +class FrogColor extends InheritedWidget { + const FrogColor({ + super.key, + required this.color, + required super.child, + }); + + final Color color; + + static FrogColor? maybeOf(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + static FrogColor of(BuildContext context) { + final FrogColor? result = maybeOf(context); + assert(result != null, 'No FrogColor found in context'); + return result!; + } + + @override + bool updateShouldNotify(FrogColor oldWidget) => color != oldWidget.color; +} + +class MyPage extends StatelessWidget { + const MyPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: FrogColor( + color: Colors.green, + child: Builder( + builder: (BuildContext innerContext) { + return Text( + 'Hello Frog', + style: TextStyle(color: FrogColor.of(innerContext).color), + ); + }, + ), + ), + ); + } +} +``` + +beachte hier, dass das `Builder` Widget einen neuen [BuildContext](#buildcontext) erzeugt, welcher das `FrogColor` Widget enthält und im `Text` Widget benutzt wird. + +Ein anschauliches Video zu `InheritedWidget` gibt es [hier](https://www.youtube.com/watch?v=Zbm3hjPjQMk). + +### [ValueNotifier](https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html) und [ChangeNotifier](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html) + +Alternativ bietet Flutter noch das *Listenable* Interface. Die einfachste Implementierung hiervon ist der `ValueNotifier<>`, der genau einen Wert als State speichert. Diesem State kann ein Subtree von Widgets zugeordnet werden, welcher bei Änderungen neu gebaut wird, indem dieser mit einem `ValueListenableBuilder` gewrappt wird. + +Falls mehrere Werte oder eigene Funktionalität nötig ist, kann die `ChangeNotifier` Klasse verwendet werden, und dazu der passende `ListenableBuilder`. + +```dart +ValueNotifier valueCounterNotifier = ValueNotifier(0); + +class CounterNotifier extends ChangeNotifier { + int _count = 0; + int get count => _count; + + void increment() { + _count++; + notifyListeners(); + } +} + +Column( + children: [ + + ValueListenableBuilder( + valueListenable: valueCounterNotifier, + builder: (context, value, child) { + return Text('counter: $value'); + }, + ), + TextButton( + child: Text('Increment'), + onPressed: () { + valueCounterNotifier.value++; + }, + ), + + ListenableBuilder( + listenable: counterNotifier, + builder: (context, child) { + return Text('counter: ${counterNotifier.count}'); + }, + ), + TextButton( + child: Text('Increment'), + onPressed: () { + counterNotifier.increment(); + }, + ), + + ], +) +``` + +Wie man die Flutter State Komponenten nutzen kann, um eine MVVM-Architektur aufzubauen, kann man [hier](https://docs.flutter.dev/get-started/fundamentals/state-management#using-mvvm-for-your-applications-architecture +) nachlesen. + +## Debugging + +Dart und Flutter kommen mit [maßgeschneiderten DevTools](https://docs.flutter.dev/tools/devtools). Diese können entweder im Browser oder direkt in VSCoder geöffnet werden. Die normalen Chrome DevTools nützen hier leider wenig, da Flutter kein richtiges DOM erzeugt, sondern die App auf ein Canvas rendert. + +Flutter bietet auch ein [Guide](https://docs.flutter.dev/testing/overview) zum Aufsetzen von automatischen Tests. diff --git a/flutter/flutter.md b/flutter/flutter.md deleted file mode 100644 index fa67016..0000000 --- a/flutter/flutter.md +++ /dev/null @@ -1,3 +0,0 @@ -# Flutter - -TODO diff --git a/flutter/images/hierarchy.png b/flutter/images/hierarchy.png new file mode 100644 index 0000000000000000000000000000000000000000..eb3e0d4492b65f4cdb3c1dc8e3479aa1aedc6f1e GIT binary patch literal 67531 zcmYhj2|Uzo_dh-}W*8=neP>XXvXwRKP?oZnED0lfgzU1-kVJ)2h_aO>vVn?J12&4 z;bM16?!3CrkAwfxCUesYQZLqPtvkPx4(lo_&raW53YqEP>rCI6Es(`*J}*1m3r(-} z)v8}zQadzgqDK>9|MzDq1#kaXm^*svJGPu2frBAp1=m*3O#SLeP*L7sqKOP|$WYvu zK7IQ1>#cL&EW!`{y}Z1x|BX6f?&25O-CUHHpKrF`A)6oN@KS3>U{CYny;M;4;I_*|B)l|<% zwb?Uyj;iXKj~9eS88=Wy&aWl46*cVB`X z;s~q=mjRUdos9e&V?E|ORQ~JjManP2R^sGps@I-Prr%z=Sp$CMc5Zy5oBQxHeLmk< z=*-iwTJyezc^+;IWyBISA+d#P2?oc4KPAj0vi-eUF$dWPDCh$vtWKN+Hu`+yL`i=B z7FDn%eMRUrpW%?uHc1e?otkkwfzM<%$?C)WyxA35Zo1(KI5i)59r#nKL*_%5nz0U_ zzYMJthPaIE&A{Bz$c&DTwy`#D+Co{njxV25QgSUT^*q-2VSenR^IvAhqzaLOD zuI&g9ood)?rG$QuiWXb+uL%0B#N!?*i8=I7YnfbkA>$=b7ZJ0}lH1pGafdR3Z59u>4+Jh=#Y zw^=9({Am$dUcl`kAOrL<>KL;T3Mh*{1UYDqL+>gZQ{AjKOcEcyTD?E_w3Ko>#c6PO zRbCvy4pteDfP2)}?`Vx>ro=957mE#F-38wb@%wt(0=M18<-5FM{r(T0rZ5A2Z3fXTt^!;Ru=zfVlmhwZgGnO22v&6hq^>gLIx zU)#Bo5P+73tc)iQuS&X_B;G9C|FKXyb~`^g?HD-p<;diGUU!|`IJTb^xI14 ztNfF4f*p=F8RAC{Nka+>;S2h!TK8@1?UiG9UmUHN3vpo%k)MC?*Ke!Q>GOQ?o?)@b zPGi;QbFw_|8{pbtQN23jM*1ym16M+BYf`Jou&Q1@D6kw!NJxmM>}}<^abqV3aRsS0b)o_Ys8+8YbA z^xGN@(V-Yx#gwBV=M)n5tRD8y9rmvtcJflP<%N!SFb*GVMT|y7L_~yD)`i#o-d)PK zYkxm*-CKQ^79D^@xCoDL6o3yxfq&d4?oIsl|FN}Vr>_3p_@&0t=4D8h)_g%0-!be* zaAtZOYr}?))M#pI>OSb>_?voARuFqraqxzwhpjZ4h%Z9BDGq+trRZMC=i%o5xD)wd zfvy~Lw7lazD;#yI`)4w5N>M}JzNC+fmB7+PX=`gA&JWf<5&=6aa~ar1_lA^x<~#rOu^w{bkyAW^V=_0pY%pd>-ZLQ&tf7q&dN~hM;s-Z zWD+?tb$`i$(vCm@y+9!buV+QLe!X$=PcKb42)}?|GdHv|NoBw%I0Q<(X3t*LU;Fg1 zMTNKf&XcWdMX~j@v!c2&u0h#0y?O1Z#91=Gm~GBqt!Mp!v%5}DGE}vYwiELIwCmb z=<`Ct!e9Yx^1M!A?+ynpe^N;9F>XS1h2wLm-~0$%kv^q6$TX8V+s>ld@Pwo%t-XH~ z4_=7b(2&E3`J}exJAp@^s38=qV5wqj?cRlZ_9WLd+ugJDap3H{G>Q5g8BrY{a_FO? zqE0*D%tlClM|gG&*>!E@b!zCd2KK?DED$S@@e-4l|9xa+f6F7yKCJX`rXp5i`0ApF zh$qNzI1JKsMR_YMcu^#%Yp+<|AfR@%t*uS0{Js!)59B5nGI9#5a|(C&-@ALKP!&L* zlADp|t#FLd+xrKL---{n-brk3{dF??@R^f^OOWUamVTDsGcRWnKzDPRbXa-U*U{0H zt=+e~1cSebhv5)naiE{ySiUYP%}&8}^h9ImiQIJbw6tFbPt!_ee&*j64A2s#(hhB$gte_vlzM8l6~5sCle%V`I4h}v&WmfmQ_L=vmu-)P~9>oz` zC)gS-c$`1-Gbi)+>x6kV?%Lp;^|?8b_w21!Q5g=9yS1Jml~QJxtBbml-0U?`Cm-+_?N0tov(+t z5B7E@CT!hK$Ga@$@%=kwvTs+X^^6R?>Cq}5QlE#A`BiaoaZ%Aqg<01e0;G+F<*nht zuu3THo~k=m21d4+5UQmqrBpim!1H|9R$H zWR?%fQ{hKnpZ7w)=y(2Lz#3YzNR!~p7Pu0E{#g}IgEZx#zP^Y;sxl(azx+u}y|?&m zc*#Lyn80`xuc^>^&_q^MRpsaB&vpCEAvRY+1E>&Zg~^Ku78aJRm5~UE_zeRR99jp! zKuy_-p;N+zFO4AsdY&PPSm8k4RZg31i95&Uxt>g1#l`C*Y9x%VUj05aL~Vsj-TT4{ zy(&41++Z`RD6@Yi?m`I?vJ$rj?joDfm9LY(o+Ng;QEa|$8Ep=RM?IjZqz3y1@0Uo@ zHD;bed|1Hz%K{%xtaX2tr#Bhfn_Wo#AWKmQVH%MFxBxLrqzA>VKsSB{Jrx5h#yolQ z1kcubMLWal=$y}z($gz{l`+YOhW^y*692cEaT!Dp>iNLr8XK}h6W@dS;VwUeNiK*Y z&_c_#zmIRI{x)>Vk~WJ6dIpTd0lce#utoIqo?mMWNIebWH`K+87o}{-$M+Es3&P`e zk$yb&pRI>HIK_|Rxrusi4v{Q5zIGbErH)HQBCYVo%pGj4LSf}7=sh$1YYC+V9(wg1 zc8p?hi0MUPvA(3PE`ts>Po1$QeSoF#IfUaJ>K|-5;z}k5i>=fDUm(LRAY!rzb>!G@ z)|OTF)ImHFCog*9YtoXEG+rZ5<+^bV6zTU!tzU1)NE+R;6uRMhP%2ug(vC=y>Ks)K0fdcwv2lzsAunF zWpC}`(fX5=%ePKFcnYr~>S+=AbU6bE6OA98!deT*FWv>`2uqbDdecbX;rx1J{Wfa1 z{cpNU4*$o5h+x=Xt?>Qx@>s2-{L%(+8l3DLB3xpP+!!hU z{b6ixZBK9GeFVMOH8hBxdK1bjnDoo`#q2A*yPGSWIX9h8Nx&`G$ibw_o_IL4W48+bg93a9SK#6)w-YtAmVT7H4GDLJR>%^QIAhcr(H zWoA2!rKwn22Y;zFNF1Bp__gEedgIRbyzl1Gl~u$Xm*V}ZeO@om=A6 z+s)aR-$z9y>qkcoq&F;vvUL+nL|Ll7x|>(aB?@^}=H1&~a|DjgR&a00O`r-k{Yd`t z0|$z;Co1w5XL?WQR@GSF&|%vJMgaSmRdJXXm#j;E!zp0t@a0sg1ko4Cx_mJiBnuc~ zCo-h5`g0M0_bY*Nn0-iV z?2-RkMas%?VP-cwUSg{{#N_`P!68@?R9pLk^Qm3`YhP@yeNg_*vC-Oa5%P@y=Py|j z#b#Rr6d1P8a?UC5!2?GIhCYF99T%ZBVpA^h#B^QZt@w5HE_C z2CiyYn#9DV2MU3h6zOoypk{#nA)q1boy4|5!&<0|J3{S+x}ya98y8N#(RTkKmK*L( z-rdG&AwioZi{V2lImX?p;EWDsRmT#?8S3wolr_W2b-#b!w}M?IE z^0;-`GC1#Gt@>nQ%v>fFC+l9VT}r#hyBO0W@=oN=)*Gt>*PE(B!r$(Em(i~VaiQN{ zw(=Th7?KhzDk#1EC&)!1$N%`64$L9Y8e*q9Gc)ogS2u_9FDeV~#)w^`Jr)Rz;-Kqj7j9 zF}|^5ZX&v*s?)$5k}bykD*yFeDprZ9b4GtJDVhf8$r^?KyhP(ODYR{raP@fVYsZU5 zOu>4*?XvoP0dywRC-QK7Itgh4VbfhS0|Wh1^#PNw4fq=t5xbj96E?-O)5aMRVNsI( zP4&|+e*R&|;?Zs44-0G;U7F=z>->U#RG~RlHgraO4&*#MTZw~q(~&jr>5e}edDy+= zzBfGb@c%^~VB!IW)(w=5{!620WASlyeK~}<>l(H^a2pCf>dRocAD694?f>Lo35JQ~ zx{L*>;U&hxAcEBRj^*QfVl zV527K-DZ}To4Q1Q0DsH0`x7rg4&p30`4|?kgq(<;Zg+4RVkv!Q78@YAoWkk@uNZ#l zi}YJ|-9Ga3W~Axrjcc_9zviuaP!G;dNIM0< z`2E9WLA;LL5Pn~`2{#aq3N52Z^ZX&Sont3TSG74jp7jO_DB3C(i6hLTK_2|gGAjJn|E(j$9 zp|$?lzbr}%>EDf4?a^yIP%wFEK*8h}^K*MJHICYaD%4h3V$pw*RSP%9S?rvY7}3UsL}aqb2#_s-a%w=PnT1M3~DNv_?fRhmuu)!xmfTCgLiGq zIT;wEQ*W=Ye@{sP@W@__z!_fdL*+5oJ0ZWeVMJ;q@G;Hj?z6Uq(|tN@9HOepJDzmi zNl_}yPX8RPX(?$O%3vinhmC?qp#GdJ|Nh%sv!9yrOpt-_IC1FAcIi0pY%zB}mcV@s zplpy{6j#l+f4bI=Wt{&K*u~Z(^DxdTasUvClux)kYRaN)C?^zn1pd;r)l~7&5@4bc zX*9gS1J^w8y3;Lr8gNAoYqs$hR)F=5fYSstEStnXhp5RY^J!zW9o!C9O?k;rFtpb( zSE2WT7i{5{sGx$uhr#^d#`3zE>*3F@-+iv!yA3T6hGaYr6a>PUdIWp&Px-TJ(pcs^ zX?q8CbhN0`n~xx|0=ScWU$gxa@UfWnbpXZyZ@Eu$Nllz{7;-x)(or~WEM`?YL!T|t^d3aX!7Mg0kE&1l=qeLcy~_(SY^C=gc%nECT(@0p!sqwP`^1}oxW+8!wq6KQ18!aRTJst^1C z6L@gSJuwt;g~jD%jdn`COML)T3q&N7EOSp)IigM9R=nF$%pbQ|{wZu+5*I>!=T)cItFP&WXpIT0m3Z3T)TgyFlzPN!MSS-z ztecl#F9a+S5EER7m(w%aYq}O}gvEvVYm_u`d|)AeYLu;{eMF5#()tw-FL2At3ypWq4B3nCGZEIQyLep6xfSveY4oLx+6iT zSB=y@iXl!?wxxmOh}Yi&OM_ff?(Bc=aY+bfg>&!`Wqnm?Y-XYJwl|O&r0dYk?Ch+X zz1u-SrS~~!zPOnfr+51U&ed7a9nJA=czEdKT|m<|?%<^_`uM`VQg>1@+|zaz1tGc}4=JIAA{e+ikN_m>jrLnHUu{A&zqet>m7w#O zx&w5DjQo>6qz8T!^+RbChOgR1hL=o2!7++YgBsE+&=FpLD?(Zxqm#*ow1RD*E)nht zKxQG(BW%8#bXsqEQdvWXON@l-#z7n0@;=CxWwy&#{|cRb$3xZU&T>+Z1bgAW4G8}d za|GA&GALpmZf=GIZS^+8sncG?hr>5d2|#5up~=S|(?_+8Ew!k90&Bwu&P9nrwd}wE1&3@~)zb|~G`Pf! z+Qpbx$lIQUtkn@k=5r75|MJ^a^=XC}^a&A9gUrCZf{8yBu(FnH{A5CYR;ict&C>F+ zcV(V4sK27_$AEf#iib+itwmE_vN_S zPHW7-6D1__2UtlfHQbtwtQh{>2hMrap-aGS@A;4Igw^eK&DLYA$j5n(6cO(8Ak#%C zautOWsi}{2md0Q>(y6)QG2pT|7BkrU02Gv3H<2imj__cPjVEmTzep{{l zuz~NE&jZS4&wzX;Ez--7V-HY4idx0;zj?-PiHB(l7UPy~=l}ZI=C0jG+Q5w;gEX%% z-D`}54a^x z4w6R{!w1VfK_SR|>E5C{4}XBc-qR%}QF2~n zUXsNov7aFlr~heOgb&tt8gK+FaBkyXADA_buZY={yN5M;VM}17gCM@c-))pR7!$T1 z<-|`?KoGth+#`mB%SlSb1UqHxXRHUS4_n?5^Wz%-0j8g|2_8Sft{>+V&zK?Y%2|r z1U#!p%(dfL)|~;dC;a~cE4c)M8g5bsr(dn2w8eN&^FW8}ofLJ@iKe^x{OtD)`-aZ( z<2lC$-=$9^G5yb}$3K!_>FPLbVAUzKataDA%(xbp?-9H~u^H*KOO-4({_!*{ef9z| z=UC0=pRxl6mdlhgZA_ok$;#j-$+?mQYP7;5P{#ufB}|9dK8CR12c5t$t1X6QvG}KQ zU(o2mc>HMT?aiRyi&eW5SUE@@7s;?&u4k|*VUc-2cOxHze0NEb za5t7jiyOy+vnFpsq8r^W9!S)(kHeqeh|8mr?+|;~ED*l*NL%;4V2@vZ>O3NH%=Hhw z;rA{z1tfy*1jGiz;8C%tN?ODR9E1^dFdx#j-xS}G@M@-e&gZsT{0rUef9d&0b+6Dh zsbO20Nom8QsakgR)nh|Hu(iJQm;a?!6c%6@*x6?B*O7V^Ryw=;@TlL)Kg!QSjEOd#FI^t;WsKb%$mfK19u*6-JIrn4kx`~%CI@n{dK|!TZJD`g3*TK zDbYZGhnbjA19z1I!0Gwn8t`9RuU61)jz^Nl?YFx=sna+<7KwfXppg;&_ez!bEhz<3 zA=L?dB@DrO#F2SOs9gr32;vox0r0nOz0DFCv?1q*P*)>LS}TwgZkaX7QZd}?MTIvIoScN~W5+@6s|Af1>!T#gkAP-bZmY9-_C(QN zLy?vki4yTvqt2Z(z^eyqA|}!V|H++xetzp4C;&MktQzmYPanWQFCR=qfDFH+G}me< zpg@b@5SHRVTX_JKd*H1_LB}tZi;?+b%R@S+$m9M!4eIF}qa10>*J*7J&Q&k}-#7V_9!rBp9$c-<2Trsou5h5(I3pCm61J+>@6n;21b76~ta2 zg-4u3Y(nc53X(yO##ix2CpYP=RmN++SqA%9=^K4T+=v~N2&QRJ5ArU z2XuXiedskqE}+$yk4Dww;$(pb*a>_L`Ru=vhL! zu{?O84rt^sYc?Dp1O#gUJ581cAiAM-j|NYOh0P=6Q+L1KdVX0!n^F{8A_00=#7>YY z5Q~1oVpQ;U2BHMvgG2-XZcX4jZw6gUhRTr&P~S#xSXb&8^06QYRrX@RfciDLptfb1 zCn7#cCz`b*G~N#|S!twYuH549{`%LN-*8JjJ$@GWH-GoL|NS-h&-tWyr;?y+t$pTM z=s@UKXP9_28Qz+@1@S1^+WRtCx+p!-2>Iu*ra<3Q;;34ffg_tgD+JO@R z60I5@dBpzfh);vq-$xKHQ0q}@eB;^`Nk0AhsT2k&ra=sST2fEcynW0u93Kq2#y}&O zls*SIDd{%@5T85Pboi|LfmSGW4vIpGD%f2L^s8v{bBUfi`>5WJ%wNG2$UojGH)@0X zM)@)X&?w7)KP|aJ)x?37q5@Bm(pDqWq+-V*@=$Uiy^$6R;?9$|Aw2zn-422DG9VjI z8V!IHj++(4QWA;#XuU;5Epx|Uw>nTNhmPaxF9Kfx?j_guQXEQQz!jF2oNMawGkT}b zb;icDbilaf<`_Nu6_|a1S7%`6Vri}c{+dh=-kwaa@&pAZf$A5I9t|hbLLLlyb$J0D zluh4?BouCN(?9}k`C=YmZ}FCh%d-qJgt3Mneu%3T^dM?ryO_aTvx(@we*xdD5=WjV zkE;PDa}^w&&M5;E&=bxu1k%%jcf%E|PBawI=xh@z8c=W~-#?J7grEp*5>nzQ);7Njrgh_Q!Ng(b7;o^@$Ax^zZCqzLP2w^Trzm?tuPV`_b9&}B> z4y`Jh__3`jz$UFBd}IU{LvkO)o3ut~%(xBy6;`^sx;xKm5xAZ1+x!$qeFwZ4aKbxb z-$8?{2X*~RlP~a&C)DH0;ZMF~;?S+g86Q$k=<~}W8x1Vj5`lm01B%B-B^*dGb+S2> zOw6?j(V=%9;Rt6T0t-d0qY!x>;0w8f2Q`exQ3Kx_Ukjn)@m0OX*o3qw(iQ3L!ARy>m2mT+`P!<4Dj>;3adj8H-K2{Z=9w$F5dgU zUh{T+zqk8!Ia*H>9o(Rnh#=Mxz_H8X%!Gg_*S@X)3pg+Pjpmp4;LW=%{1EC)xPexQj zfdnGhs){94;rV|>JUogXT?yt54F9bY5*d|LWmRqBw7ZA z+n(e=Z0m9Ol2Wonl}d#+{Y|&*l6b@CKZ2yC60<$xs+593{Wn@Gll+dg57oo_$EY5p z_aGmU8W<4Vz^u%QA_91*CsZtxkkqa8SZg*BQBh-N)GV_Qnh6pM7==NfDZ1=TYv6vw zD5xJrg`P0&rn|HmWTe$+k>0J;$=DO5+#T-0eD^zZR9SX0_V>=@LwO$LMWEg9AcmF^ zM~;UzgZMO?svQ!%c89=$9CD~;#FSrup=TWa_`m$60ub)|Ydfgz; zsfdO@V9qJi%_8FZkLT()qko)v@8_>sO`x|Cu3p^8brQTiJxsuRGf&S}vCGoJxD^h|<%vC&$+tLEg|{;kXat1nhDETGPetWL>6HHW*+)UOyI zNpHuEy0?wG74+rV7dV`DBJ<7VdQso=t9LF1JLZ!XrdDah9|s2WzOrM;sH+>g1LR}% zMqg|RI|%W)r6rkBf1`u4(d)daZ9k&q%_Pk)j|RV#Of;@O^_}gF=$KE5tSM#0q62M= zk>7NecXKC4e`0Il3|9yc7bJ(To^smU+#T}y1x7U6{4E+jr4~$EzP5T4BA=(-+*p$P zg*Rg0g`aLH5xs~8KHPT~xd)C03w(HEO4&yZpGmNMckqp+e3iV@Yf$&0I*D?gwZQ&R zTKpT=?H%Qtxm81j;4o-M=+}1q3!SGdr#v2Ah?3LV&-MY%u-{$YGm^v?c-Yfysvf6s z*(b^Y{LgIg>!=)!HB)|``>?mQL%nLzzR0&y6N+f_7=Yj(Ur6>CWto3&n0nB;zT+v^ zhYAjT`YB-j+ku#oO-asP!=EebBVZmB>`tw^LiEFE%nK#)Dq_(chU!kA(YAF3z1;a< z3y=A5J*c0jn?RN%$zTDv1!}kzq(u*dH9olVba$OF{Ceg6(T+ov%F=`Br;q$cUNl5)tTecMuyK%q${h1KRxNntb1ZOcQ731C^wCi^X~6o!K6CsR&o{>6n6bwhRpX9^EzX85BK1Fsbxp!nAOvo| z4^#*12KJs7PaT?xcSQdF!Ru?ondOU|)m2&1Jy-5UO5$ILS)toP-d5BSUGKK^OB`U1 zmsndDT%A?98U{odX-)sqva=dLVfv}wAone#b9eXhGv{Q(oA6{_0EzG`S0=mIN}`6} z)|4blj4yA=kIlK(eGv|$D0WHV=4xk}iH_(lw%#}fKChnP$hj|i+|SHFJb2LWcZ^Y@ zC@z)m{laL7XUJFK4b)T!YpZ&u+~&ZnS?zx4jM7l|=;yl2v`-RJ65f{lOqRs=oJj$4 z)X$&Sfzlyz(}WHY3o&ia8ZB7U*B{(K%^rEaor}oo^ zy@#2AZt@cQMB}!8pAOr2E({!#pWM^i+pGl`_qVs5A0vt}YvSwK@v)jjMTZ6DaJ-1A z+%-UbOR)cHxYB_z(QY;{{p|Qf?2`#t_oa;Xr6fDMv#3sZ_{Bg>FUtS zc0hgR%ZTWBic|8k5_yH@*ZY+slu);R>C;z3>ZX}t}UiLA=7p<}-r*eZ@74xoN!$cEWN7?c(-hXf)AopTAT_(Qs4D6>ccXQq3Fc7)FfQt8fA>r}z4GCOoWp1{BG^1cBT#g$|* z@gjzYRpCv9Ew0y*=kJe*J-PZ}UXq)iN>89qPLxf zS%b44C|6fRclyVN9G-{Uma(*6eqTNTzI28)|Ez6p-}BS$lTw`n!WJ*yV8)*}9%m2$ z>NnuM2_)=cr}rMBl|ervxaA5fjZhKB=t)||(o*KbLNME-J3u#&D7Pkl&wYN&mjuAZ z!tsOV_q*{=(7hWOOnf_MHHya3m_CqyV-R{D&PqymPyJq(Y*ID#pF?+o)zL3I>LszyW>q`(fZlOc3^w&B<7aOXX5kA z_sH{0_>&s3Z*L+lVIJ%Z!QW_v#=@%GyXgqF{IZ=7o^0Xl2i99SjPeW}MEQ5SjM~2@ z${M_w5#b(djme!+9U|V5fbYOTkJoKXeGXAI=g9y>D^+AkEJ5|RjJ~%G=k-iny>GhF z?6WK-w@Wa~Cc`S25}F+1qO zB5{CW@J-(Y9Xc6Q8>GNZ4T6}CHpQI#cQuf+s;SGb7C|R~*K1 z(`>==MTz>Kabo4i;2WrWPMl25|8+R3@SLQVCjtA@f>n|Cv=KZ45I|#G*P~Xbrciq+ zIbVv9;ZLM|)I6Q-7J>TSHxCnCL@e|DUN8D`aPO6VN%hUIBX2aT=&XpdiEb%?IUCrP z{*LDZGt_|f{7kzna%kaU&?08bazB3Oo{3bdW+Twx!an4u%Ar#ucMy$xh)fs$GS6?@ z;(W_xx3{V3mLzt++f=%8lQA-P#?$35RGz4r_WtYzu1!=)Gu$Z+NEL$|hu$s+3{j|; znmmFU2NI;$AyjYc@z?ZzoiXa)2Rm2=I{#F5dyD_INy)ms*4=L(@U^*iPY|sjV~y0x zDD+E@`sLquWHr$R+_7>MCtC$*)6S*tKX*BNi33Vfzp|>EJs%f*U zoVgYO-BS-xuAd{R_k5BHImcH^EkT$s45Y6I!~Md~y;(hkdi2xmRzaSpQHW@$8y1f+ zX9m1q3N#wY$%f(n6m|`Ql2)0?Mmkd}SI1081Wi75pTVxLhUG*KupL*j!RIBKF@bbT zL;GcTve-s&L&}C|^eP_rst0u`BT4{AFaQXj_4kv~8SX!-O&*tk@8jeW9|Cd_v5#`r zZ8U$%Ws|#1t%DffC_Ax#U*4?U|26J4SFrko7{!qKXR#kyDi~3U6VBQcp#yiji*N7l zSIHSQJP;~XrjuT{Z}b6FsMoH&feILO&l;GqCmbM!S|Sv3JrCF|Xn~8A-ynX93XB^_ z{}JT9eNgGn1o;2_V#sG3GoPgMfZad-3N@ThRq1ZfttY;iPIWI>$+7Iee))A&H-!~3 z_mSIes5%ZN*-!ARp_88ga^~abmzbEiE**qw;=0H@5%XfyaVNMEC;@(#U}4wzeo49- zZI7GYdpJ$K!7SbmU#su;0Rh9ii1EAe1=lbmR1!9Q8Fp-?i`fMm#c_e9e44a`8xkD5 zz9AN%Dkj}7`dl0TXX@mm>08PA@*vm`%WZe*z%;329Jn;(iH5}J%~(1h%%&YSsgTZ} zG?WVy@V${D_k)Rjg?f#Vf~CXgzDa!olQ#rR3W3~Y{~(4V@$JDDPkGXAmb^u53pfSm z!@aQRQOWqOVQF-#MQdkE^AguKNN#~H;9?Zt{j5k$WbSZPJ14Vh?!CC6?%x>J^+y+c z()F+O)V2#TC|B0SNKS7QMDq>la3R+)aP#}%=r+CrPE;lV#$Zderoru}s-Kt-@n6n^ zgrOA@ZFv@?=zGvmDmbE?0S1}h#!}LUG{zKQhy=EOa>Q-HtmT}67JN*dEC-3_lVEP| z*R^kt#9Hh`=Mb0GJzh3yy#Cbpb`hj=b802q<>tGBC`6An|?`bQb(MdzEzcztf!2{o9(|LIOB9K_vvB@ctp1f5=IR6g> z={Tf}fdtS%YoSi=03`POAA`inxBWiAwQBZ5W4tWP-?_Oi&%b{*8U9Sy%T0`m{O06) zIDe!Q~1XI&47k(qX&MPk~6u(VRyBgePY}_Z*25 z*su2oVp?kU4?s->3c&3Tzl;x#?={bwJuJ9BcG&IILb`MFS$jaI^pD_|t0_+AM#vej zU&Jb;ufFmt<) zIx|WOZh<`4294p3zude|@jx{M6*~1(a@|N{wf+MdgX-kCvaqR$2>w`Ar2OSK)Pu_UD058>TmHM zF!%<8&Ur{*-uS~MhJd#-f&Zi3pPyTb3B1>Ffp!pqnCFKiWxys5Ez)_<8DXI73}_%e zgx0~7a9MwTN1gxhN0IO+fC zQZc`RNgc_yiC1}8;+~-tm*OG!p*fm7I6(B?BcniRyTh2w-l7=3bLM;?7I$=3^@_Y% zi#lLZ!aV7JBGuyN39$-Vcofv?A*4P!ri}*;Ls07n{zW7X5+RAXU!DU<{1Fn77+l8u z9HM!aVKDXC%b%x%{Ij!bz55kD2E%pZu7JCWruS6o>xEVhs4Ty{HkbNP^|FP9=+x`tVz%{bb-@;`8v~Xe)PZ-Sq9TTuRBBDJY@o{F=uViI^ zmFy~GG;#*bPJeRbmV()!wdj#n*${}_T(AzHKsfUMJGkU*B|_9!=!u;n2|yWN7@^)( zC!`$3%Z(o!=%vn1<6p_O)xEu=XK!$8u$BBakNkxrut1vj)3opxiBK~d651sq9^tS_ zkZ7e6hjV|J*afvZdQ}`eCV8M}2aYcq6AqrIQZvB=M!(1(1LkfEf)*Whkj#y9P`HAA zUAgUHBWS@x>T)a@yjEJf=0uHOzrxhfoX#8~kv&U_5!}rsoB^!V>jh;*r!Kg|0)eL= zvlsV#|o7Sphd~6F6LedbPg#)P#puMBO3cj zoe+BjH}qD7Sj;6H!2uxk&#lpkrl?6L4LYpOU7{V;m22SM&l7@F@-f{hXb_6DjVxIu z1Z%az5!BGyL9Nr+6AxxX3_wOv@LOU(q)i)#0ye%O)T6><%v~*a+}NF3!Oy6T9(&-@ zw8?WvB7V3bdO)3=2iB~P54aUP3HJ8q`()x;yotMKgSS1wRnDtd)lUrZ|J%IZvG!1Kg9!Y~E;c*y_kkO3 z3SsFqRjZ^;4<-x5(%LNj21XAWm7tE~9?Qd}t-QS!v(+(P+Ney7J)IorvM(`!A(jW< z80@>tua&>zisOj#R821~DJdx`DypdXHoIX}d;48R1~Kb*fmv~>PUyj?)Kj3d?d|Oa zm8rck?iHLFW4+NT1D{mSR-%)>d8ufD?O%U$Dqpfk0zH_FKb}SF8~2T`uKLd@X-oPl zN!p=78*oRJLjcUXzu$MU+PCzls%{Z9*NW#K09V0FbwQ!Z>@gDH7GUnw2S4fe>*uG& zMvdDAF5kZvm|01%Ri6R>Iej|Q<@?8vA9a4yS9g`_>*)pLgbi9AjE#<3$F)pu?ATio zyMTsAEt1hnE&5Cx!LV5F@xN5uXr?e~BXmAr2$e*)3moVg2LBJMGgaMMR*FLqiY1 zrZzSxsw0C#2E!clSe}6gGfI5bfMO&Z(`5d|-fp>4= zy-Ee=VlonL^p~JZblYdSe*?(7A$lAoxBQ#l>0zBc=CW>f+Jv!I}$*xl~#ZZ%-7 zR2=yX7{(rsBVOPB{P|*v0y&XuYIe5l@-Z43n(h3l9fZsuoSIsFR#v!4G;pLkf`^CX z*EW*}0|Nt%jg2`uLSL9_s7S{kGQQW>Jnc219DZyImQMxOW@6uZZPvY6_hJuFOey{*dIpxO|azGssfcubD zM^*?3a;NSzaS@RWX*6)mo0oL7c+w7T-BNRFgQaeC^LaaiQE3kk4+jSaTU&n1F7o^1 zuY_duCr{i3BxQPbw(kv6m8?7nM6Tl@Iuj7_OFxuU+v`Cv2WkB&A_39Z+Xmq{D^FWKgl?TLZNtgc$UPK zYRCjb`Fd>dMPdyq#Kv?1DE=?9r78De?_`y!KhWml3`CUw@!(sn9O39O33sl@o|uPyaJO8BhGvtle4Wa9CJaxb#E)Jqj23SH_08yz#BS zU#4;2Ii=b0PsMwK7l|^bPaB(=X^%~nsUU`m;Jk6OfT4y<;AI1qc|&>NOt?WL91P!Y zui2JHb>XmGLOL*t(pL!pt1dd}3Kf7J9pcXOL;6;`l<^o$@QPE5V^_1E6SY1eJ3>z!!dJxTXm7v6@ z0JQ!;w%!CB%KrNUW(-DS8B$}5u`5EBDBBv^85hi2~kKHsx`&gYz))QnrXyDR03j}I!k_8Z>z z#*J5nlF`AP742nVr`h+)SCmNw=Hbfn`Q5Bza1~`QMjGQ91@<2 zg9bNOf9e$s1$ur5W!}xDdt2EpDhj=dey5VHm;qRy6JTxZ)W`M-`=91 z_LuqxADxIjH5PG(TH)n!$cEUp4z-c)hG87do*6Ej=@gnv3 zTW%E%E^9qREqh6S(G&$D4z6__l6rd#-ViC`51}f3b)9)cF61mZ%poxP7gu!Kqy~jy zjqE|$D4#Yh89A#z7oJ(P+CJ(mk>xd0)mG7Q351Netqb9o|Z!25#e9?MR#f{Sm*?H7J zL)&6A;GBEVXS91|bD$+o6WD&X)HEhI7eTwbxpd6s=DF@^!%<7GKfi8TsufP@Pg-o= zm}<*v*%EULBtNeBmcGht4phI3n&64MGQ7;zA?vi=HZkvgUl8v;_$#hdb-T2*Z@D(! z>7!SpQsZ!Z*EQ*omvjfWihNfIB$yc`vh()1{K?4c4f^h;8G>re21hrz=$Yj>K;d(W|*`{L(KuwA6JX ztLj~Ic>9bM!@ZYHQTcQ&dcAO=h!sYJ8@_gQ|wjfKrJq7B5%Fp!_@0e;=0W<<{If zlk3i#A6GRwTJUUoKJM(@b4T*)toWb2@@teUz74X_5@~{jb=t&s;5f5d$i%#5?_6XnzO$)zoAa( zR+Zh;4fvNQ#wlK^{$0(Fl;HHkzb${?@~N-?q_@nIB;LGFrPcY5)|9S%Lr2ZJPURiE zmb0g#O6=$9y)uoax;Vchf;AkvcMhJu`Z`b6!fb!0RCecxw~OB1c&7M25O_BCgvd|lMK=P@BrEB)fGWOqRG z)zu9N6;r=fS;JWK)jMzZw4u`io{Q*-d7b|>{$AGT?Np3-bzMTR#32v+)cWgu7hk&1 zb${4%_LXl;l|kKBtx3%HieK-PkA;+98nbRb7h_{?-TMB5YF_H%-89{cw`A`;qkgXXP$F6osyUQubx9k8>hBi zTpSz({50uh7X8$72_zGQE_bhR2Rl{BumiR$vCI|?AO-BP*hE-goo-Ca-r&oQ379DI zFr;2`^0DBTmww6Y2R!VSrLx7Fk`;eU?|B?BHGaA7ymrt$=778@gC$Mc(^8EeIl<`~ z`A*i|XwDi=&09ZmPghEZTZdi}`5|{K?405G8&9d^@mHny#MX;+G$;A;5-;mE2W9zDW!^L`VLtk6s2aonYlL2?%|wmWF-d^olvX8Abu zpw<_4<4yN#oPLbCf4!0L(*1eUB4m(F8Tz&6zungE>h>Ig3!o<4Tb-9sn|E{uY2!t z@pk>0Ipe3Dukyb#08Xue*KsEiM3K)Z`T-v}z)=pFa+79(%8h8)`gxS~S=Hi6iF)QAgJ%iS2>`o?Cy*r77pk3?J+K9YUFc^F}hQUz@Z;gHODwa%9x8W$huAmt(|>0_U)`@X*0P|zBQba$#(}IYx@4WC`1~x z7o*Tbfstt+l}K6pAT0%Q8=2xZ2B@5L4v=b?lmQER;qc79wh}N=rF!dsm}uvizY=o8 z(q3@GBMmrtia5?v+Lun~6Ro%N>wqmT(XLc~sDnc94GIpn=Oy2*Jfcj2^^8H@Uj=+8 zDMrB$2TQzuegAf}+XJq{Hn|ATypuLtw^sKP(hCc&`$Bnof|>&|0x^dJsygq;nefk*sPQ@biH6?gVp z!Di4g)PADoKW7FQ9QEqT+^SqS;?TkbcEHqitl|yjvWv)FH3G$rh{sAI_D>u^#^Nd| z@VZUCA`BQHvz3%V1gMj&Uotx*$_kJ}BW6l)7tWs#gmZC2dSB zvJ_tpG>-AN{n9J2?H(i`ILP&EU`tNc{Yk1jK>972knO=3W3_&^b?v7@Kht3 z-X4+l@91D&0FjCgLj4pA8n7}?=xo03EyWgy_AT!=_tl?P3?9P+#dy|P&8}o>-$}E5 z(!2q5nk^f7&|87MzpMMm{)k*)j1*iG;jZn@e#kKda)FboT*p>-2Tr!#{25F)S{YFB z8n%tDZA!ycSN?l(e_diiIBi{}tRtcEBk`X4ZZART&4z_ZkAMk#uQ3i+l` zqWZVt&rRM}5HB};uP_md2`3;eZx~)?CB3TFTP1zjM%i z%tsDW7hmRO>9x_6r1$#u>zt=-Z#eqA%JdYJ?R}TN?11xn2~DyGyaAZS&3Ot(0hMIMfLWfTW-!;>(fEYK(KlI z{!aH#ap0}Zazl3nVv!;!>ig}iiqx{xstlybiAiIHK;bdBJa{u7o-EZ-BnG}lhIZS>cb|_m@TwEL; zfAr%jYF)iC3ch?{7<_}eB>|f)ne*M9`qRUNboEZ4 z-@LsJjHgv%t>TYK6wqR2o(*jSu^FZfChNDoT<*#r#AhZk1|jDtGb1Pkhn|JVGpQP;Kh6>)1!I8M zw9N4R@(eIRg*fPB&AmN}`+zmnI3D(%&fk1-d3{w{N!`5vu?j8F$3zUx78s-D#jY~# z>OMsR&4)i=*Y)$9K9~w!lIS)2(Y1N3UF@a-XvyQS>_V9v@u+*7k5^ewzEgB87+m}o zGqoqv^}%k(UyPjx4jj0gA=qEV&!kf*vQh$6mwQuJS11dwP9P5U#GajW8L<`w2pMJn z-W}NR&Q&&=iO!cdylQBaHA?=pe-+%pI$Hi=@9J1^lLULa+|((_WzRbvdxAd#pO{Ym z!|69ifmNk{sZV{RTJ$VxKP{Gn|AEzixBzWYS-)Jv#xWbJ{%`QJc+kVAcjrZ7q;Em% zx~>$z@bMH_k2#PVM*{KRDE^NZ-J$KP;{IN-9LPFtrmT$eBt+dxBk9j^o$v!y2Sfru8KwE9{RMFAR`W(&g zg6n-;z+U)rCs&shoTW3aUNBo%?aOJzV5`ND*!ok%;K)t{(uX8i$BL3emmF0}`T?c8 z@yore*(_)F@?!7KtTqPg!%6yhZCw~&Jmz5%8F2>8@0nQbZe+#;*nTfR5kDO=?+1Fj zqrdRAZ7t;D8hBDTw#3&=G8#64Y@zPCMf|I($W-gsbP@?ch?XPGV5qBLiwXb;-jZbj zhw@K-$p6b;AbZ8}n~(858FS*+Q%~4W%YUIES(0!K=acL6=p;TOmr;BIl>@Z9spw9& zBmf)wdTdL)r#nNR&N3fIVm*AZKXM&C!{nWfVz9;o^4m7n*=kVL=4mP7xlIqx*6x{6 z%R1~4hY=d-r`G&=)30dw>*{?Z34AJLA#Z5#9j4u5uy})PAoextdR zDIOj}y&M>?b9#ufL=~rl{paEv@Yj|d+1emf%=D@>$DK>QFF@eTy55!eoOwCt#%$KC zp>q7=ANT}ahh(g@;B^*SxCV^nt=L!pO<`)Pq`<@=GI!XDDz~#0brm>z(tK`W6c4q3 zhe89LT*tooq{a}#Ac>Xn@S5|FXQax$TJ-U-iAp@0n*4%{FeK(gQoQpot{V-*_p88OC2XAmgMzG zojgwJ>_{gA^HPG_d`SGA3TyG^7R=pwcA@;&+LbGcGUxi&4p@ie^;xzh!+v(sTfb?YO}d>-#u1stI&T{;$K?GFwNuc@QPnttfdGJdw57x5Y(V#b8Jt+H+{5mQGP+X`tKhr zLlLx>zL9T@VBN>>+r=x|ze-EPF3_$#0_V3f7cPg~qau($uiZ&szr#bRDG}m2`vQcM zaSt)}0T999mdZm2Aem-p(4{d-(QY~3_k$cE?=)%MjBVO6{ACCftIyUhu^Pc z;Y|1SZA_S=Ns-l&)U3Sj-^(>1qXwrd z%@K0{^zYoY-qpMtfm~3ll$Djy^SRG{zp3<}yq2%zX1zS8aa;abRWAL&@BzHV{23g4~I2CJSjHFsV#VC4hCsy_B3nNl4Ri3oeK z3}6w}aN^bni#&__W{hZ*^YpL%;^OzGXYwT63;b9}5p?7h4E%8=zqI?TiHX~2{mZ~W zhWi_Dg{J)h7CLl!pZb6h6nP*P+mnz7b#$l$V*`xN8d>KcM}7l{(QTqKuUG>%S?vq7i}3Gqx;)P?ZSH?$|PZtYla=^JkMYeq*u9XA@2k zxE8STJ~8nUR11@E+XWjb+iO3j0{whoJyb~9%yw&BlGj`dTy%M+Z@ZkH&(YagA4<-X zdn2k6r6n@fFF>B)i!ShXy1(}+va6iP1rmH9ci|yIs)Bz){mQJ#U!bmp^6V`e|DK$ z7P-t-u&C8-J&`)w*}3+0`(lQ};ru;0Rv~{`)HbB;=wjZz&>0d~2x~ZT@bO@jwgUIC z4MVST387Hjk~L_-ZqRyU?yPz*Rl&7b5|hVjt>so%d&#HE--4L94;Rf?=lKh@ws*B(^iRiZ){Z9nL_O|F8nLpuvS;+}uItMeKI<$s zI`OL?#vIwt&Dl_p4sQ!6DcKtM77UJR9NuiQG8oQun2Y`R+c`yc8Pm;!+aJr_VGX9p z&Kk^*koQFF4cW=y*hHb1!EC9WJ>F7i-+9OhUOjTz(b17aN_h9KYI4c#bNu~gHK1Jd z7T^CPbCr}QMpIZ@n#7p?`A)FpS)ch2pZxNy*n5(EEV7~-PBP6Mmi>^D9vdKYE>}X` zZk5Z$CWRopQs}-0A3x8)d6H91{rG$Fd8ZPIBO3S4rfdTw#u7GIFyn5X>$Kff`d7`O zufzGni|hmU+}jo2-n(UZ)$Tx7mjmw$zZbu3>zwL3YL#RCzW16sr0bltIqc~h@xi1VXeKV(C*$W)W-&>f>z~Dc*pX3zqu75K;8I^-i@kmk83htgJZ*@a^bu6 zSQcHarNvm)(ZB1lmhsaIFBfLrg&9zi?X_oXGSybr%UL|{tS?XH=jUrAW`F){e0wD8 zk)o|gsqwp3C+b}NOM@ug^nDeI7$Ox4ckKzp*R&yEoZ`HR!RVp_??J`#NvxUTp`OcU zD$PF?6%~E__z}M3z>}8JBzmv4oTIzlE%ckK?K3uRns?i-nXG>FvXHPh&rA6=q~Y{r z^tEW3x81P#ky*3J6y5DSY8z!&NlD4WGFQt!cfShL+@d?yPTS&%9z!`1=H<}@oGSTI z&7b9;GnqYkqd6I-7nX~P29icsSWtAkjXM|SciY)o7TPtu{e6xO+P|K)>I^s?Y3eLV z1Lq=duk|_f{qS+P@sH>YriYezk_Y+Pq+b((imZ(u+2wz#;vEjfI88NZB8a|lYjbT) zlm4x>4Q~%Ok2)3V?e5tLlv5RU#d7kLwx1V#Nfl;=8&@h>=0XnpEh2~^CR{>&Sl#$g zMaMNR^N0?$jQgC$T$jKs_bV&{hdLo*kcf@a6?P;GwTpbxBut~Ze$sMC;r^O@RmE^< zlh()7(tnv9x=0!xiB@3b?Y%qw$A6r!e$TE-*APuyGC|r%%q!BJXtwD_D6ecX|w-f{+y@s`9g3~CIu2YN@pI)fVAy@?7K z4xWJW0&f%Nh&fab=ID1n?@+Y${Aypv(y8%{bhlgBAn#Cn?*@4c<4Lo&D&~y=?r!)s z*YzVz)EJ>SZzFti;dK8G@w>j z9T+ltx3Psq?^45+rUrF(ojc~2-Oi&OuahR#;*N7Y+@(BBBpgNUDe&$J2ax>eMiiQS z1D)PVdvB`in+y2{Yxxnlk;mxFo|2`&((y9|WgmP4lw#yv)yvFKGKu}~6zq!R6>4St zMlQ(5UDQp`z#FWyCSZ-Zu%GvFquM4==}H_gIKJXA9C{>s`?>ICFTFrinnm}Q=cvjy zxiF{aWo9{qTAXUDSA;*^QKn}bgvP5VLq{^?R34Rf(M%zqR+cvDUmc8SA{hAZ2q|63 zSeQ$ZmKx(2RyIFBKm0<$imtqD8EEFJsKD&l5b6UIl;VE zTe^KZ$2k4*^DKTU@(>QpDdwbz3ojYWa!4^a3C=W{0W8WKdFm+LBuUc~b2xABM6|Vy zjatdo#}lp6#$}DVl^J&!sw2qpZc9lJh@`CuX1|7*$rv~{&<3ATR*8 zk=YmAG9UE<<|tkl8XFj8q3n+zhZO6tsIcJipQSei8^IZ5u)?1-ld$f#3$Rg*OmBho=62nBUZn11cG@EwbnGy~b%9^}jPp+aBQO1N%LxX|YOi^F&Q z%03&d2ftaf4=}^~^5#&#Y4;q_EOS6*PKJerrPhZS-bx={3R~xAjWE4#Ya4RLlV4rL z6m#rkSsK>rLubDez2GOQfdLnzha8IE424OCJZ_HUlOh!^!nedsY&M^|Gfj9C zKS`RbktnUGDE5-mSLK^L<`|K9JE^=}{gco`Dn%_K0fqR&h>VGFLznshMdUG53aAEY z2iOBUP}FxZLk{_`wZnC7DtW4azuH3}t8gJ8B%{s@VG zCcJs7XRu+coI6g|-H!@1lR)i7^D6If%K0Er)<_{v_z^pKb#=8OfJM*dZOY9PjMNwx zOm)UrWfTjissT(qe20;);uNAD;-u+HNkydxv5a;P9V+%-S= zkYJP!4cUqnL?&%PxM=t=7pH>*;{)fycK*q;UGOWwkuG>#x>d&jE$t#7X%^H$97Vtu zq=x(t2Z=*$7{{le!W`+?XDw+O7 z<$vDBiR@DFbAKA+bhy(l*41OgvQ)*zNmK~_A)yjFXP;O}yyRQ*VWo*|L_B7XRE%bV zI2u+`pG`UM+*6)u%y}{K!cvvMYhCppY@k>_X`eH(G-*=7Q;~{@*v8LjR4yg~+agfm ziV=z?`b+i~+y-T?amr<_1%;GH`N^5{gGH=4QMWr4`n=TOr=s=+(QugMf8MJC4?7lf zP5}Kyq`#H+eW&egf$t^mmYxmxq!V5~i5>@6+$^A4z@RSsX2Ef^VWh(opdC1q<{G=Z6?Llj^WrhW1x>V_Js68ns`(uJCA3}^Dt(1~=i2`(&D zOqaul@(1d7jB%qs$g8(z;!$SL-CvkqpK+ z;*kGP?;Rwk1V;GHB?Do7_~cP?0ixDpsMb#Vz2K^RiK_9y{?B%_?y3&dgYfhImu^+q zzS_|R9NY~f0c12$FF0G;#!aNggEX!osok~<^$6>+bNDC}bsA=9VHHaH@jBySrFxBo zLG;iCE*>7MB4X8CBtN=91MVHASJ@%n#HB1@zQ=*A_OfJ2DVLm(Br^Qe1ks-q&qxo+_>W0OKK=t6o(W6 zr-_2Gk2=AJmx@VeH4tHc9FXQl?RYqkT{FFMu56FMkJJ0nS~^AV_;E*b!V}$`5~A}J z#L*o9-{3mJ`phJMWzYX;N8?9H+$Z&(AAS8>#?Iwu-Oi!YgEYe>%RJGi{C5R{r05vn z!PE_eeBl_uA*lHrzXQo_AVp-EO3yG^CWo6+w~T9}K1A!kefY+F0MQEc?0`YmjGDn*3=3|Ijyi3iF$>~gH`7EE12yK^NkV*sYmPf@+F1j{1hC3R(jXi-P zRLin$6Fz!{Y-$uv2Mv+siwpVKd7k4MgmJi3qXSdeNoy!eEc(1iPHv;%5boE<7pp2Y zJKUc50%cQ+*Ij>&7Z>eajdqU9{I`B04&#gXS?dpnCsFt7Uo!d1xN0QqN6}UB7ZA@q zB3=eFJqL$kLU6sb7LeAgltiD*XRCPc^GnHMtJadQjXsuUO{&x>>W<{)OOZ6_%ZQCB zhV4$rvQIy`eA$h|+BnzFor9|8gCLqQOcP>ZI9}&nbJ$>Z2~qpi!;_GWzJyWz)2|;5 zlIH-;w0Jrlzuh0G@F`D%N$2($Dzrt5u~~JZ6Z~jxbQ;mg@$i z^*yNrks@utpq)~psQUk`;uh8@qeFZRa2uLpa>|FFdK9TD2_`JZW|d4gO|(=v(SHi} zG?Ib~OqeB4>}={Dj>qAzV z^M3#S;LD<(q1N(M@6kp`DPR(9)05mBr5kZpgo_z2<3~JNywCeBmYFuvo)-bdnL6Tp zpG5Pu@eN&q4Ieeqdq4b{ickj)Yx;9s7Qy23utBbH#H_{zy z{ov-4%u$e1ZehBf(Lz|jq=!=TD1<|iL~!w--d-LYQ7;GZgCK<3fofF!p-N?QfA8=8 z1FV}XYqQOfIwnb~$IkFouWYpc<{P8Vw~`4;xgLv!2TU8J1K`=;)(2mDg>^X#DsHF? zuXM3(y@Hs1s=FcsS%?jRUE1*wn?--#vV{r^f^DDo7~m0>;C;mnH4|~tFCdTM%Ca0k zte|Q^$~2A$d$n;vN?5nStc^M1>%JCRtBYuTJ4-9(QptXMgSQ$9Se>5MqY+$I9%Pe; ziY(->tZ# zG0lf~$xtH9_V+Iu7&#pz8bF|=LLVg(jPVAOkjN&`B-B8zoGj!mX;33L_L%XVW&Y~b z?eOi1??wUo8K-C5vnsaNe8D-JmwCZxxo}dfUDb z`-7Lb(9Z>T;$Y*pNuL>c?(`JrnZ0&ba_Wu2xh9rU&ov0%F>mik{dNdktR8X*^jTS$ z1iKm0BWCe0AGM;bqN%s%Jr5MCeoJ_?M7x-T?yFU&ld5)jP(a5o@}&0a2K7ggBi zHHW(NTEq);?076ev^ID(yyJL*cJ2CcP!;`(A4+3-I=VNq zyH6$79K5>TQ6ax^p=j|}+LJ;yxU%a&=v2^9YCG?(L$k>=@m(oOTNAVUy)LIzwa@1{5WCA28gpRWU2??tY7&PITeumxOShdOV%q+OXgYpVDFAKmA{)E_^EC&?y zuLP%oQ;|29J>dfbatITP^;m%DwQxUa9qY~2;B-B965!;=Zu4ZYI8+*SP= z2uc1JrxX-!3zz}+5GZpsA)oqq?$KR;pXYc3J?2@fUR$@#`FxGvYkyAq(vIUDc!a@K zid{dq7gPW=GuxGyY!hoL=S18O#S^9SC%imuBg0vVbr~RsLn^e6?g&rtcrZjo%}+pT z5I+WboX$}w7b9w{0Kx%p7C9}u49Q8400m%zJT?*Qn07-9kFth@N4n5+PVzD&GK|d3 z|C|0>n3vhd_RpcVrm8BWgO#Bk5=5M#BekG7*E<4E0V1q@8tb1Ea+JI{k(@bjmv%lRbz+A0&L-fXEVr-+>b8M1uMw5Y2e+&D~G#owY%w~ zDA{1L7UGXGm?p6;1C_2|a;M-rw@^b4|NZVVMx=!AU!vW}TC(;o!-BU&{qvSS)SOBP5*oPO6CPB{JO;X#~q0DMth zik@;HHOQ9ZJW7O^P=SCGb%JBkSKj*)AOFKlhZUJ8v2vVJLV#6Fz_}+kJY)xa&N~we z)!2h91fRoMB59S=mH$!R^8-m2$BCYs z=EMQets|&a_qa{WbIs#7FfAg4g0i2lwSUsyxI4g&5efv1z~I0WnV66hgS5AgENHul zJ~+$VM2vjJW@;*6@Y#q<_XzxKK!OPkI>bvo$^7a3HY}CLpuquPUR5~bu0?_l{W;em zbR<%fP=eg(LM1k8@6!kOdS>?XGksN{%SqpjKE}?`H9T}hnJ8k62c$yuGFcgX-vRW< z=qRU@fj{;gp^*&^_Gz{~EBZ2mAt;7j=}`9BC}wLSreDDgc8Nr=pGh#2q@M&pyW^e& zENmkfK(0=7l8p5y>g~WzdK;fHDEof^XqElR=h_Jo3X)8SDew%kzTm}OJHWhKXje-s zZO6L^B`C@;fkesZD?Q~A9g!LSaC#Eyj%J7Bc3=~8b3)=Vt|+?nE(O&@!|T4Cp;edO zA0wwajaoWKEYYU=3w#!L;MTmWyoMwuqj5fo=O0zjWgJp_gyqy@Jm)ZTjh4Y_jt zO3kqM@SJEPiHD1@E2{P?44lvl?bRrw0?}O~3J++wgE4)5Cz*}@7rNrK8(qpl@?r}n4k`(at+5`Lja7;k$hYX3Ua88!A0KVB|930EuQ|GQ6=-S zHpN7rX}K!ZqV@z*4*J?B?`?_VK6u;tqOaTrV$58}v}NGi?2%(o6hEtxAPL`A@KA?p zCnJhES=r)>4rMI3ovrrt^Nq@WE7oh$%-k_tyS=JZkW%7O5pm-t6P7Ubyy!S{s;eTn=4fJ+5vu5eA7H`J82SplmC3a)<q*FIJWKksIXiS49~KVvz;VRn{$cni9Y)5uFZ#e z1p+Tb93Dq=Jcya3tcACVU)|p_1n@z^{65=A8Op{D@h^U5nkpP63>*vWFX*GvF7dz|56cIZTk%{|ltO zW`fswdjE$>cYn#;4{JR)jGPu=pOC4Z%TY%PdwGy#hW+3H%z0sm1$(ff{1Mj>N8M`k zKaP5=9iChIbU6|d{J?mX+!n_IO4p1i9R`j8j@v|n1M2QKpMr4Jjn+|s(T;fT@*^4} zW$fV>Q+q75U;X_g)%d)x_P_i9lA)By0BRS0pOf4|!cfz=+$3|Q znraTTaD@!S*ny&?x-@V%Rjt{1%a*OU%o_8RTvqn=suQ2byniXsI}S*%i7@B-FyXGf z*lB%|x`iu`(m=kLlgUsR`r!aGrxeLKdr$=}AkbehJvKIGSF&=a2x$ch9(Z<{K;j`o zhgZzJ-^g;~``DOR8ZEa`g`(AdFwLMK1($s*yVO>3c00vr@+JnvGBf-2C`Krv@JI#kc}~BW@aJeGtWRaAN6*!`m)j=#)Tqx zRL}$x6Vg(E*x*6!>6$IVbB7)ElhxS745fh7NbpcxDA_RV5ZYCg!sFfB+bx7hZ;iT2L(+b((0I+5F9yYVji|I_^rfw`*>Qb|Q4Ty*>~0 z!%s}t-+8%Bw4}f*25tmeF;gSujRtsEyg5N;5#U2C0}@oi%f2L+UBw*JL^`do?k1~f z{`>ZvsMq2weM5%nh_S8;*IHDhI|{PmbcxxYYU|KCcc1(IL|j5LVk77q4K(EdRF;Wh zT);eJrj*BHD1NfADHsPU>cfW*eXI4(p6egtH~Wp^44cXxT(Ldbh&j?kOenfEn_Y0z zbD&dgJG5`}PhRQfRH5^Xq?D8|VCm3vp+IWo3z**1N32~eTCr(uzSC&@sZs_T1{`OW(=~`Op z(fiAPVBL+jjOcE-TV})%pxIzAeN>qniJHnGL%&3JkfO6GQG<$*Bv^0UIRb$@80j0= z)ztyhu`BoH_SDQ~=HlH5x*M!;$WY`+?%G(yw|V}?T8_=k*kn`XP*Ya`YynNY-O3ha z(%UN2#;tq{EA>??NCrj;E9&@%`eK(E&~<`v|L;5he7e=`xf<=YS}`+oDjRT>!#qlL zCNv|43cR@|NSOu^<1_#&O>0`N#h1>Dm(H(EX4!nW$lG@7{jdY(YbXc|l9XRJF!v>d zNEt{T`jb|I(Z%cmK=aUu#rl8AzdLH-HlDP-62H}~ue@Xp2I-;Lu|Qlrlj!mXO4h%JU2XC z*lr5PB}2)g3>rmMw*)jX#Io}{)fCVyp43=9(G+^v+kZ~UcVlhPr#+2e^* z)T*oZlg{Q`X~t|KjjUvkT|a5N0;x0okq%hGQ~o+D8H8mC{6D+RHaiud*YT})RC%u9 zw+8jM^7__h`8K!o-NzeLSDx66i6gx)zK!p)aEy9dZqL+nh^Jwu>y1 zk6yUG==F_idzf!?Oj~U^C)@)}ZyOYDRj+hW59qY_ho2ZR^D4W*!nl~2n6oJ#i;B!f zSir|Vhql4b*5JE`SwFj8y?rfuFs97w_t!;ey&rv$-~CNVVPQ?t=dAq+^CzW7^X~8; zH>Kf|aWCeqxFc$QOr9K{qsq#^+S~sw zWqEDFtxIjKgm154Emyugm%pjH5Yw z{x#ZVxPSPy?rYVPsBf8Htm12s9QOb~_vN$dF~RR^_T|lhM-Yy4k4d{I-P* z&nNClqgkgjFMhC|>dLj;7{G%Uk+SRhxc`Z6@#b>3QI^Han3#*k#t%p6-mpVNn}m9+ zz=9UAqO_1y~KHio>zA0 zN;cqrDC%!eH$pEF{K~5P79%KH-u%GL%(ljit$=nC1AIKJkJ?60XF zgB4Udar^iiHF!p5o0IkMEDHDVAdgvpIR&nWoC8QVYTiz-$q5=+w=Ncy@gu2 zJ|w|GTFLNVPh07av-Ga?{^mUwT8_P@280X?TBAhI}Vq=P#y zAA+!L|NLuqweO3XR{vu*?xzk-<*8$bhp5S)FdumW;0BOLws<;u8!pi3EGBkP0<^Xl zy+iY1nJOsFN-X%h@K+Zxn_;hk(3{cvYQH#h$^kH@Lz^dL82^USl?o+-oLkO?IR-d4 zq?Q55A8B*tEVB#}NW$bV#gVFAmNo_lBx&8#-nE2ZE%83uX(BT0&`Ie^J%?Fa)KS+k zq~tM%j!@Yb-Kwi_QiX_%tc?dE1|I1ydF+}BqAP+tfMDPY$QP4zEOfRAxV@k!+0~I! zfJ%rqLik-ndESl%z#q0EQ#(?prE=Qhd%_xqZ!>$xsuG@}4LK#z{xMP4J8^+-GjXL*B!)yM`46uAqWJ&+MfM2-Bw zJ3tt6gpS!T!h8OMlkr89{s|hM@7%9wQ`iUQvs$fZ>8kiY^t%2*XH}ZoPuug86wGWI z*Uuozp0n^)UlZiwa9@s;^`&D3sFfST8QmL!{MEL){%7FKKn`NS+!xo6rW@#DPZju( zf+RuxA3a)A3ZS=5bLsN=&TxJ=K)~CNi8NzK1Li9MrH11Ppan1g1ERnGDQx=^fayzF zPIK3L(*-&ajrs+MePPT#!@nFw#v}l9@b_LY{%RAEl^8Hb z`>~Ty!~aLaLV^eQ0uIFFC>1+zMnDJg#@IU!AWVwX+1&zZCISWm|KxiS^nVDIgMj{v z2vW58_VPIBK+Y-wI{_g7mR!9!@@35kRQB%dG_HAJB}q{5(Se;YFHsjP2$R*&hC;ix0%v-wx6G2r_^8oWx>Mlg5EyBzsO=79_XLppeqezWu5Fn6*LNhV;5ZH})bd8p ziyy;!FHgrFj{XuHfkuKS4M9LzfyGiG3EG79T#rC${r z-B=>Kd207UuoP;>Oa9|T?@2sW;--lKk}z)k#R25!SmQ*}MR~G^TB2qO)V+8!Ng@^M zp|WYt`YGjZqJI9T{p0WND*$!dxGe9m#?rV|5>qzP%zcGOGpnChsUq)REkM?+vTTAsX9}(l*E_Q$1 zByhd|4yglK1jZ1J%;j_nR!C&?>i-maJW@Hhwlo{ofL5krd+vBCQM!CrP+gLyfPnfd z(~;~FM@}qByo&j2m!+d>I6wKT4Vk@nVfGfP`x%iFu_Pqn-d8}M>(g1U`yfh;lSWvI z=lVvcVbf7MTF`L|Qfn#qw<`s^#}=;|ce3c8m4}L8_~&=tFU0-_mC(n=?q+e$5CztC znL!*FH$XF^~ zP95TzaHa>+B;UKREC?W)SvR{+-d!TkVC%_Y?OBX?eUU7%SBoaU%}_QG0A5`gdarG_5hqRo(8Vm!RxG)+*2jSz7{@zDv<5 zQNPVc{3$%R*DpcgweBg<+JgM?Ta*g2S@S*`*4CgUBC}1k&w@S*qA9j{Q2L9nmaPhm zD-Rx#ru52H4-2dz#0yZhmVrBM`x>IUIeB7J374Vd!GJ)UH~=a_tJBKnJD+un;sc3G zwMm=b&f_%DIUH^BlMQ#xfedfRU@&JdL`(^wEdY(`d#<3)_K>?p8lQjf*8Y-ax)%Nw zR(}nsr}E|A%wouyP-fbUDW_{c$)OaMk)aHQ*6FdkL{E{{bf~2=q{YJO1kD`L{`#G| z-~vk|EY-eO$$ni}62Y1b34Qyv>3NoT%NtQmiTa|qI-kFUqNH!QHhBcme$#T2TXBW# z;-h9*U%{gC6(6U<1Xr*cB}gzMtE+pP&zNsy4SL)XpdG41)bh7)18$nJ6hmfv5ht9j zy}b6iVB%$J5In=?*YVf645Me!P0&?_8lIzp$>`40_%NFvr5(!J_apnOHkeZ~Pv`dV zAPY$s+kr#0kB&oda!{!r88GB`;Kh050vyQwuVY~`deHnQe0HP0_l~@~)W0gsf#MsR zQ)XjtHev+LW0VBm3LS}$+UrT?`U)CtV*n;Dzx8d@g)$+CVsWSD7Lg0FO{?3>C0bMi zb9F^bf}6Px4eVTT7;B`IoZ>j59;O}M?-P~Y7f*TD;{Gy?_mx4)>ESqDI7Kz`p#~a> z(`_BFXm#oo3CuEVE6953K~Ud?_vh}Y4tFmBb_QK+l3_o>jt;%!<9<_|7xMZOetuO0 z7aE9Rf$-{aq+Db@Rr~tb;-%{unkcJYqk^Y?ChwW(a4nf z|9%%vHNT#8)6ZEEMCrJ7)kmeZjM9MQyjs{CxMCDwtA?hZGNU4+~$GnGQgK5fwJtX>KwL5VLIHV|s;}pPBaSd%oF|40; zo8|f63Y?*HjNrkPB%wkcY#icNkGlS`t>h>fnc%-1cnZyj?DWjO$2Ki4rV}$=P>wjZ zX{RT(EDMKs5GKc$)|@s+HVLrKb<^bV%}F}OY=$Z?hu%crQuJ{z9Fu7vN9kRtt{6Bo zmw9HUi{N}cl-cHNB`1I|H~fN#x)g6yY)_$GINP7toWyQX4#x$0(;HntpF@6$C^<4o zoaWOfBH+}{#I0UXM5JKr)yjwI%it@jOXJJ=jz*P7L@K}9GNi;?(2h2vGf-eb##~<} zuK2SV`>E?YVBXys(qQqwni%;IP@M_A`=LxRT=7)KM=X!Xrj;e5GKU(x7&e?e7b(B3 zD$F7RqlZ&xSfGAdMjUXR_>%h>8<&Cp6q@B|*I3pMotpXPLIUGmM{By+f*Cpot)B63 zy&@w1=fM>ym-UcEQJAZls2E7K&0ZnJX!kiUdUTcTV)4I>ucDy5V zflu&l98@VOGEk+QCqywtxJ)kaE;jf|7Z9ll*;ftia2Fw~c+r9v`k-e=VBoM93P)yN zMDE^StXJmr_y%oQ9Z6FQXL;%3MF}7M#`E`O479W;S(Y3~1 z1VY2|1*qyoD6SFQfuh$IgV2T|Du@8C{`ES6@n4#_{j$aBKek|R`aHyUt7|V>Zp~5? zXkG?}J49TiB(Lq)iPttZHa0g~TUzdTlkwdj-8Wf?7P7ZoBwGz$2UqS7{?(>+oT?Pr zUVgjticf2%I?FRI-abtup^4KsH9nhOX(-96Ml%+$m*|x9ozkn6Nh|D(?qX- zd#5{16~pPs#u6G9ljDC%O1%1~9x5?(Sg%4YG$RI67kF)cI4 zS_fPTleNxV!a`SYOT}7_r$npFP9a0>Ct*jQ;os%<-fX@8_IO=gozO!xvF+@juG{cQ zQ9@<)>_^Dwew^$}?^#l|pupkiR(fkPwC)#fx0- zyN!)iTRg1;TThAK)rR zG;?lZ3Gv0t`%>6yRtvRuXvSHL)g`ymtZfvg%)HZToLw*OBjtR zd-!=~ROHD14BkonuZKE|venYa0kr_J;#3sKCab&|lNu8K^w@H673O(F%oAH9!#U%@ zyE3rs5n-Vo9N&oAshMZX`c>Y^kq{>7kGiJF zT;1hgkV&lJ<}Xg!qDC7Sv0yiUR@c>ARX z#*hpPXG%5aw&&IdiL<34H@$W@EThD^L_`?0OF)!>Pb9CP&@j)(FY%978qxJ0gBC5dPOyM9#QHI~a*eHpE zF>kkE6D#HKOY{IU3b{=l1Qjhz+QnVK2N*)f88E(JRs&eCGbxF>K{Vsw+f4(?ky`?5 zp6kAOGYyiNk|T0Ny$XhKgqsn$CE#%R{i!=5@rTh>+jPk!VC-I)`nF4r7`vOJ`TwqV z^>CSApFI~b=sj6Ew$zk~eesLj^sSGO{brE<>YR3OK8KCtoMPg47Op|iHKTO`8&VKo zG_b60bs65ja9__xb;6*}Nlv>G&V5hr`HPbK2l%ju?(MaFAe!7BGN2ysxOC|RY!r7U zE=@oNG_pg@h?F{9K5?6KvDCnAHQr_NJ@uU}%A0tulOjc%u`y){5e7K_nQ~Qc`NIrA zIJ0+NFaN-KKJT?$w_-J3_*1cK-YPP1<)tioK6qTGk$BE^cf{JKs&b?)R`A2&1_*v& zw5WNfz~vKmTg7D9cf_D!PT!pZV#r9eIHIw!$iBqDd-;=_$#7J^U8d~?^mx>+IVVF! zY~wX^Da63LN|bL-DwGe)l;M8VD0qv&k(s7;Ks2O1IW!w$3zPigP-x7J+a|;jq ziC8x>=H6y|>p@LXwm}uchMKR3NW5IKn)h>A^gT_!30kRP*brt!0NyiFx}>F}-_kPR zId|wI`wP!05z%$bJVZw^qeg$JR0Y&8J`<&4pDKKHnxciPa&owYgxE>VzeI#Fpvc>Z ze`GrBtW7_A7VtQD?a@3VCkMx$Vp~Z^V?L{aUN1!b9*z9SSY4p9^bVC=@`%Nqb0?ys z#stxQtnjqKVV6y->eQ!DX7OJQ^{T64Tz2cev%Y|u)#Ei%Umq>reJOA-Ob3tjb^Qb_ z_BbI|mDfdl8n)+A^cUbWmX>UCwdQ2v)VZ~Fb(@U8bVQ}Se;x)WazBs*f~@Q0`fok9 zlKN}~8OinpSH!xK4szw`t%8^3yQVCkk2Ebf6Jz>>?P)m5`@};n7*hBI@G4xs#?u|E z5&pCS?q?DswJ#|jc$i1ofF2?N)ZjcqLg!~(z>&xu_1eLB7@Y3;NGs;V58iYhnW{bW zgsm?(${fJNlJMi%Z)WeG(Ezn34rIiqK`;Y91jxu&XgJ3SMa&yUs4w`ZL=cljIz0M` z`$)|UJal-Ay!t}{fZBOv;KLzxTKVs>$$@9z#v+nGn6?RpLW!=9H1b>}hw{U6pAG`w z9{RPaZp~{!&?jW-F`JMaJ`QB-Ay3f6$>ckmVDg>zUzd*H^?p1b?=* zDSw&;SiX9c?b~__xM(qJu4oca%6qJbBo@HxaNndHiQs`wx@j90nWebM(?IVb%ZBGS z);8oL)Bm=385{z^ZM!629?FRIVRI18P;Jh}^~)uI(uLl4*0SP;1+E)$d?+sMpo}H+ z&4ygQWZY=~qkZN@2VcVu>O;%DIW1^9}5Ud87?F}^)WhX9#A5m ze06?cngh$`2(s%bD?QaxS$H!#9{q$6&l3v1qnqE;4v>T_vv zocK!i<6eob6P&PiPzMvW>*qtgglwa zq94Kat`0~BwEyxbh7f+31ers)feEH52?ASUnD_oF2=&%=WtbVQgMlt293xD^0tbGRZ{m-1_5m#zX5%Gw@+P1#Q1XBuMNEU@9NAMs(245|sE*z)Umc(Si zLp)nX*=AmxL-zHLBu|C)F|9(rSGH~vV4)J^-o$d`U`jm1-oT0jJXa5W=dWmI3!sY2 zXSnN_a0^MnLIMmREd#)U14{;Nm`@=K%6$MZ_`@1xOju4;KEwwD2eRupVq+Rbo;8qC zvIo(_a(BF$$y5pfmv1?>IJP9fp8EF&6Dv`^%n`cW!FN3<;k(Vq4I<`vAKYpO={@6w zBn-9w0KLAxg0G5c3IbOC5Af~ayTJr$V>$&* zt`Kej>+s0P$g^kC56?w6r&>X#4bF8@2ZPLJeWpt#t#j*HbIPOmuf{J!7$D@~pS94D za6fw;FL?!aSTMKaHXGSyCYfL)Ai=HK?j$V3GJ;4($sNcjsl^R=aQTo(=@w9#@F0sk zBj*vg^2gK)sqh^_|Jkr`Js%b#uJ1${?1Y(H2d=jRE*eede#q}w{z9w_RtpIlQ<_UX zk{Ah?HJM)+#RqW@L*XWV59V_o!UsNyh8gC_{vdVf%n1SqtYQr_BW`%?K#7Q^L*J6Y z$mtmx?&a}N(K}#2V+=1G?fieuGdXcOUv`Mhcul7J9Q1Mk?RL}nr%=OnzP8BHg825% z&0)okA+rWTEYNEB2I2{_@F(x?yl1~9+kjv=4Z0S*Z;vpXBulvgWDVw8JNTB# zK@KnG>x#y0S@JZx$udM|rC?wXbwkpa3!dLaLe0{<$cX(*0rYvB9{*R4K}37>T=w7^ zB>?ovmU7)fz7KN!Ioj-=^1FW7sS)MDP@L88wfo@DkKqH!A&WX#!}Rlig(BQ)F=q=4kujg(hCF5=UI_ zyg!~kYCrl;om^vU1o&d@bbh7S#ZoGkDsy~1vHni=ayly+!K02AO`w6&4*MFOn$umt ztkYqUFuHVJ6?W>@SV4XPf!?8^D(j)jnwpyB>KFP_CBmkQ{E#*^g(Sym`xXfoa{VqTEzz;yJb7#wb+f7c` z-5l1YT~ZgkWM>(TWbq(7tMMvevQMHzAqIcJ5bgYlfex?! z!ZgAxs(rXH!H8FlK67EcaR&u=l;`H=#>FvAk2Q!}TW6;pavG45kx5(6EG^~SNT{kZ zkfRCPBZMna^>S4$xDUi44JzDt)f5=@5@NMZbQ7qp_ODc9hfR|6=jVG{3+P!f>m9JQ z(igF7&?3H&Gp?`O5%jx*FSpW%0ss80sQ4$w^W9b=nI9kT7v9?@egGW-1g6pO39laF zM5*Z2w@w%ovYf`rgtZi|EejEr^4LHXA4ta!m|{yyi`&Y};l#+k1dfFB;^&C6&6w3B z5FblUpbnKD0)-;1-60@^bF_90?kmA&UYb=lc?G9^O+E*^(1rq2+-DN6sP(9Iv)B{7fz0Zj6? zK~kq?9LaVs1#0EZ=?x(i68*7uB|17fEKBcJEH$wq^m9>>rf0#!`cwO%91P&LQ&m;9 zUp$=NupTJRg<$b^3%^(IhKco%ZJ*94Nwh`i_YX~gg2J=MwWnQYwp$5>u6C)a_c^V% zhkdwm@9_=0X^k#x^D})b#|UkIP2#6Tz->YhD1l3a?7$WBKa|L2Z?mX)6udu9m1W~Hvnq)GgjXybCZ+j_c#@wwdQIUb064Y<=K zjz>vJ`Im7|Ho_hB&Q~g4vj@vqY~YBtEo!y-L3tu?CP`|F5ovYWvxyU z%@*BKd)_hYH9DQB@>GaYSV_1cm8sO{?Zx@3TOi`75)oEr*R8V5Pw!61*c(L=)}Wtm z`z@nLLW^NI7{##vs}{Uk6LGrx2v9V2HB^m zu#Z$zi=;PFhSv${&i~B5WHy?7!+pHt+C6fB>9gN)FYWoa+Y(x~C?jQ(|2G8U=qOlq zh|AFFc*DUZEPM^0JW1Jccsfi;QPJ(R1x$$4R1+H;^;S9d{HR{x^OfCME#&~b64W6oO0r6t<=wwDR^YjZ zwbSCFI||(y60<8E?^X7#b>Cjk6p95=2dDiBDH=rB0%8>Y3P;=4{+1of#1#*khkckw zgUP7F8ZUIgKk1g&($?=?3P1|pTkiyb^T?OO+07NMgv4JwXt<5E1mpcCvOE=_IHgg=(hwCk824f?SJzk+cAs8wCj zmQUXbok&jZBI~xM>++5Vx$BD7+NY(^pg(8Gyfoy*8CGxq+!S(+ciH(=DiuJjX9##h zY3#>A5P@h)p_MLse7+5)d-Qulr9G>=e( z^mP(2GaiOH?~c5=O_Y>^ZhW43gts34 zSs-DvDcM|S9nu_TXP%o<$3>Aq%idt5_sr*cKSeO@ptCh)_KYz_-Q=n3Kkr!_>QyZN zN_A{w-SdQ|u&a!hWA;C9o3vWx^u;;klDI78}cvlTx)Kb$wBBksIsHV>wE zT27nQpD{J%U}5}jxM*|j^1nx`uql+Rw(}AWb5fDG+V0ftrrPd^mqu}j{aa!5t5Q0Q zZof)m>KEyrs44TgheCl7J4cvN6BCg?9`6GKw;$;}sm@;^Npt&l1!O<}a(#E|QQs!3 zLE7CUhYIg33+i@pepwDt?}NS0U2j-NZ^V8`O_i=C3SnJ)LUP5vok>W=zc=y3N|PLs z&y#POC{YVNO#KeZvNmk(?|u-sXLQ^=S@*z8pkal)g;l|F#91mvcSiqE1?uo9X^0~h z+y59E^A(EBFx`94`>oW^%k?wEwJmidK(nv$8^O}KLz*;GRa&!C_w)*C{TZ>k+;-R3 z!;8r#-)*Il%DNCiBuKk^=H}*>$Lf=NSSzNE=^0vCDN>UxDOE*$MxBCeN6|3A4ilxO z+zEd$!(ApB##yZn%I{|VFz22=vAL0^FiBBc@($ktyV>Nm(afDr@Xlb-9NGHzQ+IJy z)vb7_g@E4_ir~^0lkQ1-h&|5}1Sdx@n+FJNizPfpy_-eB86J)ub}mI8M!1ED{p#86 zvQ70@VV@!2_YqNYDf5a`Vv zbsEQMvC6|&Ug3uscKgo)gw@LyLcbW$1Zy9=AuB$&XWe`4Laf@-tKVjsdGmZ>a|MGEwk^dazxxF2)6aGyThqA z!4lWzTp!lJ{Gc4%(E?t3 z#XO(yJ$v3Y(K%CH#ZRl^-Jn%v=>T={Y>_?wL;lC-u~{@f8iXa^>|{#v*>ro`1LLhy z1y!mQyHxVUskAITsOBq03`%sn1-n*e-MSVk_FgY) zj~j5xbWG{(?DB&$vXanyMX-GD2ZY|=(WU|WypodAT~4)DDMdIDKgeY>yA7Xl<4)o2 zsp;u<6ZGq#pxxDWLh+`SmVi5$FQ**{=Y^2*%2-N8PN5d(Tgrg!*(=(47S^!= zLMY8RpHMH@Izai957a%~AE89f?u=O!G*^ik$ohcGAHROZ>;UjY{&H7icx7yE^n;)* zX@I=$t@3h)yD^sEigPw!ln!n-_S?MtS=*Pw0CZQ}AM2VNz5+#E_!1KN?%4k=KhcIH z`c#JJGN{cI+|hT|_w;@tL7S)rd2Ix@y?ZXh2KVOUjkml}xBgPz6lTV#^9c!+=j4zx z(17MUr%Q0e%=+sHG{@L>T_9bus-B}LYWf<~>DOZwGaA>u)n9&paL>yc*^FV6jg6&e zWQ@3du%1;JGdDjEr#CuAMn(pPca7%rs>N{NJJ>t~{Y7eOL>ejlUaJTEXJ@Cu@ENgCkvXr?ZV)F#Ws_plr{I@^V|B2 zpW@G%r!wf=*tKEr{6Vf9|O7?3SN|I{4+@#;m7umx@KJ8_e24fG@~(xU>+3n|_AO|7f#2_|o=?5V;eU z@El|GMW`CPW?vonNvhs-Io&!ju-ScisXWc4V<}ew)p1HH0YXKq4Ya}bdt+SR%?%hz zD7@c!<&~z>>xr#52;Y+EQc;upt!e`m7^rKYY{|MsD}Hk0yv1NCZ>*xG9*LoolZMlE zHO=j2@_Dhkb8M(Q1wU!4CDnqF*IZ#Ox1XXOJFU zD*>dKJBy+a-5s0Zf237?DYcn`fU67!UXtT~2{0bToHU?o>eEe;wd4jEa$&%qK%_40 zA@@|Q5l5tW!dc-Rg+0UyDMyIqa0JUGpU$rYtg}1Q>NMiBSa|~%9$~JBBDn(ynhZE7 zN$rQq+!`uCqDXZNfYwKB?Lotn;Wt$y)t1NLeuDIv2`co*OGgsEf1q5-8GY&4^ahC! zHY?+af!>1Q*S5u~6`{kSw_A=?&prjl3A470;+Q?f!gGg9@D1{G(lnn0^)YV%u4Dn~ zdGS1--3-?ekI{~(0>3RwIg$GdBbaqJ5L1sC#OJ=@;Zs7A4K}Bqq={P}b6p(!ffxby zcW^0Y=&ip0twBw;4D~z_c0(TwLSHLLe+;s~13eWE0ym4riK`x@u>kHG1<^#Bl#5M; z%i>WZxIPdoklcSJIH36SUb4x z;Wa}!1$WP_CoK^2sc0;%Pj341UwBaky2lh51Nl^gx@ZVM0^*Q?L0?PjQ|L#@REVX# z5c~)qyNkZ~i@**TJIpwy?K;9aI9Up5nloT$OOZfrIQMRh(SHef4Uq1yBN+9OsISy( z8Xyina~itkj~Y0@2Llr4&~}*^Wr;9=bB=`ez-yG{{Wyk_fOnqH5}fkRjJP2HbO?rA z9(N#sFs_;@P@x>hA-aeg+0ce+L}%?^8f4T65Y|fH%k9eWNS-;B)rr^L7~A$OA2DDL z3-nvD4-uUV-Vsle$0!bEDb)!A1bO8F8Z2qZk6{!&x7 z5umR4Wtj_Ohob{gWJfdyD6@D*Ra2D$Mt zSj~H~O8VF*-q1A&`MzPgBncEb!o87~Gs1KIG5)-Tn3a&kHJ~;>F9Wo!&?N}pFOTa> zwBP7`qS^ptq-Jw+wYp%n)P?GpV-Rkom5GRf(`M6HWQ5JP$=hP~T|)NuDO3pgk!mDg zA+J*GQ`Jp}bQCglPdS}n*%m`Kn;!Z{40tuiv8P~4!d)>JArJ{+fOzwKkq`YW7zuSj zJxOF>$A8&5QC}IXwDF=CaN-N|Ix#IGM081AQf+zto z!8^5wI-qA=&OT*+Eg!*~VKPd37EP%s=Pnrj5AjOT#8m7w|K7nEE`B*8)Z6}2z=1(< zy|w^SCXZ;lK3ETK-2Wwl*eo(vPOjABDisJjJ7J`Y6|+AYPWHnxq}VKErP=d5@jECX zgI8h17$!9bWL;h91{9Z^sB-qo(Fz0X+|2p!is}H--?|;@ff=Ud%*k1rDth(i8whvn zLqb0^Bjd`wT&zV8ZWLkXJjush?+h|IK-h21o-#(9ZKjO;H<7&lqv{jL;k*SyLGhLX zL?nhinz&i3RCFy<72NkU)qz! z{7ad>{p^aKlMb$?BMBQr*cIS=U=`UxEeGPW-#rI$e-ezGIm$v1YMk_paH9@k+x#Zs zl8H-jWdsNUtVJ2_$6r4QMo2mw1E`vZ)So|Gu*J7&Hkcz|@A&rZ+lLl}%(C-CH$IfX zNFYAS2U>@bZ`F|JgUrVTA!Gwm2{Gh{Id~KIF>pwI*^Ar+veNrOEQ}c8%Dm7psqM~= z+#g3@>?8U`OH%m=gfhJN*2K%F2_?R%VRK1K>5cF)cVd@D|(INIvF z>E(4y4Xr=pn08KffcQU4;*FiY4Qr8OH}1NZ!RT$Nn}1?*gDeL-1_+X#ixK_j;Wq#1^ZQZiwNs#DI)Mvcw?y5td!* z@=7sat;Qy`Oy)sn+O6#{-Yt&gDCH}_rss;Le|;&>7wkqdfiL$a$Gw!aW96ZCZ?)Hc9iulqJ!zeFCz5$sI!Mp1WrY0>kQiIu!0-j`6x;dbuL%}RBs z70Q&1?8>datHASS9vgix^G^yRuB53>Q%&`nu`heO4+r~;#H5gSh(!Zs@o9I8sj7&3 z4w2F8mpNJfN>NA`Rb`_(i4M3+?*9}JKDEtuNdEZBz!}VmsD?i8eY0L|3QV%PT>A)` zcz-H>LC|a7ti^M!v76ALO0Op~1KB`77(D1*Uf0%NAkFBIDt(BHwhuzGK2HP9s>Oba zjO_5m(Bb{Z-b4M07bQs(FOcJ-;NaQixyIPNKmX&$4+ti5xpR$!uP&@gg#8#t?Y-!@a-y>z+r z-zx@+{~K!VAvnzw1aq&wy}e86pdjxMbj4`F7DQEX4TZ2VyHiy{C5{#r?45tUL(10p z_&6M|)V(JGRWA5E3Sm~{@~}Bb3`)b@EpR|Px!&SPb2pJcN>Z_BiRqdso}S55Ckm$EwlpNuRP@zQFz_zuFnsR za>=1Lf*?)jiJjewxxsFqk_St&?%5KD3{MeIvGo)E@Tv3OkFvJU^M&xw^-L1*wc?r(W}>sKTK74v9niw(K| zN(*QuN5aeR15ncl^1NeEMb?6NLR10;2fu>pcdVNUHCtGO3jpu=oHaQ|gCsF|mR)HL=n5G>VV2ybkV@6)_J-nd>W$-0MZJ znJfTa6#rAVx#SGVK3^u};0x=-U@T@6TMyx&@sacG=?lY1LM&VyF|7tjIX`T&ke>o$ zM4n|8t{dE&^WJZKA?hOHYRa)`T1KN(umbM9Hn6JdMonyeIo!O9gq*%)2P`@7$( z4tJ~KV`Bpo20)_^F==rStwlGq^?Ywwn~mxs+j~fhKZYPlS4lj^=!T#=ueATG_$iEt zDj&f6!r;T*s}bFE3%kRA8CqDNp~a*|BzTY&;{)f~ei8rX`EmzamU!>{{Azrre{NU! zm_{cDLIk{M1o61I_4~pxFI9tZx#YVM92HAnNf-|~y;)H88M_>O;lwYIBskmmNWL2*LHUhwTHXE8kj+Q zXt?M;s_$R_kA#mOJoBO8fCliRsp&k-K4w&`Z3fB*^PIx# zC9R%ThK88;HK?BjFm527&)L*;3S{?TBW+Lu83`&LKu|M?SBy#?!)Sf+{5gc!(ATUn zez$}vpQS8t!**hIcVn;faQArYK++4!4QWYu%!4)6p*=?HdHStzK=&*kZMbRbCd*(0tGkR}12z1T1_tiX9y8lYJyO8Zv{fLKUkp8Q#LXFn6 z%wOVE%GD?Q{V_xWA27-_Cx~W9?opi~mLZ}D7XNBEInAd%Zj|eM`X1)d)_8%z@^5qD zG52|k(E8Ze_IT;mIAM3VhSky0kz?1ihRBo$NU`^-sfzQzzq}Cno50JJ_k!?a*-gC4 z%McMA2mjF3*;zdI0PlFcA$8f2k%eMrm;>D&^^Iv!cRa@}g&$5`CxdM7w7b>H}gyJPv%0Y9v-Lpes0wzjsKnmzuV$vv|mb9faHaEks` zK6o0W@f50XLxyeOXlJ*Leyamor(7h%svgN zEX_7;MIP#SlY85{iUf;Sw+sz95~hB?;~f$0d7vLSXid&#sx&}Dez+$vg*& z39}c!`u6QxxO}_vs zIF)H~mmYt1Co!aPf`&s^Mn;B+ge0*W=^&t6+v#nZuV4cDPX}-9^sCV9!9f4Ff%g~;~gZnR~Y9^NQ>MU979eX zFSaE*F8${Ga`EmdmE1W+W4Q7UM&%*g!dhQ-bF74nZ>3B8I3Bsn$i6j8IiKss+;13* zSJ~Om|4unp)+?RZ&cnmAx6yA6YHbP%iZv2~xi8A{@`$tO(rH-;pt_Uzp+^u@5^|_> zfhoz!6O+x+1OxSYO8=iYdT6fY`175EL;&Tn1&>0;}-XYRhyB&*oFao@{mu%cU9xW%CGYo(Z{r>Bn2 zVvz5R1mB(yTb31Kcx9MlD9o_G&ldV?eDUvf6oa!j-j!}s9w$dfbv3oxx;l>gg%%JX zdXRmv3~wvaf7pjt`RE5`m;`<2nvB~>MOD>}WioE#CappXZD7G+Wi&BKUFBz6`*}{I z=FyGZjN<{XUIm@Md77TSdC1D=FkS_I`3w5%*RLNF6T`y7Ab;!ejVQTfyB9Q^WoE-} zst=pO?>4?kZS^1I_v-ocw}(Xt`A!3Y`j25j`y$-dI zY#2*m>`J@${*$g%y!+DL)Vxr5x36E(J*T$jOeCll zzMv%}C1FcR{P+?65vn6c5I&})uoURbi*V{EHY9aUF9=(-W@Ys`eZa8I|9D;OnSJNk zuJtRo>0SKR?3+S6jZBH}Hg*pcvu`$kBqzt965pi+>V)jj;SV1_Lw*dKdI4#f(9qGBRnnN_A9`jP zMLdvt8dBsS?$>L*OA`+d&$?p0dp5gDDadGQ=GwmHw=LpMc?3B|g{F-;`D zHSX%^6{FikmsB;I_m{7U|mCSV4DDO`C*&bIduH?G#Z_L_&gnk8|}4=!M2#ps|u7{F7ll zdd0dqfqtpH)$xInZ&~imsZAqM)M>c35g`8Y_E6@@3&q$>56E2bJlQ&+!hL z3x3hqczNSF7R4R-yTuQyR+0VnZ2Zf6#&y}2O$4j_<(Kunjx2Dbgm$q|J@xPMGbb_b0)r-=SbMzab+w0IVIUv`NH6MMJ@dCGg*y|nwpw}1CRS8K?+M;fuKa3wrXOKFp+z%=`AOEouplenXMZqn64-j*T6P& zN8DC?JnKRI-i9N63K8(KtE7@(ETE*bFs-Rqvk4FAR#RP2+5 zZ12z(x7d{zjoPZHjqm=}aSmF$RLzO*GX3^lLb>fg(T%LJGJ)^vT@5~|3T&}ZR2adN z%gBp7KEL1J@Vel4)L~OA>D9|}X>6RI%QXA{bf;-NAsnDEkHCJEk+AIetv#dR_TNWr{4nB-=n!SUAv`0A^7ZJOT)X()=oA;krS(usvgR6K|OyH zb8O{&LPzWqw8iIn7Jl5k%!Q3xU0I3rjyu-8@?m@J2c3}-(`WrF0dgs}9jQXwaK<<{W;T)!A>nRM6%1oMJXSYi+umMw@p#@yd1R{;2WxLWMl-J1q__o+1dWSKDYtc zU7yKtGu)i(Y^T{e+-eGcZw>Ce>5k;Ry*)T1!WfAca$N!GBrPrNbhq^L^_s>#DmoAm zu=1YX633o%HN1sV@X|OR(VWLd^~$N?jKV$B8mepz7frNs+%)JMpFAlSxlGbAxxYGz zAJK4uvzJsMnHc@Cp74@2&s`?@Vdu0H=f-D~wf~65Y{BRyUsmRmt zF4=RgH+QT)$i&zfP6r!+21RfhRJyJjLrvlCUKTx|P`!|$&SiQd`r4py?8XNb?VrtlAiYlEB16`Y62L3fOMtd{YjPW`pkL+g9* zAK#DZzF(~HOD-?Md&rKfwfm|2vW{7P`g)ekB1dLo_{sZnjc<7!IBEI1thFuMwQqcm z(XHfh;##A&!W#0L+*OkHe-@f~o;pA1WQ1k*n`Yw1f%kORK3aym*W^8L58`Mvs(yo} zYtknx%+Gw0MY>+{e&*tIMaKUt?90Qc+`h*-j#)AjnTd!aAyej=%o#FP$Q+rE=^RC+ z%yZ_-m5`8mI&NmkJWoet$b1}QcVwyf%L=I0iQC6)?t1WV1*)GgP|9B zMN+g>Ug{MMJBctRXl%FbEEZoI!5Q}lRbD~Xns3t|PAOEp?=0YS-IxsZa@*c-um}UT zDR$XTsw_&41bX`+Jz?+GW)|La@`C~7uQ=)2G$QV$J1iniu)g zjF^CV72|qQk~N82XCA`LNI#(FsD*qH%_c;nI^*GsU5o)2{PPEI77kepF+`}uEj^ij zE)Hj|x}R2OeS%L(I_Qs}vcRK2`JiN#({1vk&m zr9kZjed)fC!m3>-K5-Znn0d$jw zX(qG%q4EMD#7i`1*zJhhI#~GEEDn-W2o>A||RBv{17XHy%9n;f)^>xlg*YbPsRGnf94UWuX zXj5(V#Lp%^4zaNNY?DUgi|m|^MToye&2Sbz@2oPOb{vES-&ZCdC1U*~nNz;M{`8ZI zZy?{aigcziNuw%5LW=+EB~Cu>|t++yzz==_p=o_`$QWvWPSYByW~l*9NNk<_K9g=`FxAwbyV<|Joh1&*UTTL-6-FkC>qm^ zGa60T4i5$vF7L`kURt5p$eY#eq2ItxbG|E0V}RgN;Qalta&B}Or#NpFS6{0%mkPuU zh(bLWrq1)n$Hn5;-ae-XB|-pG^vAQui&W3oF6~lX-bIzHV)4_gV7l=f+bH1}VIl;= z?8HQC8YV^iLtTpv)gUKk+h0#cB5K*`)4Np%X4I!a=*o{DvVGroH_ny5zga!-_1)9Z z@BRu$3gvsD&M8T7lc_ugHA#aV)hS0mA&TpZ2!szc7#cvoMyu*|VkG@|WYo}IXy@{M z>s6MUCF%8ut!4bCd&}3}V{+B}jT9#4tT<*n-nL26=9C6@bqyp=ux!j( ztW++*9#~r6p=rDL8~)I>hLARjQ|z#4T8|x^FTZ9)P2#E`H$wlqs7Oaslc^L-bi|WU zd%b;uFUYLB8(u8U&+hFJl#zup6X>de(UqM2iqUSa9r*B_G8^FPAk=v#ArEHlAh;66 z#gkJ4ul_IrUN(-VfS1Bs0L6*xmy{kJsG8K5yJfAAl#+b<#3c%_(*7;9kmNo&E_5Oq zfrfJ2Gw91}3HIZwvBcS>cl!{ksPPZN*@WC6_kxq8K2)EX0KEV;v#hM-iiSIFK&pCV zgq3&lRKaxY_BWC`w98SO-FnHQ1*2$Sxetx!=#%%R#O?!}$r#PKwWpz4DbDG;_=|Ap z5X{7zBO>M4Bzl3BvSSWxQbV*r#ZA!DqqNrGGC%uHE96$ipL$^ogh)yrHgV*MqbYBG zyMg;vP7WxcfyGw|NQwSQn}9;e!chK9n5m=2k~1yD&KBwbK9sdk+ZPP5r#|fohfNja z(r$mlg?Lp=CG1y;(;cnXvAwM|xOF75wlRgg9a>q!kG&Oy2t)!%!Zk10m8LWlu!&1~ zjBa}Bj|UdJSvoergnMY9-Q4I!aqL7muSX*>m!OV9?(;nUSx~TIs;380k24eUlRyFD zGStvIsX?}>JQvneC3crd86sNd=jRIw3K|NYWL;gByAB>-K$M^bm z{AeA&T#lppGo_0wfp zfdD9&oD4iV*akJAh&6BLiBcr-y9e5FQuVek6bnYcjTBbqj$*1}9sP0@rt(J2qZjB) zxQ+F@^lf*UZ6Y4%n;yj<#VjQxY7*~(2JO}IBh3nc(+!Pe`PmaWUcgW$GbzkM^(gi_ zH4UDWP0iaJoSgi8eBe4>K(_Y)(a&wcfmmvx`wJVo%3LkYUnV6C6~VwFZ}8m!&fVL$ zKl}45ED3z8U8S!ra8~`&lj~VSg3-qRCV`j^7Vm-CbOLS$Y~8og_u64dmP6#zPG<;E zNSwvi>rK?=D=iU=@@QglS#rPYQHlF7%G!q8svv3xir0uRMyS$<&dL9j#y}>^jJqRU zIh*)s%Um^zEPO&}#i_Cr9Kw}H2}n5*RCHX#lP^yj&j`%O>eH zcCd`V%lxu;545NQy9NSr^|c%H@LBC7vYv6cPSN>&;3}%jz)6=2xzo9b#dQ361TqUN zJ(pq?vQb_5<_)}$e?g?8CL8a2-)t|Yn-v>k0Vm6dYX|+8-n@AO>^Sj-qUFU1DGOn_ zd&`etJifpuxM2iYfpS#NqSLsSsi>$xy_9@IKH!R~7>ca^=a`ZMMS3oc2FAF^=oit) zUt9q>W_Ib?!vh2Hsi`%fbVu>l%h#eP!Eah699K z#{J{F)1i1(RKR!Uua0Jg)+VEhE{hUkxDHKi?a*V7A3p{asouU740wWin#7WW{3-kP z$dL`J0=B8^@heLSV@oG+7aH-$wF_Ul((wIY`7$yFdtnNw2=6B=uGQJiorSOo=|lUx zr+2LU!nH5X%1l%d zg+u@gpspZHbDy4kJ60wLGqxg_<7(o;D8C?s`R2x{#*j^@fHI5vAea+*OwhQ9+8f=X zQ&mzjuez@cs-}^JAyG*3Q~7~Fq*9xdtqYG&Nb9^(g|6)4k8tDNuaf5HUk066zI~Y3 z9-!Yai@yxySIMq$qnT`!->9%(pu`mX{HHO`NZGjBbMG91nRb$?+HS)b(XyOGiw~Tz ze`_z`(!NRQG0AwqD@zz}|1B$&y~qw0YiMKy?0&cQSnk<*=eg+~!q!AT`OVK%wcA_C zmHN*ruizF*lkc-$z=Daz(2qc5ytTP`veNx!X(_c2o!bH)Rn2+w*-79BapCJAtN0JZXOZ4HT}Fkub$3OpRVH(NxmbWU>%CPsAaBHZg60x~JCyHe*-j zc?I~mpx_eFe)RL)YUFp#rEKs|NmfS{{p}%Uox0;HSG;VxM1i@^$yCl_Ok`neYiqzm z4+89EPQ%*A{b_gcX2ERh6?C%EK(0=ma4OJt$mi7etXRRGZ91coOQrKrLPA#HBLYmC zn@c#&=639*6n3gyY$!p+xGl%yeO>N+F8d3GzFJm@G6EWsW^f4>$PA;xo1z}h8;jj9 z_J$B39%L>q6r%aKhYcMD)yTiO;L^Y*uJ03Yi!j)+pF>~xZh7!xgX70>(#(D7#C3A5M#Ov-HeF3Z+9FeEzwTy2YpI&wBq;;mjOXLkyGNGYtgQTHlK*Y$0*we8{hGuc% zpinuh-(-@z=c1GcsKEbD5B#ScL*i*T2}gbc@&v+MI(lh{GRZ2QgZA~4PI^wNB$$e! z|8_PTxBLEWrO%k=$4y_JQjQ8(MMsS~(}9;zJe%pn_w__#0Ri5>1Q2*{sx~DfmXiQT)xxNm!qkus90K30!Ug?QgS2zVa7K?yp)sB-Cqw% zj_4svO>;StSFx3uNLv1coU!mgAD;?CJ^?o%HSLex8UH zRK`asXOC`dbV4!;L}HUky=#!*q)O592H_Qu=>n3ewwH%NlWyPu-U1a1H&~Cyp5CiH zo{-{K4Sc_!f(YIZq?;wpe5%Vyv`2yLzMYX$b9bO%JtMVzulZu;UR3ZZ_*Jrm$4Tzf zaFG=&Ev*-5gtk0f1hQw=)?5#LUBMk) zVBnm)5_T=h{g9#aS~49Io0)67)~2rH?@veD8kRzqF0pS zDrmV>kZO;NDfVn6L+)ZwNpcUo@#hn-OO$-}Y9lWC*f|$IOw$GlEax9$H#U^I*LGJ~ z*dg|6F*Rdx{%jkb>1bCXT?jq&yF=0&bpQM;3C-SP8;+e8g{`2yG{3-Db)<7^1k)}C zK#A$#2Oz^|0fOMmg@ERTczNx+o{NIuXjD{GcYAx4_v%eRsK&-gM){(Q5mP>2!F7sX ztdvNi!`Q$imq;3+HCHIXAicE}Kfo0TK!6;k;^Iv}osrK_u(^`I4OPV<$m!n2#cAe*D8yYrP+xripDKih)J*NiUYk7TwH z+E?=PwQ?6e%O_@A;Qyh;tt%WKHWI$u!W7G|fpAtwd$lF{s1Zj~^IWKn93k$K4A&;{ z#y9r%SqLPEe0K!XA? z5G-iE1j6&$T3XVci@F@U)-~RQ5`{7`A}OeVo$I?vd!vfwga+)gwaqgh9<4@hNpfAe zu6pp*sk&GQXwF^5BI2!bqZ!O+Qh>EyvGW zjsb;k-Gx4F2M{u3`+*R{(FbN892`PnbtQR2G7vKd_f)fW-65BgqNGnq>{xkZdt(P@{l1wJS|bRx~NPaFOLUyzVreDCuZaw{S(gwDK)b zMR^=7er)V!p5a(ePY(cdXb5DX0K;v-vCOhaRSM;C%)sVxiVOxm$dmoDM;IsSEvH8v z?1yAmk?d=vkNZVI_526&MZ?RUl7&Y3#WDkqqSV0d8r%HPe(8FQ_3)Y0FPVO#8g_^W z5KveQlCO!5n5ONl73dmcp1ZrsxACA>E6f|;(m=rl&|=fWOscq7cT|L$ z3)&K)?ig=X=s~3 z^vk1SeNWiF%MoY6gmm?L@cz+kfBxfp32BhTsBP)kRy(j^a-Fx582BM4Y}lg-ejm>l z4|&CS?(W;R(zk7O*$mUKaRYIuP3{^JpQ^Bb2{}P_xAK*sNG}g{5$HJxWRa8%SE=%+ z`}1^ft)Z9>rcna63$6@$|vu(5&Z-P}I@NiERy}!>y z>o%f8@6r?(#d*reuoRpF$OkfpwcpW;N+P0}cD$7eqg@>>djUg_EXL zb^Y)q?R+!aP@6OgV0;pv*E%&ie(D<1=aBq9<0CsZc0DW-V2#ht`zry%_kWjJ#OYS{ zN4krpamp~=!+ON9pa99gc3_ygGH9KB+?GBXmuuG4zP=-0#Ium8P?3^k-i?Cz2) zx~c%(1#Y zN(0j=M$(Y~3k^QR17cpKT|Sh)H@_^B`i_OOTm7~&3b#9uKEi9x9<%3TZpwo7V?Q97 zfByyt*+>yxCKFpG<7gi!@gG)Un8##j`0&rnXW>(^FCg~sbdSupek zQl!ZLObTE?F%fxU?P-U)n8G(B7=gz-!b>IL_p#7akVy@Tr2juXv*c}SEdRd=TJGf) zdryM0PiWjK4rP7-yc^r^YXpwfbGUVlZRMcXmFfpAoIvC)0Yya_*EDVsxgKR>xe{Cz! z_ME|%`|cdD`~SyF2C|b)Q9N7zJIp`@LXScLmRGhFcmCt05W|VE2xIvV(I|&11C%*!5n2C{1-rv}GAB!jq1y(b&ThF_n?HRbcp28 zN_R+ohx__^^?v{GljCNcwVxGd@AW*7wKSB-h#80h03cIQzM%sE5IOMQ8-!5srx?}2 zD*#{xRBl|?^Mb6rCV8kgSkrDi%)r%g%RD58`&9_}X22~QdD6!!Haqf#=ht-_mNLwW z@(M?2^KY|MRg8?@&Tc3ibLiE{Y6K`^4axl;Kp-&fp!3uesGqQxS1UOP87bW5E!1TKu)ruuUQj~uY@VTtM9p!sH-iUbWGX@1P`hZE3y z%$kaT&&$Kn!g#f2W^A8!0^oP)QLA?f&=_Jn3?W`zm;ujJyCNM5#u%M%16QUN!Q`pa zKys+N0XG(FIH?|hg(+Or8VM2ZiwUZ$H6<;*Qd{dwZXc*%manpUo~*6^o#0E(R~-e{RE0!RFZ|(6v!`%j9muT-in9BhB2IH{{2P>$NEhMj&dAF1o|1|musjNf1#ewLZU{Efu#UGA5apDpJm*~ z3c61{Me67Rd$2QP0C?$=QQmgR+Zcvfl9p5)(xx^v6QdSO$nfIuqx76iLm=YSkQ$$R zd}-4;YlkYom*fI7M{h?Qq<3>SNK2Z@J`S3gK%S;H))->H048;T8{ZfU0DPm))Zt)u zeMTj#q<_r-m#h}`7~d-^L=v2bIj)?v+B%m&p~|?(E6R4wDl`yo5Q_gehJAEso|a(2 zUGA|kT_#};K}TL;?To_*1Cl{#i<%jY*+W^v(Gr8K>F^v7L-IDjq}pxcEn_k8=G>?p zt36J6uno+kUkmk)w*8ThA(bu*)4HHB1S%P+ZxY$gES3ThZzS_RPh;>4dQmLRs;S>% z8y^~Cu*JQ5W+ism1|8|FFcBPz0H{UW^T;f-(%XW=e;fN7`hTqb<9#62=l3!Zn&Z@P zK)erqMUCMOh0BwR$ryF}n-h=q5x|6Aa0pE@2%;Pv%yBTKb;0Qg^yO7Y7$yq?qDE_p z{}wMHKzl`CWUWdV-$uLp6R|V_)^fn4hTg=X>zUI+sxc!^*Hee)||jEYA}ceDQov zZ{!QTzcYuk4v|`J{6CrV&qBFY&B?uIbSO2@n0*otgFQ9ZNu|TWyVE!>ZcETSncrklyqyv$o(9IbkzscYr?e*Ijxg69v z>$*INM#5(?>o(G+HstT+W~;ssoXou;KjC{ru}-7rwYwuL5wwTL0rDGZiM9 zqluKTAq8W0AB@=x=t~7hSS}c|4H=iifw%oDyIqltqXAnOZ^6 za^1W$DIewU+%V|BnYI70vO)w*Zezd2HCf62x zQMwGE5hr*Cy_AA65e_aO0M9@rN5nY}s?hD;~x3Lp|L zTk~*pA(*3q=w?7c9y%mr81yaYHs0m+UB!qL9s&P@Ym?rU8CFX7_b;zsEBWT#cAmKvST(*#(&6K}sw1Wd<-$J__y?r|frrtcvC;o8?rs zD@l9g3>c?>g3UqQ$^`~;@L~+clC4~r4n?xZFYB&Lt!0m=rJ@koPEU8U3$@Pif~WK2 zwbDE&WeoU{cphX)b=gIzN=7Ced9=Q+#6lECkILWWmvz>q1_-BC{IL3$U9#@Lu%qT) zmMb!QUy2HnmpqD3=Y+foom3e&HTVmFv`bUGPV#j2Se;Lb31;F=>FI~F9Vw!=`-VIM zV}<(zU^W9h;F7qWAgZRwa^Ja6+z%cY+)Al`INhSJ`{t~T{HMTJ6Jtf6hm!K2aD7;n zm>90RZ>H%f@jK8u@8ZSTz_yPE!=BaWUTJ%j<7nkc<6%pXYD&xHU!CZ`HEM2V23BA+ zCkI&dG^YgxoY$yR%WH^H=Ss=azz9@g!igGb|b-wY4W;mFAT8Z2ovu3kcomo@#8 zI2hWuG#JwQ%!-{Ill$L#M`~Ths-tbX=Y3IXyF9Y?REXc8?tjiyx+dZH{iGe~(Xjbs z-~C)8fq1Eix8abk95fDW>N>)X^7!WB_Bv~sxVARLSBoacK8`Nz(2|y4+dMBo{(kfT zoqq?X+<}kwrFGuke)NNH!-35f|k&qf3eC=wakQ@vM)z0fI2ynJ^w2%e*vy#MfE zGak7Qu|-AfaLJnWBgo}ct5uW0fVMcZ?>zKOy&5pKwioEV@3NO*aT)ujihHfciui`; zgRplom?;5yVJ||b(sQjj_llr@;96(yKRDSB+XhcGRU+QbL%M&oyxzMB06viVeg`codlEqRi1=SJ+5vQ;CkGaj~;_U+UWT8o{5-Q z0BuosFrPVE^(t5>gHGfK$n-u?uLVhhlv!~m9}e>7(H`;X;10&va2TyLgYQSBla z!@~foh~n5Ymy@AToZa`8RY`*N^6SCt+#{sY;=QZot$s+p&g!j&L?JwQz>B1*@>WSF z5i@~Rk}0o~(5Q4ao`I0Ep@R75RctWNRO!da2EaL2fXa#LXQ7)#+9e95i&(m7z}z}7N}cztuRclz>1 zKgACUK6#p>^3vM_rZg@3W@G zXlPL$Fo<(n5^7Vh+sJu!7pmX;a|W*Yd?7+}q`($V1l zD)GT=U~!^V~94c!PS3f2)1j{|@Bf{u$O zJ^g|O8`>j&b?DL2aOrfyLk%WL;imMdRG{S5$VTI~4p1u#lh#tvM}zf9WbH_a%efgj z((oXAU)s#HfE()^%~L0Yym!>o@@{{4LxJkl_s;H%pTs{LCBDW-w8-XjSp3ZUyyX4X zXIoP#%l8?T>a;cEGo~t=oK69ueP6dp7qcgZ5_~JJ8YW+yzI_-h7LVdN)DIY%d#i%j z9Tp+lc-9|&QuFb^-nxQ;{(V_`hdGHz0}B9E3q$>}2cl+E_{6ejHkVV9s5>Wt%_ zYMa_J6iwNU@*giZd?ZqAvAbJQH!En{2Exg^;+l}dHJ+L}XkhZhIz%8$-gDlD>Apud ze@D|*ff^3k-Cc?wYk2Uc_E&FTTs}NB#-512QKUh5GFy4(5Aqq<-D9x85o5tuf5uG#r^>c*dlAGpqPPLat(nEf~@d zp*2)tJNOdZ*Y2~#p4>(;Z{N3d4wbMu*U$TX%ZHiwBgLlb=~u26wd^QgnFYj=OPR0E z?I-;-x^%X_r<66}LPd*=wG$~xrF_Cd?5hD&r)TDkYLs=jQSIEt zz!h~gQC~2)8W&Z&IX9T8nAJ~HmX${y1VK?Cx!6uP>P?!Bamv7e(^ z#<5VVcm5b>aqW{gQJ*LTrpsHEVO1B%^u;hE$(1j`{%7urDBO82m`exONv6ep=v;-V zixDI`q$xe_3~sJ5vOt;J$OWA`yD0BaCPp2bX?2IjHx#;!ElKt0xj8&*SX!yx$@igh zwoERR?KMxeV@bNx{+JfYCtJ}WC~HM{yipsW>GTe{ftpk;kP=LoT(juuo8CKAXwZ;M zQ{m4Mn773=CynTV5jC;JsL{o#mJ zeFxEN_2Zil)$)Hb+Z}i3QtX%L`zk3+PN~z!3XVgHiUknid#L7voJO`z)r6-j%|BCl z7>RrXGqQxZk>8NkrwuhX@8~%{fHvknCS5LX9g9uE9A1%8$6b_Nm=w%9Sm))o^(VT=?lyW&(Jd{nQ#m+^Fce;wAF*k+X(JF4@$U zPa_MR?Tgm-RV68c`_ZY3fzq?h3SZk3gk#@g2G{&fd9Iz#;p@TjBs;$9zMLCu2O3m}lW3``LHtT)}BH``jwY%9<`C{Lm z$xG^^L4!@%+gz{OIKnyZJ@Ucd9<2D_yCBL%s&+1CAo5j1=Je8Ge}FF5TWz;yE|>4# z6Gg+6=puiu(-bwc=}}~47d!`e=}4IRwd~Tq7CAB?u;3%lB9Rh30@ocJp6(7GqrI~h zHRkq;1!(&~AW0&U8J5F#{fTYwI^MLud2}`UCXw(mr(bic$RiOq?c|l{P=5(_2Qo61 zbl^o=YZCbJzj9|CMTF}%4?J!;IW9@;lS5nDxZ~d*RfaBa`2^52kwmg2N;Z6~>C*MN zwA^89r$LFM4fd~Uqu4`T@46?}nyOotsii__?Phk68j)OeH^=N%;=s#Ux+Jj3>}Wmj z8 zRqQWLWC}eWEWL@@s&bj-#oh~^c>^ENze!D9^>$(wFPHlYcF|al!D4}Jeqsj-#;uk9 zf?eK$4USpel-PL%nZs!%4?K0%U)hf!L>t~pIl7_IMbg>nJRW%3b@#I%H9ESY+7{1N zHiSmewQ}@bxKIH=sgyI|6g(J+#uVwL7m%F6_n&;0^l{0_+5r<(XXX>K9kN7-oU8PNx zaT~~QvC>qC3&yMVvu1Yv|M;e_mb4ebU@~teN61Pgq&Rs=|`HOG6uC5S}QQ)gVQv7G4N6aX2CDxdWHdM zZ}IP|w~}AIg|<|mCTrF%e_51pzt_@%_FVjxG|HEuLk#k%oqEDk9B+yhyLg_Ktq?g& zQ<7mrUBzSgtkBu7kaic$fG9Z^N%TT+Ca--X4OAH|lAkBQm8*hY7#x&IiQC*=3k+Nz z_v}uV^|yqOF8GZtebccJDdVt^MqYYS*5Io$d8kRA$-oA*HP|n_#y&1)|&=wvDA_mVZdhX&oojvsF#-nX`^B>WK+>wHD0c&PZL|elrPS9LhjQuz2$=v z>t3>KdgyHqwuNY~>)DsOcFkP^N-KEB3{^pQSIzEGk_c)}XooA=zMgP>1>EkMx-q$jQ*X-#S`lAGt_7&{B zs{M86g-iYBz|84mMmiPH*dBjivNFy!a%Z*A_V9nJk@^*u-&{me!1%VLJ+3=>AV_#K z!TdWdnW23&yDb%~cU*MrM`ODk{;mmq{bApn$rBj?;|5|DyTv+&Fewr;zR@hVr*iAq zBgM%)mD1YPCfGe1VykdRrhexYRfV_@uY30BRD51*LFvwJU?qW(DJ>(?{y!TlEBh_{ zdiSoTKfG*3RdgxcP%HO|g40yD{Aop&Gd7RmVWzCDX0tca0UMeV0((*@7E*-_N5iD2^=fO^;}*;BxC*9P zEK>4q6=|roX(}+rF{J?5vuoM7_>CYMaZVrb$zzMtk(lP-aI+)#qa-6+jn_ZDPL3ka z%apH#(sm_Zb-#WGcWftk!fZRgxp=V@x3MuTovy9o$6xaZ%5am87j%kF?h&1jA&iAxfNlX8rej# zyKl+|JFHKc#7p?3uEA1Uomb+2>)Tb$A2ZDa#EP!sKL#T(6EKqFR^fK=MxtmwPi)xh z;MoB>c1kFXq5T>D3S%iU2A+4pqackw&HKClazQM6K5y{89!xI@Npty87YaBNO1o0$ zUmp2tS{|6W;^Mo!Q8fcGE3_Ewcv@q`f4X1q^bxr$%euLe!IKi&nSbn6U zR>cM-^73oq`$Dzy{OI!+0kD;oNAm>VT`-EYi2gz&>ShV8FH0x3@OWKGR}a17*PMW7 zAyq3;Jb78xC|aM+c9gol{)u~6-Dns#U-2w0A_5W>Ivd{WT1GQouHN&M_R;lRcgp&v zuRk4~w395gI+6}Hvb7f~s>&3vDmKBG%q>#)e2U^#=<}j&L8(iFDD_@Kljc%{M z%m04gc7D9;){ibFUO}h#S9HExe|K;dDrWY#z2DZ9&GEdoJ%F8aYQDhI?Gwqmg0;5W zOCa^a*c0uCk`YxKBKIKtRwM1tWrQ!A)N2@}+BJb**{GbDFGBS10ARj!L)Vzi#R~R9 zwKlG%(_JS-D@Y?h*s{?qOssryRP`7DZ_?e$@nOg*!p6GN+otN^JY+EGw6ht}`;}Ca zB0)LUUej}U&$-;cWOoTX)C_FQe?4gwG1#y8jyTC&ab+p2tM>0yJoGqLy@TU4G(YHg zAF5R8G+K#jj>|9V!!x?-G;!o`2@tX=P;!2Gw3IT^i(|^!W<3tN$}X+8` zW6NSVd7K1J{KGYiw=+F1A`XvFvZ=T)V`-pXOT(%g-+kxdW8?aL;E8#Nd&D+=R!y^O zS7Pu{=tb}&UCFDtKj3p>%uFgU49hSi!D+3*PRqJp?B&-%Sq2RGiZEMj_rGlAhIk9={)WCk1QIZ4N$3EUQE)LXkUTdBYSTN75$uqZO#qd`+ARPFN*j zfjgqY($^@EGaSbswOfw+%7egB41KHj^ed^9AevpUC(!P%04{t_3BR`jp3<9yC&E|@ ze{_N#LY(_r-UU~1f)JjvEgsoH=uuvlzPS8lckf+a8`lUpG68SzNTZm33O_xn>!EKD z&4AKPA$+g#hP|K1Cl?9}UAVsl;tun>OYC3AduH(hfYL2z&-NPRx4L zlDJ`cbkYW06J82!<0ZelifCnn1(pV$Mhy-1b8Bor^xQkniYg87t|)^J8L;AEZ(G~A z50C*uuZ1l^qM_iWw70q0_&b$nk}#7xH;|hwM$=%Y`Xj=7x?jYfm0Q)(Q5PAb7v<;u zp&1!)AWw;doQ)G-z4|8WzDOk{WhF&mi~77LM1w_v>>Giq5I$Y;^mngW3jAT*(9EJh zF&fVWZ`%qzS-oQb(mFpj=fElc-1)$dfXP4HD;g5@)SOg!qrH<CTw1nk zO^cp!9}3LAG}EsDC*+ODbtilGgIQxpFoGOVQgAl>DuwoD3vcdh9V~+$BV1~H5_H1$ z<(_bnX_H@GKRw=W_Z=#r2z-WNVc#)KtTKYYs0lIm_w)H7xILRMcUum-0f3PCObcKn zd4H^5QE{c|aD1xJ#GCRnnr4RA7?LGNi0%MsFjI1X#(1yig+PR0UBTKKEYrWV1BCx7 zA6lwK9IeVANjV7%+?$_jRd;tge%f@A4q9t90t%Ie|I4I>4t7m6aCdEhWXD;=T z-S;Hib+tEmZP1(*-p>#{#*p}%K>U>fuVl<)WMRhE#shb7=8C3{Ipd*ns7aGz&YlPD zwsL{$fbRu>9NkH>sNh9M;N^Zl?^PBd!*R?iS9_(KCZ z;Ea(uW9ce-z~{XOUNu+2G?V*x^liCGPkN$#jYB-C06p{KgU~rhcVVY#rPePchWAj2k;!5Q#X2}dKzfTQ-U zh3@?P8I^*6cM_vTea21bMaba?6jcs^%tWJ4MN^Kg$6ZbslYQCQ8>QiT3?L(@HRFmO zaC|sv;nscuoT5FLJ0DQu@QE5ISk^!sEpJbkHdvw#@*QTPwgKp$Yv8C!{P8*g~>G^2>jbdbEdoYj#dD{?j)!J@>%7CEf49 zJhGW3y~_sse?5f`r<@e}xfD9Z!^&x5Pjwb1Vc^Ko*tq+ZXyE7WwXqURpbmFE&Dlv9^{0F3nReuYpNL6s{6UYt{3?I`hHunec!q(TytX44*E4#akP5#zG^+M- zHPk@P1Ol8VeJppz5h)O_ELQcSw&OxJ54K>(BpEG9`T{za3?Rj+Ku(x1ru=Zq(7oIN zg%%63mEcPYhc0kxMT`n}*rIprIE#W2Jz8>nZp4Al!5$_}QkZ_N&KP1wO$c$>-oA{j zEvGPiPi)7V1}_l=e@(32qAfrR62|~bibAjgHAYv?0`wbD|8lkOGjMOQ%c2BYdx;fN zAk;uwBIwi-Ujj`eMxotv5+FkZubaRM*9HLbAai`91o3HABZxX24~yUX-@hcEQ|;J_ z>*gzcXwOv?f=EM13SorX3?f}VUB;HLF5d+$CuX1 z1`2m7REdyLwrKnDZ=Z?3X8?~ca%oj`T*N(}rbn42KIXH8+ra^8VGd}vIw3n5&&$Br zV4Bwr1!(iXaF&8C4eFSl=aajHX2V-lhJk_|;}B8dbjdm+$OTX}Qb9-^K*%0>COKLf zHZn!*#RQzJv#|R?-x9TY5`?3{$TSu-{=#w$uEul%!!&_$_ z@})ufDK0sIf**FcD7O4Nh!04){Hr^<{jaURgh#g?bqN~C34yHSt=A3;bRhGNvqbMW z@Dzn3SgC(YFy0YL(xZMk_^&V&tv$N6)MaiUNBg&Y=ijiP-+mA$S7wmZsc@yh ze>rppwjgot{*S`|N-%ds!Q24~(Zs@Csd|FCh%>`{<*4DA3?vN*MJY2P6ipKEI_fO#Y0nWM^gamlnoI*XJY% z@yPryG0bm+e~02L LYTPK2M+N;K9a1TI literal 0 HcmV?d00001 diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart new file mode 100644 index 0000000..3c9d781 --- /dev/null +++ b/flutter/lib/main.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'styles.dart'; +import 'widgets.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Workshop', + theme: AppTheme.lightTheme, + home: const HomePage(), + ); + } +} + +class HomePage extends StatelessWidget { + const HomePage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Flutter Workshop')), + body: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth > 600) { + return const WideLayout(); + } else { + return const NormalLayout(); + } + }, + ), + ); + } +} \ No newline at end of file diff --git a/flutter/lib/styles.dart b/flutter/lib/styles.dart new file mode 100644 index 0000000..f0f8dec --- /dev/null +++ b/flutter/lib/styles.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +class AppTheme { + static final ThemeData lightTheme = ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), + textTheme: const TextTheme( + bodyMedium: TextStyle(fontSize: 16, color: Colors.black), + titleMedium: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + ); +} diff --git a/flutter/lib/widgets.dart b/flutter/lib/widgets.dart new file mode 100644 index 0000000..e18ba6b --- /dev/null +++ b/flutter/lib/widgets.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; + +class NormalLayout extends StatelessWidget { + const NormalLayout({super.key}); + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Normal Layout', style: TextStyle(fontSize: 24)), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Button clicked!')), + ); + }, + child: const Text('Click Me'), + ), + ], + ), + ); + } +} + +class WideLayout extends StatelessWidget { + const WideLayout({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Wide Layout', style: TextStyle(fontSize: 24)), + const SizedBox(width: 20), + ElevatedButton( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Button clicked!')), + ); + }, + child: const Text('Click Me'), + ), + ], + ); + } +} From a3d37f75840340f0d51c26fc14b0bb53d7fa0677 Mon Sep 17 00:00:00 2001 From: tlaudi Date: Fri, 16 May 2025 15:00:19 +0200 Subject: [PATCH 4/4] Cleanup --- README.md | 6 +++++- flutter/README.md | 6 ++++++ flutter/lib/main.dart | 5 +++++ flutter/lib/styles.dart | 1 + flutter/lib/widgets.dart | 2 ++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 975853c..c7ab744 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,11 @@ - Asynchrone Programmierung - [Flutter](flutter/flutter.md) - Widgets - - Build Context + - Layout + - BuildContext + - Styling & Theming + - State + - Debugging - [Riverpod](riverpod/riverpod.md) - [Bloc](bloc/bloc.md) - Cubit diff --git a/flutter/README.md b/flutter/README.md index 15ac665..b0aacca 100644 --- a/flutter/README.md +++ b/flutter/README.md @@ -6,6 +6,12 @@ Flutter funktioniert sehr gut mit [VSCode](https://code.visualstudio.com/). Eine Für eine praktische Einführung in Flutter ist dieses [code lab](https://codelabs.developers.google.com/codelabs/flutter-codelab-first?hl=de#0) zu empfehlen. Mehr über die Grundlagen kann [hier](https://docs.flutter.dev/get-started/fundamentals) nachgelesen werden. Hier gibt es eine kurze Übersicht über die wichtigsten Konzepte und Komponenten. +In diesem Ordner wird auch ein Demo-Projekt zur Verfügung gestellt, welches einige hier beschriebene Konzepte veranschaulicht und zum rumprobieren gedacht ist. Um es zu strten musst du VSCode, Dart und Flutter installiert haben: + +1. Öffne `lib/main.dart` +2. Starte es mit dem "Start Debugging" Icon im Editor +3. Wähle eine Platform aus, auf der die App gestartet werden soll + ## [Widgets](https://docs.flutter.dev/get-started/fundamentals/widgets) Widgets sind die haupt Bausteine von Flutter-Anwendungen. Fast alles in Flutter ist ein Widget. Sie sind im Code als Dart-Objekte vertreten und werden als hierarchische Komposition verwendet. diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 3c9d781..a67c439 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -13,6 +13,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Workshop', + // Styling & Theming theme: AppTheme.lightTheme, home: const HomePage(), ); @@ -24,8 +25,12 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { + // Scaffold gives us a basic material design layout structure + // Also gives us access to the AppBar and SnackBar used in widgets.dart return Scaffold( appBar: AppBar(title: const Text('Flutter Workshop')), + // LayoutBuilder allows us to build different layouts based on the screen size + // This is useful for responsive design body: LayoutBuilder( builder: (context, constraints) { if (constraints.maxWidth > 600) { diff --git a/flutter/lib/styles.dart b/flutter/lib/styles.dart index f0f8dec..326cabb 100644 --- a/flutter/lib/styles.dart +++ b/flutter/lib/styles.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +// Styling & Theming class AppTheme { static final ThemeData lightTheme = ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), diff --git a/flutter/lib/widgets.dart b/flutter/lib/widgets.dart index e18ba6b..52bb1bc 100644 --- a/flutter/lib/widgets.dart +++ b/flutter/lib/widgets.dart @@ -13,6 +13,7 @@ class NormalLayout extends StatelessWidget { const SizedBox(height: 20), ElevatedButton( onPressed: () { + // Find the ScaffoldMessenger InheritedWidget in the BuildContext and show a SnackBar ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Button clicked!')), ); @@ -37,6 +38,7 @@ class WideLayout extends StatelessWidget { const SizedBox(width: 20), ElevatedButton( onPressed: () { + // Find the ScaffoldMessenger InheritedWidget in the BuildContext and show a SnackBar ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Button clicked!')), );