From a24fdaee97bd5e919d2cf63538b6a07b9c47636c Mon Sep 17 00:00:00 2001 From: Alma Alexyz Date: Mon, 31 Aug 2026 13:43:45 +0500 Subject: [PATCH] feat: session start screen, question context input, and G4F-style AI multi-provider fallback --- lib/models/history_entry_model.dart | 34 ++++ lib/screens/history_screen.dart | 85 +++++++- lib/screens/home_search_screen.dart | 221 ++++++++++++++++---- lib/screens/oracle_spread_screen.dart | 97 +++++++-- lib/services/ai_oracle_service.dart | 188 +++++++++++++++++ lib/services/words_repository.dart | 12 ++ lib/widgets/word_card_widget.dart | 283 ++++++++++++++++++++++---- pubspec.lock | 24 +++ pubspec.yaml | 1 + scripts/app_icon_master.png | Bin 0 -> 67849 bytes test/words_repository_test.dart | 11 +- 11 files changed, 857 insertions(+), 99 deletions(-) create mode 100644 lib/services/ai_oracle_service.dart create mode 100644 scripts/app_icon_master.png diff --git a/lib/models/history_entry_model.dart b/lib/models/history_entry_model.dart index 74530d9..2d81cd1 100644 --- a/lib/models/history_entry_model.dart +++ b/lib/models/history_entry_model.dart @@ -7,6 +7,8 @@ class HistoryEntry { final String symbolicMeaning; final String readingType; final String? positionTitle; + final String? question; + final String? aiInterpretation; const HistoryEntry({ required this.id, @@ -17,8 +19,36 @@ class HistoryEntry { required this.symbolicMeaning, this.readingType = 'Жребий', this.positionTitle, + this.question, + this.aiInterpretation, }); + HistoryEntry copyWith({ + String? id, + DateTime? timestamp, + String? word, + int? sessionNumber, + String? partOfSpeech, + String? symbolicMeaning, + String? readingType, + String? positionTitle, + String? question, + String? aiInterpretation, + }) { + return HistoryEntry( + id: id ?? this.id, + timestamp: timestamp ?? this.timestamp, + word: word ?? this.word, + sessionNumber: sessionNumber ?? this.sessionNumber, + partOfSpeech: partOfSpeech ?? this.partOfSpeech, + symbolicMeaning: symbolicMeaning ?? this.symbolicMeaning, + readingType: readingType ?? this.readingType, + positionTitle: positionTitle ?? this.positionTitle, + question: question ?? this.question, + aiInterpretation: aiInterpretation ?? this.aiInterpretation, + ); + } + String get formattedSessionNumber => sessionNumber.toString().padLeft(3, '0'); String get formattedDate { @@ -41,6 +71,8 @@ class HistoryEntry { 'symbolicMeaning': symbolicMeaning, 'readingType': readingType, 'positionTitle': positionTitle, + 'question': question, + 'aiInterpretation': aiInterpretation, }; } @@ -54,6 +86,8 @@ class HistoryEntry { symbolicMeaning: map['symbolicMeaning'] as String? ?? '', readingType: map['readingType'] as String? ?? 'Жребий', positionTitle: map['positionTitle'] as String?, + question: map['question'] as String?, + aiInterpretation: map['aiInterpretation'] as String?, ); } } diff --git a/lib/screens/history_screen.dart b/lib/screens/history_screen.dart index f70ba55..a264306 100644 --- a/lib/screens/history_screen.dart +++ b/lib/screens/history_screen.dart @@ -26,7 +26,7 @@ class _HistoryScreenState extends State { ], ), content: const Text( - 'Все сохранённые записи гаданий и жребиев с датами будут удалены.', + 'Все сохранённые записи гаданий, вопросов и AI-трактовок будут удалены.', ), actions: [ TextButton( @@ -49,14 +49,19 @@ class _HistoryScreenState extends State { } void _copyEntry(HistoryEntry entry) { - final text = + var text = '✨ Оракул 999 — Запись из Истории ✨\n' 'Время: ${entry.formattedDate}\n' + '${entry.question != null && entry.question!.isNotEmpty ? "Вопрос: «${entry.question}»\n" : ""}' 'Тип: ${entry.readingType}${entry.positionTitle != null ? ' (${entry.positionTitle})' : ''}\n' 'Знак №${entry.formattedSessionNumber}: ${entry.word.toUpperCase()}\n' 'Часть речи: ${entry.partOfSpeech}\n\n' '🔮 Толкование:\n${entry.symbolicMeaning}'; + if (entry.aiInterpretation != null && entry.aiInterpretation!.isNotEmpty) { + text += '\n\n🤖 AI Трактовка (G4F):\n${entry.aiInterpretation}'; + } + Clipboard.setData(ClipboardData(text: text)); HapticFeedback.lightImpact(); ScaffoldMessenger.of(context).showSnackBar( @@ -119,7 +124,7 @@ class _HistoryScreenState extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 32), child: Text( - 'Совершите жребий или сохраните выпавший символ, и он появится здесь с точной датой и временем.', + 'Задайте вопрос и бросьте жребий, чтобы получить ответ оракула и сохранить его здесь.', textAlign: TextAlign.center, style: TextStyle(color: theme.colorScheme.outline), ), @@ -188,6 +193,34 @@ class _HistoryScreenState extends State { ], ), + // Question snippet if present + if (item.question != null && item.question!.isNotEmpty) ...[ + const SizedBox(height: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: const Color(0xFF22243C), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + const Icon(Icons.help_outline_rounded, size: 14, color: Color(0xFFFFD54F)), + const SizedBox(width: 6), + Expanded( + child: Text( + 'Вопрос: «${item.question}»', + style: const TextStyle( + fontSize: 12, + fontStyle: FontStyle.italic, + color: Color(0xFFFFF9C4), + ), + ), + ), + ], + ), + ), + ], + if (item.positionTitle != null) ...[ const SizedBox(height: 6), Text( @@ -238,7 +271,7 @@ class _HistoryScreenState extends State { const SizedBox(height: 8), - // Meaning snippet + // Base Meaning snippet Container( width: double.infinity, padding: const EdgeInsets.all(10), @@ -257,6 +290,50 @@ class _HistoryScreenState extends State { ), ), ), + + // AI Interpretation snippet if present + if (item.aiInterpretation != null && item.aiInterpretation!.isNotEmpty) ...[ + const SizedBox(height: 8), + Container( + width: double.infinity, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: const Color(0xFF261D3A), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: const Color(0xFFBA68C8).withValues(alpha: 0.4), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Row( + children: [ + Icon(Icons.psychology_rounded, size: 14, color: Color(0xFFE1BEE7)), + SizedBox(width: 6), + Text( + 'AI Трактовка (G4F)', + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.bold, + color: Color(0xFFE1BEE7), + ), + ), + ], + ), + const SizedBox(height: 6), + Text( + item.aiInterpretation!, + style: const TextStyle( + fontSize: 12.5, + height: 1.45, + color: Color(0xFFF3E5F5), + ), + ), + ], + ), + ), + ], ], ), ), diff --git a/lib/screens/home_search_screen.dart b/lib/screens/home_search_screen.dart index e8303bb..0a5bc2e 100644 --- a/lib/screens/home_search_screen.dart +++ b/lib/screens/home_search_screen.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import '../models/history_entry_model.dart'; import '../models/word_model.dart'; import '../services/words_repository.dart'; import '../widgets/gradient_star_badge.dart'; @@ -15,16 +16,18 @@ class HomeSearchScreen extends StatefulWidget { class _HomeSearchScreenState extends State { final _repo = WordsRepository(); final _searchController = TextEditingController(); + final _questionController = TextEditingController(); final _focusNode = FocusNode(); WordItem? _currentWord; + String? _activeHistoryId; List _searchResults = []; bool _showKeypad = true; @override void initState() { super.initState(); - _currentWord = _repo.getWordBySessionNumber(0) ?? _repo.allWords.first; + // Do NOT show word by default until user interacts or starts a session _searchController.addListener(_onSearchChanged); } @@ -32,6 +35,7 @@ class _HomeSearchScreenState extends State { void dispose() { _searchController.removeListener(_onSearchChanged); _searchController.dispose(); + _questionController.dispose(); _focusNode.dispose(); super.dispose(); } @@ -49,14 +53,33 @@ class _HomeSearchScreenState extends State { setState(() { _searchResults = results; if (results.isNotEmpty) { - _currentWord = results.first; + _setWord(results.first, recordHistory: true); } }); } - void _setWord(WordItem word, {bool updateText = false}) { + Future _setWord(WordItem word, {bool updateText = false, bool recordHistory = true}) async { + final question = _questionController.text.trim(); + String? newHistId; + + if (recordHistory) { + newHistId = DateTime.now().millisecondsSinceEpoch.toString(); + final entry = HistoryEntry( + id: newHistId, + timestamp: DateTime.now(), + word: word.word, + sessionNumber: word.sessionNumber, + partOfSpeech: word.partOfSpeech, + symbolicMeaning: word.description, + readingType: 'Жребий номера', + question: question.isNotEmpty ? question : null, + ); + await _repo.addHistoryEntry(entry); + } + setState(() { _currentWord = word; + _activeHistoryId = newHistId ?? _activeHistoryId; if (updateText) { _searchController.text = word.sessionNumber.toString(); } @@ -101,28 +124,35 @@ class _HomeSearchScreenState extends State { ); } + void _startNewSessionDraw() { + final rnd = _repo.getRandomWord(); + _setWord(rnd, updateText: true, recordHistory: true); + } + + void _resetToWelcome() { + setState(() { + _currentWord = null; + _searchController.clear(); + _searchResults = []; + }); + } + void _goToNext() { if (_currentWord == null) return; final next = _repo.getNextWord(_currentWord!.sessionNumber); - _setWord(next, updateText: true); + _setWord(next, updateText: true, recordHistory: false); } void _goToPrevious() { if (_currentWord == null) return; final prev = _repo.getPreviousWord(_currentWord!.sessionNumber); - _setWord(prev, updateText: true); - } - - void _goToRandom() { - final rnd = _repo.getRandomWord(); - _setWord(rnd, updateText: true); + _setWord(prev, updateText: true, recordHistory: false); } void _reshuffleSession() { _repo.reshuffleSessionNumbers(); - final first = _repo.getWordBySessionNumber(0) ?? _repo.allWords.first; setState(() { - _currentWord = first; + _currentWord = null; _searchController.clear(); _searchResults = []; }); @@ -155,11 +185,11 @@ class _HomeSearchScreenState extends State { return Scaffold( appBar: AppBar( - title: Row( + title: const Row( children: [ - const GradientStar(size: 24), - const SizedBox(width: 10), - const Text( + GradientStar(size: 24), + SizedBox(width: 10), + Text( 'Оракул 999', style: TextStyle( fontSize: 20, @@ -170,23 +200,24 @@ class _HomeSearchScreenState extends State { ], ), actions: [ + if (_currentWord != null) + IconButton( + tooltip: 'Новый вопрос / Сброс', + icon: const Icon(Icons.refresh_rounded), + onPressed: _resetToWelcome, + ), IconButton( - tooltip: 'Перетасовать числа сессии (#000..#999)', - icon: const Icon(Icons.refresh_rounded), + tooltip: 'Перетасовать числа (#000..#999)', + icon: const Icon(Icons.shuffle_rounded), onPressed: _reshuffleSession, ), IconButton( - tooltip: _showKeypad ? 'Скрыть цифровую панель' : 'Показать цифровую панель', + tooltip: _showKeypad ? 'Скрыть панель' : 'Показать панель', icon: Icon(_showKeypad ? Icons.dialpad_outlined : Icons.dialpad), onPressed: () { setState(() => _showKeypad = !_showKeypad); }, ), - IconButton( - tooltip: 'Случайный жребий', - icon: const Icon(Icons.shuffle_rounded), - onPressed: _goToRandom, - ), ], ), body: SafeArea( @@ -195,6 +226,45 @@ class _HomeSearchScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + // Question Input Field + TextField( + controller: _questionController, + decoration: InputDecoration( + labelText: 'Ваш вопрос или намерение (необязательно)', + hintText: 'Например: В чем скрытый потенциал ситуации?', + prefixIcon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F), size: 20), + suffixIcon: _questionController.text.isNotEmpty + ? IconButton( + icon: const Icon(Icons.clear, size: 18), + onPressed: () => _questionController.clear(), + ) + : null, + filled: true, + fillColor: isDark ? const Color(0xFF17192C) : theme.colorScheme.surfaceContainerHigh, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: const Color(0xFFFFD54F).withValues(alpha: 0.25), + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: const Color(0xFFFFD54F).withValues(alpha: 0.2), + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: const BorderSide( + color: Color(0xFFFFD54F), + width: 1.5, + ), + ), + ), + ), + + const SizedBox(height: 10), + // Search & Number Input Bar TextField( controller: _searchController, @@ -242,7 +312,7 @@ class _HomeSearchScreenState extends State { ), ), - // Search matches dropdown list if query produced text matches + // Search matches dropdown list if (_searchResults.length > 1 && _searchController.text.isNotEmpty) ...[ const SizedBox(height: 8), Container( @@ -287,7 +357,7 @@ class _HomeSearchScreenState extends State { ), ), onTap: () { - _setWord(item, updateText: false); + _setWord(item, updateText: false, recordHistory: true); }, ); }, @@ -297,8 +367,85 @@ class _HomeSearchScreenState extends State { const SizedBox(height: 14), - // Main Sacred Word Card Display - if (_currentWord != null) + // Welcome Idle State OR Active Word Card + if (_currentWord == null) ...[ + // Welcome Initiation Card + Container( + padding: const EdgeInsets.all(22), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + const Color(0xFF1F223B), + const Color(0xFF131526), + ], + ), + borderRadius: BorderRadius.circular(24), + border: Border.all( + color: const Color(0xFFFFD54F).withValues(alpha: 0.35), + width: 1.2, + ), + boxShadow: [ + BoxShadow( + color: const Color(0xFFFFD54F).withValues(alpha: 0.1), + blurRadius: 16, + ), + ], + ), + child: Column( + children: [ + const GradientStar(size: 44), + const SizedBox(height: 14), + const Text( + 'СВЯЩЕННЫЙ ЖРЕБИЙ ОРАКУЛА', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w900, + letterSpacing: 1.0, + color: Color(0xFFFFF9C4), + ), + ), + const SizedBox(height: 10), + Text( + 'Сформулируйте в мыслях или запишите сверху ваш вопрос, затем нажмите кнопку или наберите номер от 000 до 999 для раскрытия символического ключа.', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 13, + height: 1.45, + color: theme.colorScheme.onSurface.withValues(alpha: 0.85), + ), + ), + const SizedBox(height: 18), + FilledButton.icon( + onPressed: _startNewSessionDraw, + icon: const GradientStar(size: 20), + label: const Text( + 'НАЧАТЬ СЕССИЮ / БРОСИТЬ ЖРЕБИЙ', + style: TextStyle( + fontWeight: FontWeight.w900, + letterSpacing: 0.5, + ), + ), + style: FilledButton.styleFrom( + backgroundColor: const Color(0xFF2A2D4A), + foregroundColor: const Color(0xFFFFF9C4), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18), + side: const BorderSide( + color: Color(0xFFFFD54F), + width: 1.2, + ), + ), + ), + ), + ], + ), + ), + ] else ...[ + // Main Sacred Word Card Display AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (child, animation) { @@ -316,26 +463,18 @@ class _HomeSearchScreenState extends State { child: WordCardWidget( key: ValueKey('${_currentWord!.id}_${_currentWord!.sessionNumber}'), word: _currentWord!, + question: _questionController.text.trim(), + historyEntryId: _activeHistoryId, onNext: _goToNext, onPrevious: _goToPrevious, - onRandom: _goToRandom, - ), - ) - else - Center( - child: Padding( - padding: const EdgeInsets.all(32.0), - child: Text( - 'Символ с таким номером не найден.', - textAlign: TextAlign.center, - style: TextStyle(color: theme.colorScheme.error), - ), + onRandom: _startNewSessionDraw, ), ), + ], const SizedBox(height: 14), - // Interactive Numeric Keypad (1..9, C, 0, ⌫) + // Interactive Numeric Keypad if (_showKeypad) ...[ Container( padding: const EdgeInsets.all(12), diff --git a/lib/screens/oracle_spread_screen.dart b/lib/screens/oracle_spread_screen.dart index 6a7ae79..f9bb264 100644 --- a/lib/screens/oracle_spread_screen.dart +++ b/lib/screens/oracle_spread_screen.dart @@ -15,10 +15,13 @@ class OracleSpreadScreen extends StatefulWidget { class _OracleSpreadScreenState extends State { final _repo = WordsRepository(); + final _questionController = TextEditingController(); int _selectedMode = 0; // 0 = Single Card (Жребий момента), 1 = Triad (3 карты) WordItem? _singleDraw; + String? _singleHistoryId; List _triadDraw = []; + List _triadHistoryIds = []; bool _isGenerating = false; @override @@ -27,35 +30,47 @@ class _OracleSpreadScreenState extends State { _singleDraw = _repo.getRandomWord(); } + @override + void dispose() { + _questionController.dispose(); + super.dispose(); + } + Future _performSingleDraw({bool saveHistory = true}) async { setState(() => _isGenerating = true); HapticFeedback.mediumImpact(); - await Future.delayed(const Duration(milliseconds: 250)); + await Future.delayed(const Duration(milliseconds: 200)); final word = _repo.getRandomWord(); - setState(() { - _singleDraw = word; - _isGenerating = false; - }); + String? histId; if (saveHistory) { + histId = DateTime.now().millisecondsSinceEpoch.toString(); + final question = _questionController.text.trim(); final entry = HistoryEntry( - id: DateTime.now().millisecondsSinceEpoch.toString(), + id: histId, timestamp: DateTime.now(), word: word.word, sessionNumber: word.sessionNumber, partOfSpeech: word.partOfSpeech, symbolicMeaning: word.description, readingType: 'Жребий момента (1 символ)', + question: question.isNotEmpty ? question : null, ); await _repo.addHistoryEntry(entry); } + + setState(() { + _singleDraw = word; + _singleHistoryId = histId ?? _singleHistoryId; + _isGenerating = false; + }); } Future _performTriadDraw() async { setState(() => _isGenerating = true); HapticFeedback.heavyImpact(); - await Future.delayed(const Duration(milliseconds: 300)); + await Future.delayed(const Duration(milliseconds: 250)); final words = []; final titles = [ @@ -71,14 +86,14 @@ class _OracleSpreadScreenState extends State { } } - setState(() { - _triadDraw = words; - _isGenerating = false; - }); + final question = _questionController.text.trim(); + final ids = []; for (int i = 0; i < words.length; i++) { + final histId = '${DateTime.now().millisecondsSinceEpoch}_$i'; + ids.add(histId); final entry = HistoryEntry( - id: '${DateTime.now().millisecondsSinceEpoch}_$i', + id: histId, timestamp: DateTime.now(), word: words[i].word, sessionNumber: words[i].sessionNumber, @@ -86,9 +101,16 @@ class _OracleSpreadScreenState extends State { symbolicMeaning: words[i].description, readingType: 'Триада судьбы', positionTitle: titles[i], + question: question.isNotEmpty ? question : null, ); await _repo.addHistoryEntry(entry); } + + setState(() { + _triadDraw = words; + _triadHistoryIds = ids; + _isGenerating = false; + }); } @override @@ -115,6 +137,45 @@ class _OracleSpreadScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + // Question Input + TextField( + controller: _questionController, + decoration: InputDecoration( + labelText: 'Ваш вопрос к раскладу (необязательно)', + hintText: 'Например: Чему учит эта ситуация?', + prefixIcon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F), size: 20), + suffixIcon: _questionController.text.isNotEmpty + ? IconButton( + icon: const Icon(Icons.clear, size: 18), + onPressed: () => _questionController.clear(), + ) + : null, + filled: true, + fillColor: isDark ? const Color(0xFF17192C) : theme.colorScheme.surfaceContainerHigh, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: const Color(0xFFFFD54F).withValues(alpha: 0.25), + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: const Color(0xFFFFD54F).withValues(alpha: 0.2), + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: const BorderSide( + color: Color(0xFFFFD54F), + width: 1.5, + ), + ), + ), + ), + + const SizedBox(height: 12), + // Mode Selector (1 символ / Триада 3 символа) Container( padding: const EdgeInsets.all(4), @@ -264,26 +325,38 @@ class _OracleSpreadScreenState extends State { ) else if (_selectedMode == 0 && _singleDraw != null) WordCardWidget( + key: ValueKey('${_singleDraw!.id}_${_singleDraw!.sessionNumber}'), word: _singleDraw!, positionTitle: 'Символ момента', + question: _questionController.text.trim(), + historyEntryId: _singleHistoryId, onRandom: () => _performSingleDraw(saveHistory: true), ) else if (_selectedMode == 1 && _triadDraw.isNotEmpty) Column( children: [ WordCardWidget( + key: ValueKey('triad_0_${_triadDraw[0].id}_${_triadDraw[0].sessionNumber}'), word: _triadDraw[0], positionTitle: '1. Истоки / Скрытая причина', + question: _questionController.text.trim(), + historyEntryId: _triadHistoryIds.isNotEmpty ? _triadHistoryIds[0] : null, ), const SizedBox(height: 14), WordCardWidget( + key: ValueKey('triad_1_${_triadDraw[1].id}_${_triadDraw[1].sessionNumber}'), word: _triadDraw[1], positionTitle: '2. Настоящее / Вызов', + question: _questionController.text.trim(), + historyEntryId: _triadHistoryIds.length > 1 ? _triadHistoryIds[1] : null, ), const SizedBox(height: 14), WordCardWidget( + key: ValueKey('triad_2_${_triadDraw[2].id}_${_triadDraw[2].sessionNumber}'), word: _triadDraw[2], positionTitle: '3. Совет / Исход', + question: _questionController.text.trim(), + historyEntryId: _triadHistoryIds.length > 2 ? _triadHistoryIds[2] : null, ), ], ), diff --git a/lib/services/ai_oracle_service.dart b/lib/services/ai_oracle_service.dart new file mode 100644 index 0000000..48ab3cc --- /dev/null +++ b/lib/services/ai_oracle_service.dart @@ -0,0 +1,188 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:http/http.dart' as http; +import '../models/word_model.dart'; + +class AiOracleService { + static final AiOracleService _instance = AiOracleService._internal(); + factory AiOracleService() => _instance; + AiOracleService._internal(); + + // Configurable local or remote G4F server URL + String g4fServerUrl = 'http://localhost:1337/v1/chat/completions'; + + /// Generates an esoteric, archetypal interpretation weaving the user's question, the drawn word, its session number, and its symbolic meaning. + Future interpret({ + required WordItem word, + required String question, + String? spreadType, + String? positionTitle, + }) async { + final cleanQuestion = question.trim().isEmpty ? 'Общий совет и скрытые тенденции момента' : question.trim(); + + final systemPrompt = + 'Ты — мудрый и глубокий Оракул 999 Сакральных Символов. ' + 'Твоя задача — связать выпавший сакральный знак, его номер и архетипический смысл с конкретным вопросом вопрошающего. ' + 'Дай глубокий, связный, образный и практичный ответ на русском языке. ' + 'Структурируй толкование: ' + '1) Суть послания через призму вопроса. ' + '2) Скрытый вызов / предупреждение. ' + '3) Практический духовный совет / ключ к действию. ' + 'Пиши емко, выразительно, без лишней «воды», на 2-4 абзаца.'; + + final userPrompt = + 'Вопрос вопрошающего: «$cleanQuestion»\n' + 'Выпавший знак: #${word.formattedSessionNumber} — «${word.word.toUpperCase()}» (${word.partOfSpeech})\n' + '${positionTitle != null ? "Позиция в раскладе: $positionTitle\n" : ""}' + 'Базовое символическое значение знака: ${word.description}\n\n' + 'Сотки глубокую трактовку этого знака именно для заданного вопроса.'; + + // Try Provider 1: Local / Configured G4F Server + try { + final g4fResponse = await _callOpenAiCompatible( + url: g4fServerUrl, + systemPrompt: systemPrompt, + userPrompt: userPrompt, + timeout: const Duration(seconds: 4), + ); + if (g4fResponse != null && g4fResponse.isNotEmpty) { + return g4fResponse; + } + } catch (_) {} + + // Try Provider 2: Pollinations AI OpenAI-compatible Gateway (Free GPT-4o-mini tier) + try { + final pollResponse = await _callOpenAiCompatible( + url: 'https://text.pollinations.ai/openai/chat/completions', + systemPrompt: systemPrompt, + userPrompt: userPrompt, + timeout: const Duration(seconds: 10), + ); + if (pollResponse != null && pollResponse.isNotEmpty) { + return pollResponse; + } + } catch (_) {} + + // Try Provider 3: Pollinations AI Raw Text Gateway (GET / POST) + try { + final rawResponse = await _callPollinationsRaw( + systemPrompt: systemPrompt, + userPrompt: userPrompt, + timeout: const Duration(seconds: 8), + ); + if (rawResponse != null && rawResponse.isNotEmpty) { + return rawResponse; + } + } catch (_) {} + + // Provider 4 (Fallback): Offline Sacred Synthesis Engine + return _generateOfflineInterpretation( + word: word, + question: cleanQuestion, + positionTitle: positionTitle, + ); + } + + /// Helper for OpenAI / G4F compatible JSON completions + Future _callOpenAiCompatible({ + required String url, + required String systemPrompt, + required String userPrompt, + required Duration timeout, + }) async { + final response = await http + .post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', + }, + body: jsonEncode({ + 'model': 'gpt-4o-mini', + 'messages': [ + {'role': 'system', 'content': systemPrompt}, + {'role': 'user', 'content': userPrompt}, + ], + 'temperature': 0.7, + 'max_tokens': 700, + }), + ) + .timeout(timeout); + + if (response.statusCode >= 200 && response.statusCode < 300) { + final data = jsonDecode(utf8.decode(response.bodyBytes)); + if (data is Map && data.containsKey('choices')) { + final choices = data['choices'] as List; + if (choices.isNotEmpty) { + final first = choices.first; + if (first is Map && first.containsKey('message')) { + final content = first['message']['content'] as String?; + if (content != null && content.trim().isNotEmpty) { + return content.trim(); + } + } + } + } + } + return null; + } + + /// Helper for Pollinations Raw text generation + Future _callPollinationsRaw({ + required String systemPrompt, + required String userPrompt, + required Duration timeout, + }) async { + final combined = '$systemPrompt\n\n$userPrompt'; + final uri = Uri.parse('https://text.pollinations.ai/').replace( + queryParameters: { + 'model': 'openai', + 'json': 'false', + 'seed': DateTime.now().millisecondsSinceEpoch.toString(), + }, + ); + + final response = await http + .post( + uri, + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)', + }, + body: utf8.encode(combined), + ) + .timeout(timeout); + + if (response.statusCode >= 200 && response.statusCode < 300) { + final text = utf8.decode(response.bodyBytes).trim(); + if (text.isNotEmpty && !text.startsWith('Error')) { + return text; + } + } + return null; + } + + /// Offline fallback generator in case of network unavailability + String _generateOfflineInterpretation({ + required WordItem word, + required String question, + String? positionTitle, + }) { + final pos = word.partOfSpeech.toLowerCase(); + String focus = 'ключевую сферу кристаллизации'; + if (pos.contains('глагол')) { + focus = 'необходимый вектор действия и трансформации'; + } else if (pos.contains('прилаг')) { + focus = 'вибрационное качество и внутреннее состояние'; + } else if (pos.contains('нареч')) { + focus = 'модус и темп развертывания судьбы'; + } + + return '🔮 Послание знака #${word.formattedSessionNumber} «${word.word.toUpperCase()}» на ваш запрос «$question»:\n\n' + '1. Смысловой узел: Символ указывает на $focus в текущей ситуации. ${word.description}\n\n' + '2. Внутренний вызов: Обратите внимание на то, как энергия слова «${word.word}» соотносится с вашими скрытыми ожиданиями. ' + 'Не пытайтесь форсировать события вопреки естественному ритму.\n\n' + '3. Ключ к действию: Примите качества этого знака как ориентир для принятия решения в ближайшие дни.'; + } +} diff --git a/lib/services/words_repository.dart b/lib/services/words_repository.dart index bdb7cf3..dbb79aa 100644 --- a/lib/services/words_repository.dart +++ b/lib/services/words_repository.dart @@ -166,6 +166,18 @@ class WordsRepository { await _saveHistoryToPrefs(); } + Future updateHistoryEntry(String id, {String? aiInterpretation, String? question}) async { + final idx = _history.indexWhere((e) => e.id == id); + if (idx != -1) { + final old = _history[idx]; + _history[idx] = old.copyWith( + aiInterpretation: aiInterpretation ?? old.aiInterpretation, + question: question ?? old.question, + ); + await _saveHistoryToPrefs(); + } + } + Future deleteHistoryEntry(String id) async { _history.removeWhere((e) => e.id == id); await _saveHistoryToPrefs(); diff --git a/lib/widgets/word_card_widget.dart b/lib/widgets/word_card_widget.dart index 5fc4efc..ddf1183 100644 --- a/lib/widgets/word_card_widget.dart +++ b/lib/widgets/word_card_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../models/history_entry_model.dart'; import '../models/word_model.dart'; +import '../services/ai_oracle_service.dart'; import '../services/words_repository.dart'; import 'gradient_star_badge.dart'; @@ -10,7 +11,10 @@ class WordCardWidget extends StatefulWidget { final VoidCallback? onNext; final VoidCallback? onPrevious; final VoidCallback? onRandom; - final String? positionTitle; // Optional title for spread positions + final String? positionTitle; + final String? question; + final String? initialAiInterpretation; + final String? historyEntryId; const WordCardWidget({ super.key, @@ -19,6 +23,9 @@ class WordCardWidget extends StatefulWidget { this.onPrevious, this.onRandom, this.positionTitle, + this.question, + this.initialAiInterpretation, + this.historyEntryId, }); @override @@ -27,35 +34,51 @@ class WordCardWidget extends StatefulWidget { class _WordCardWidgetState extends State { final _repo = WordsRepository(); + final _aiService = AiOracleService(); + bool _savedToHistory = false; + String? _currentHistoryId; + String? _aiInterpretation; + bool _isLoadingAi = false; + + @override + void initState() { + super.initState(); + _currentHistoryId = widget.historyEntryId; + _aiInterpretation = widget.initialAiInterpretation; + _savedToHistory = widget.historyEntryId != null; + } @override void didUpdateWidget(covariant WordCardWidget oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.word.id != widget.word.id || oldWidget.word.sessionNumber != widget.word.sessionNumber) { - _savedToHistory = false; + _savedToHistory = widget.historyEntryId != null; + _currentHistoryId = widget.historyEntryId; + _aiInterpretation = widget.initialAiInterpretation; + _isLoadingAi = false; } } Color _getPosColor(String pos) { switch (pos.toLowerCase()) { case 'существительное': - return const Color(0xFF64B5F6); // Celestial Blue + return const Color(0xFF64B5F6); case 'глагол': - return const Color(0xFF81C784); // Vital Green + return const Color(0xFF81C784); case 'прилагательное': - return const Color(0xFFFFD54F); // Golden + return const Color(0xFFFFD54F); case 'наречие': case 'наречие-местоимение': - return const Color(0xFFBA68C8); // Mystic Violet + return const Color(0xFFBA68C8); case 'местоимение': case 'местоименное прил.': - return const Color(0xFF4DD0E1); // Cyan + return const Color(0xFF4DD0E1); case 'предлог': case 'союз': case 'частица': - return const Color(0xFFFF8A65); // Coral Ember + return const Color(0xFFFF8A65); default: return const Color(0xFF90A4AE); } @@ -63,8 +86,9 @@ class _WordCardWidgetState extends State { Future _recordToHistory() async { if (_savedToHistory) return; + final newId = DateTime.now().millisecondsSinceEpoch.toString(); final entry = HistoryEntry( - id: DateTime.now().millisecondsSinceEpoch.toString(), + id: newId, timestamp: DateTime.now(), word: widget.word.word, sessionNumber: widget.word.sessionNumber, @@ -72,10 +96,15 @@ class _WordCardWidgetState extends State { symbolicMeaning: widget.word.description, readingType: widget.positionTitle ?? 'Жребий номера', positionTitle: widget.positionTitle, + question: widget.question, + aiInterpretation: _aiInterpretation, ); await _repo.addHistoryEntry(entry); if (!mounted) return; - setState(() => _savedToHistory = true); + setState(() { + _savedToHistory = true; + _currentHistoryId = newId; + }); HapticFeedback.mediumImpact(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -95,6 +124,47 @@ class _WordCardWidgetState extends State { ); } + Future _fetchAiInterpretation() async { + setState(() => _isLoadingAi = true); + HapticFeedback.mediumImpact(); + + try { + final result = await _aiService.interpret( + word: widget.word, + question: widget.question ?? '', + positionTitle: widget.positionTitle, + ); + + if (mounted) { + setState(() { + _aiInterpretation = result; + _isLoadingAi = false; + }); + + // Update history if already saved, or save now + if (_currentHistoryId != null) { + await _repo.updateHistoryEntry( + _currentHistoryId!, + aiInterpretation: result, + question: widget.question, + ); + } else { + await _recordToHistory(); + } + } + } catch (e) { + if (mounted) { + setState(() => _isLoadingAi = false); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Ошибка генерации: $e'), + behavior: SnackBarBehavior.floating, + ), + ); + } + } + } + void _copyToClipboard(BuildContext context) { final now = DateTime.now(); final day = now.day.toString().padLeft(2, '0'); @@ -103,13 +173,18 @@ class _WordCardWidgetState extends State { final hour = now.hour.toString().padLeft(2, '0'); final min = now.minute.toString().padLeft(2, '0'); - final text = + var text = '✨ Оракул 999 — Толкование символа ✨\n' 'Дата и время: $day.$month.$year $hour:$min\n' + '${widget.question != null && widget.question!.isNotEmpty ? "Вопрос: «${widget.question}»\n" : ""}' 'Знак №${widget.word.formattedSessionNumber}: ${widget.word.word.toUpperCase()}\n' 'Аспект: ${widget.word.partOfSpeech} • ${widget.word.category}\n\n' '🔮 Символическое значение:\n${widget.word.description}'; + if (_aiInterpretation != null) { + text += '\n\n🤖 AI Трактовка:\n$_aiInterpretation'; + } + Clipboard.setData(ClipboardData(text: text)); HapticFeedback.lightImpact(); ScaffoldMessenger.of(context).showSnackBar( @@ -161,15 +236,51 @@ class _WordCardWidgetState extends State { ], ), ), - padding: const EdgeInsets.all(22), + padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ + // Question context banner if present + if (widget.question != null && widget.question!.trim().isNotEmpty) ...[ + Container( + margin: const EdgeInsets.only(bottom: 12), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: const Color(0xFF2A2D4A).withValues(alpha: 0.7), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: const Color(0xFFFFD54F).withValues(alpha: 0.35), + ), + ), + child: Row( + children: [ + const Icon( + Icons.help_outline_rounded, + size: 16, + color: Color(0xFFFFD54F), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + 'Вопрос: «${widget.question}»', + style: const TextStyle( + fontSize: 12.5, + fontWeight: FontWeight.w600, + color: Color(0xFFFFF9C4), + fontStyle: FontStyle.italic, + ), + ), + ), + ], + ), + ), + ], + // Optional spread position banner (e.g. "Исток / Прошлое") if (widget.positionTitle != null) ...[ Container( - margin: const EdgeInsets.only(bottom: 12), + margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( color: const Color(0xFFFFD54F).withValues(alpha: 0.15), @@ -226,12 +337,12 @@ class _WordCardWidgetState extends State { ], ), - const SizedBox(height: 14), + const SizedBox(height: 12), // Big Word Title Center( child: Padding( - padding: const EdgeInsets.symmetric(vertical: 6.0), + padding: const EdgeInsets.symmetric(vertical: 4.0), child: Text( widget.word.word.toUpperCase(), textAlign: TextAlign.center, @@ -284,17 +395,17 @@ class _WordCardWidgetState extends State { ), ), - const SizedBox(height: 16), + const SizedBox(height: 14), Divider( height: 1, color: isDark ? Colors.white12 : Colors.black12, ), - const SizedBox(height: 14), + const SizedBox(height: 12), - // Symbolic Divination Meaning Box + // Base Symbolic Divination Meaning Box Container( width: double.infinity, - padding: const EdgeInsets.all(16), + padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: isDark ? const Color(0xFF141729) @@ -303,38 +414,31 @@ class _WordCardWidgetState extends State { border: Border.all( color: const Color(0xFFFFD54F).withValues(alpha: 0.2), ), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: isDark ? 0.2 : 0.05), - blurRadius: 6, - offset: const Offset(0, 2), - ), - ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( + const Row( children: [ - const GradientStar(size: 16), - const SizedBox(width: 8), + GradientStar(size: 15), + SizedBox(width: 8), Text( - 'СИМВОЛИЧЕСКОЕ ЗНАЧЕНИЕ', + 'СИМВОЛИЧЕСКИЙ СМЫСЛ', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w900, letterSpacing: 1.0, - color: const Color(0xFFFFD54F), + color: Color(0xFFFFD54F), ), ), ], ), - const SizedBox(height: 10), + const SizedBox(height: 8), Text( widget.word.description, - style: theme.textTheme.bodyLarge?.copyWith( - height: 1.5, - fontSize: 14.5, + style: theme.textTheme.bodyMedium?.copyWith( + height: 1.45, + fontSize: 13.5, color: isDark ? const Color(0xFFE2E4F0) : const Color(0xFF2C3042), ), ), @@ -342,7 +446,108 @@ class _WordCardWidgetState extends State { ), ), - const SizedBox(height: 18), + const SizedBox(height: 12), + + // AI Interpretation Block / Button + if (_aiInterpretation != null) ...[ + Container( + width: double.infinity, + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + const Color(0xFF2A2145), + const Color(0xFF1A1B35), + ], + ), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: const Color(0xFFBA68C8).withValues(alpha: 0.6), + width: 1.2, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Row( + children: [ + Icon(Icons.psychology_rounded, size: 18, color: Color(0xFFE1BEE7)), + SizedBox(width: 8), + Text( + 'AI ТРАКТОВКА ОРАКУЛА (G4F)', + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w900, + letterSpacing: 0.8, + color: Color(0xFFE1BEE7), + ), + ), + ], + ), + InkWell( + onTap: _fetchAiInterpretation, + child: const Padding( + padding: EdgeInsets.all(4.0), + child: Icon(Icons.refresh_rounded, size: 16, color: Color(0xFFE1BEE7)), + ), + ), + ], + ), + const SizedBox(height: 8), + Text( + _aiInterpretation!, + style: const TextStyle( + fontSize: 13.5, + height: 1.5, + color: Color(0xFFF3E5F5), + ), + ), + ], + ), + ), + const SizedBox(height: 12), + ] else ...[ + // Button to generate AI interpretation + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: _isLoadingAi ? null : _fetchAiInterpretation, + icon: _isLoadingAi + ? const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(Color(0xFFBA68C8)), + ), + ) + : const Icon(Icons.auto_awesome, size: 16, color: Color(0xFFBA68C8)), + label: Text( + _isLoadingAi ? 'Синтез толкования...' : 'Спросить AI Оракула (G4F-трактовка)', + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 12.5, + color: Color(0xFFE1BEE7), + ), + ), + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 10), + side: BorderSide( + color: const Color(0xFFBA68C8).withValues(alpha: 0.5), + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), + ), + ), + ), + ), + const SizedBox(height: 12), + ], // Navigation Controls Row Row( @@ -353,7 +558,7 @@ class _WordCardWidgetState extends State { icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 14), label: const Text('Пред.'), style: OutlinedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12), + padding: const EdgeInsets.symmetric(vertical: 11), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), @@ -366,7 +571,7 @@ class _WordCardWidgetState extends State { onPressed: widget.onRandom, icon: const GradientStar(size: 20), style: IconButton.styleFrom( - padding: const EdgeInsets.all(12), + padding: const EdgeInsets.all(11), backgroundColor: const Color(0xFF2A2D4A), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), @@ -383,7 +588,7 @@ class _WordCardWidgetState extends State { icon: const Text('След.'), label: const Icon(Icons.arrow_forward_ios_rounded, size: 14), style: FilledButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12), + padding: const EdgeInsets.symmetric(vertical: 11), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), diff --git a/pubspec.lock b/pubspec.lock index 1a4ca43..717622b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -96,6 +96,22 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" leak_tracker: dependency: transitive description: @@ -309,6 +325,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.11" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 868d4cf..ecad597 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 shared_preferences: ^2.5.5 + http: ^1.6.0 dev_dependencies: flutter_test: diff --git a/scripts/app_icon_master.png b/scripts/app_icon_master.png new file mode 100644 index 0000000000000000000000000000000000000000..09347075cf58ca4b7d42e573df18e971ece0fb8e GIT binary patch literal 67849 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≧P6cIoG0MMs5X1x=F4&8duXG8hd z|AVNEoI5>-H#Ft@M4f2Q8L#$rky%_?;y5__+(7=JNmMVd`Vc7&?%J zlsK-iu*orTY_M(1088@2J`;r$Z|#y?mA3Cg5!5(k`8`}=gR5oPW1&G9FKLyViGx!< zlof>hg-Qqi8dMnwmBf^APml)))Hbk3CN3IaYVM9|vH%c&NzGdB)gI~mxA(x$7dL71 zeOCP6GLuM60RCf=_EX%5QQd!v696irimZQs zprN6e-=LZo0_f8G9{$GoBi~ici{g*#%2E-6 z2g?5Ns^;}}i}fWnV4+xB!?0`Gb{Qi&2YSFl&cupjMwkW&}e0wPpMux_|kLq&8TSfjC_4gPS+jw4mA|JkE8-B zG8IBgV)#BAdg`{gcC&spLnhzT%Nhj&T71o9QX0-Zp{g*0{a=)>x6jY81Zsxu?|uZq z7AVt($wP8&vQ3>|bQK)|zbnKggWo3mh*w0h^Z$cc^BR!?_%3t1--LQoJ06t=z|0&Lr#NH);TCKb_+vg#3h7QiKvmvj9_)J>mAKp?lq z;;=&2Yc=y@&KBNfJ@_&?-Vt$M_&mE^P8i2A?E`t^=k+*R$nGExFyu@jF}P%UnJ_K| z`^km`FYuYj7N?hqEZK92&+ow#<8rO8*qgsB3E3(&U8qXIW`?_Bm$gOUTrE)=^6Wd| z_Koh?KXsG1k$HTPLwQE#056^LgAc{|7{4AAMf^ZufJ2iv^#xKyN5rU`R^HsMyN)2v z?VGLAfZNg$^;rBGS}uyi2T8h}tq3}x*ubop8_&0*#kU+woT1aQ*p(@>KfhowC1E!= zG>AVgO#$Cgxzz~*Jenr`>hkQIISF+chpqu`CID;4`wiqP@UJuQI+L~X#uxA>y)`D? zkwKo3db=!{^`lHf%u1tb@h_Dn!{5j0Q5?mozv5dg@#>HilqExEHQdvqXH<11ZG0c% zFg*AUsLqp@P6(SJTIIlaJ_GTXik)s zwHI}|2Q2AfbZ1x?;1%B=MVP@)jcyQdU$cR~W1eFljj%0V5~R`zhu`PVxI9YL%(AU} zw;?p}7Dje!NTO0aEQ7bgJ7pEzWPYRK@_qiCi(>bZK8ync2iJBWxqxqTKHIf2b={lm z=zec<(Hs14)p*9(*w{6g!17g4n&e#%{_|^qQ_EI31Kl%4KKgVxm(fSX@6?d+p?68> z0`P{?CtiQazsM>G1%MpqFs*I`4xco#hT*VQ^YD~aXwa;KA!WAD9`h%e3C0`$sN3TV zypL0kLxeYXi{*)?X)cS-)r&lAW%C8{J(Ofi%?_2>Y1H@~UjEB4u+KVV!Iq882hX;1 zxyhBHb%hfcPUxZw94U|IbbjlphMh}zwd`s2HOpIs6{inL;HA>=2@=L=buZuS|D>{= z_>-aizVV9AL#dE^P~y{DNxYMEH#r?SAYs4O0um4~a5(%NJu_qLc%;sL)}h_Tlq0WC z6a)yc=srOC+$`MG@9ZCVoph4xx38&R{0g$6Z>()V^X6B_GSWQzO1k9^-)p}-+n3|C z)cCck22yj&=cDXc<0@A@Xx*~br5S3r;B~)~N_F`u$JLh0#3`od4gmwt{l6m^^J!C6 z4>q{IkmZNoM@_m|HV~z=s^OvuKfJ&^7}3|22nxf-_^zdr$BDx3D8{2skXQ9r92<|&dU!8 zRUO-AIqz!_IZOb5^OacFm&v<4a>jVnt`PM3qRj4LCwppK4zADuqFG#;IE5YVY+eP4 z5jI$-_qU;2?vSK2}km-eA%rbq?QV?A8Ge;}P{&PviI6Uj2wiS@{ki+|PGHXz?`QZYu)vHEKFc zv%|RM`jjQ9c)k=1TzB>Tx~ss_%e{{C-m6I9syiFJ0%HNRY+YTYZiP%o1Tt*ke59Bp zK>H>piYj5*%?HGmAXJ$KwdeEG6xwmQA+rI{NLRNK#giz;4K8PvGIg(?~4 z!zJq7W9zisxii@=-+dq>czmnQHwaoidiT(uXw9(e9ShVK%&?2^AUZnryI9nG=cu|g z4Ql#xI!ZZ*%lo2$( zG=qZ=RUriiQlyN{$bf5rj2w{!FszQ+VHc|J@%#>pRl4Kq@(mH`8=&JTC8_({NokQ~ z($2sh*+7He%9AgZ5iG%@+iH@+t%4bm%%)KuUJ^bB5U(~%bJcK)!!ydD9W;VrGtH+v zrqI8RG)*Awd#n?)XV_RX_~u!OEW2~F8A<*!9AkitxnbH^s9+@eK`J}x)}Rp}@VJ?- zSECbT*3!6xF&vNL^c2dVKv%eNqgqtI)ng{*>qER*AqMh=k3$}NyGR)+uu<`9WQ#-_ z{SUn0f2;a}OH{rbUwqLw4HOm~8B7@%luE9+p-x~HF9(eq6je23M?c>gVH4t|gzI6viO~;q(vCu^gcai3_+jGU zhS1%HnoI>{qZg>?NbqlVg)wHMrW?BPOId=T!ox*9xBHcL;w&ByzS zM#>g(OSV1dPPqclIlwp!bi3kIcAokS@AHQQlg9+=)_p5-=Y^!-+zmhZauBtJwl9bO z9pU22tfDYMFpAphUC#3j=Ow-^aR_{Tfyf%Z#`VfD{?w|hw-l~b+?vrW9 zY=0-xCUIM=ZyHx}0?h_$+z8RNe-jY@VNuTijdXn4e3o|VA8@^bl6FKZgDwy{0B$|G^~}9N zey1{%D*FdGs&Jm|P3PJpa$O90LYn1GqvYFYpUz4OGXb`pcYm=;kmoyQLK2@MMn zpTLzN@`}#^xlWKejDN4&9V8bL(B+6zupcYes=sx*-ZqTrjF#vJzC4@oHM$ahsJYG! z_Iltpsg`H_fdA|@w}n?IwJ7w^>HQ$RY^6;0D?iv8?{jqF1I} z4;f}3YJ5Ou$gY>y*&j`k)NwyD0{you@5v8PkzpS|NaU|-_}$La51=7GTPpwv7|>l@ zhyN|a=ZgQgo*Z$tWFRmaW|&q1sY*J zOvp0wES5Sff5UOH$}HQZdWS~FXrfUauuw(8mlI6*U3yth+lR~H@~Rb_spmGAvWA6# z%yj=9EuUE}q2Fca72F{~omP#iv*h&py)_fVbGKOfs%hPm!aSjAUc_5*T;><$(a(c5 zF?hoaB!Gn|>SpO^%4_Wr4uQ&0Hpx1K(?yK)%)AYX3JtnGLXvSX^!@wPIujyi)YDODzB0EP21n1lC~onb4_DdG@>PIDEpW zF9h*)%d(Mr!_>XEAL1Ei?D+|-7|Phx`C6G@JI8d9+!$)o{+qeoCSGY?;|C3Pgt`CJ zHTlGKnrS_3h5_CnMB~8VTj8?^A(X*p>?c-5hcBEnNwqpJJ@Enq#&QsT{YKg@@7}j5 z`kG27)WU8a9@(qh$8i)a(!YHRbX@A?MGX9Rz2=<+S%E7HdioD=61^idipz(Efw}?E zqA0cf8cxPmvf&7wiwba>*HUXwW>-&F1RvD9mI$fN(sx>wBJ_>CF}xoFRIvgYw(7>J zSGKA?8t{GI{^Ux8(sOa%#pLD0_j6qiDX?<L>Z{)j;NdUx>C@y?&<)vfOA%Um0N|IfkW?IxD z$6&_+`$`AgKOMLpMg>afMI$6=_ESNX2Ht~|)N>)jKP1clRL}>gE%{#k6wu_ASQxyj zl5nQ;d+wCLf*1?h>fL00AtfhG?oz=F)Wv?F;ZOV&WJ4uC`-vD24R%dHAJtUy>8OIk zY+xTo=dk2|w^EEt{#5QX$`@aFwU1}_vQo%?!Ktr##-P1%K{Z9s$FS+_PPiy&);3v(v&!tFJ?xkdz9DIyTL#W&f(S<7NM~y1guc z=i%@F^N{qb|2B>xI$*QB`gyu~))!@}%hYRQK&a9os{=4FeY%fE>+R!46a<;z0^{K z%SaY)z{MF2!WWn);=@tk+N^t_TwPuTAP9Vx+=j?a8#)kPjr~hv(#-b11wZ?Y<h(Z;(JqCO!$*$EOyxAWSDGsL`q?Q`N@o#trHXtJxe4qBSm=#Wy11DP_T?*9P{ zN1=E3C`-8@xtiN`<<%<>|E%zz6gf_;G&>D%PX?|TugS-cAKZJGJ>D?!Zia$M&>0VM zluIZ+I95^A81wUW((!L{i40i7$k0YiEl??B_z3U_R2mdgnhP#Yo11|Lb+9$qMy%pQUk-|0h%|4=RXRX5sfB^AB!8>;HAZaT+-K<`t2zV$?W(Kl(9 z&?kax$n&tF?&tj%9{-5BF`!(?BXK!bB+IDf_|HjG={4&5l7W2ICm7FX0>p3VNRS3Q zH@XI&R11EJDc@{Puos4GIK?lo{~j&1gVoHK0H1@wfI@M)WggRZ#dTAikQ{art(^^2 znipMVZPp&EB*hdCtw;FX8{POzdM!ua!}^F&qm?)=-2@k%!U?pn{VGkW&*4ya-UU;# zU)p{`hVLD*M|(VNnKxy;AGj}vP5k}OpyfR8uc0`Lhgj5wbZUV3nY78G?&Q`5yvqDA zO`L(qvwvHNv-Zbmudwg@Z0lez$BL$=|KP^GqK~Kf`iz_Uk-rH;|tGrFS{jqR&dj_W+54aufKl?<37!0?^r#-a2LZ|Ef4;KZ@Zz6+*@xd3F--F zR`z1b5emXI^%!y1lHC+3C|`@U^sEP7y_0CW3IPQ^XpL#Ym|ia$)|2#F$K6_XX9KT3 zt2MySJB{Kc5Xg!4m9&Y>j}o@`MKP1&ox5yr4=4B@M(_$Zyi27zPR7US31Z%YBdMF3 zJD)`#9vT-+GhML(P;PI*^$cA~O#p3n?~7K`Wra@W%6 z7#Qn<_IPi!oOMWlJk(wzU6h?3=8`=!0K5hSP$2&pIaMoZE-9GzbLYo59FYV{ui`N$g-{Y{ zQC53KVZ;Y&OQNVgp@(1ikhpV8Rn@EiHvRE`oBps6+^*yR8JOzOTX!7{S&)l4P;w?R zg%bKCDtZlH`iTYC{o8kWzij~s#=JHVd#aj4uezkuujRk7W!;Q>9cL!hicDzq|?I$_p(=*;{#V>e0 zM`Z@j)X#0baYoVzO-iV!6l?!j$aWhF;D&^=WNWt zKYu<&aia2-vOTB`y3SIkH?%ZjkSeZFMdp^bHx=_JQ?kFItAVn3Nm zONrEn%h1c56A)IK@DnULscoyF>O}M2L!q;H{4T?!Xe?E>-JmM=k>)Iva02=FNtT3H z_f6+dx&D)Fn?E)Gg0DIwIu*9+{S>isk5~8%RO5z){uBT-2FWj7|Mzh}{vuA&XXH4+(aHun4p7MO8bPgruC9-I2bBFE)*$^#7j^d+z=ghm z-R(A^l837-HN}!<_G~=*N!;aIb>pFBuqAO+QfB5*mw5Jl;l%&uK#wDJ_79E)UvB< z>rq47H`Buwlpe-Hf_;1@>{Y#X5d&%wJx2Qs@D#DeC3wJW9TuPu#0X*2RUr= zVVVP2s7P- z#S}G-O58Wo?oFr?9$d^MJlz*p=Ly*0+N4`$_T|V+a&Qqs37=^5#jIxJbr#DjD{CWY{W~p) zH&;vPz#gNHU9uak1@r{+t2gFCULspujFzY$y>EdqkRmBEG}I_5GFndMNaWi#!=hmA z2G63lhZ?EYbz`UuDGr&j#zX)-X})rFK?x)S)Y}FSi+;4*J>C%{5fS6?>K(~xd&~aC zP+xKY?#vGtz5LqBDK_CQNcf8auJFA}a`=g4}kxL@u%+d?QJ)4Q;2^Hnb4U*PsLRWJ@lFF&{vTl|rrz9W0522+FloLj^ zRfa^UZTtdlNa52|HWCdmaQZg5ULQC=FKGgAuX&$oQHyjqMw1A^Lu^;YaX|M2q${E% z6+vzFb5fIs?G~I!hSQSIH)r)`s8-6o_3rQWTVr^Uf3hJfuI!BI<+Hfv?Fl|6{ql-L zZAd|i7_sz$y4ohvewT27tF?^MeSND4nmMzGB=2Va_m6w+TgBe25-x@ndle%CBf2%e zVRl=!122iCrQ0n+COsyl?|yXu{h{WRVL<(%VaW6tYN$yQP<>yFwJvgzJeQ_nD)QJc zLQa+pSD}RJ-VmYv`ezjzMiA;F%Rqf0`FM&CbRA(ayNl6(x6wrh+!$`akzC5vNPFVg zmlA(EJ^)^qDwkfsv(Ha4b(^`Ka)m2zd_;yIk${9*!2lF#jq;5vMoz!H%i5Dg;Nwwq z(=!AG<<`!~^|t+aZED#yhLiDg4Rch`kkG@WT|w~}|ATXzBFOg=%c;ff3KoFz?5+@u z43i@$vClqo00F`83Q1{iyh-A~qhhBBvZ9Bk_b%i}KgpL;P9V)%Q#Y^lL z`?iKF^3;}`buYvA;CMgM6#mKyXMtMsONgL8H zWM?Y$9<1crUSCl#v5R!xMIjN`mi-V!m&?3<)e>}9cnX&!OfR4FT>T%_sB5k&9es$^ zQ;h7}nd!EBv~t9~0LX6#d`<{V2&-z*xk`(x1hz}**zk!3!X$oZQP)kcV{rc#njs1- zZ)ADcBzA7Pp@6Hgf~NF0#q>!YzDKSg!B`FD5hBzjcQ65<68Uxjsw63X3q;yS^-DQI z{PK~veY>p^nK<|MKSq2{R#VK#vB>WN3_0z~bC|z+0kC6ah`OgV*Y7<;Nh}w>i(L0P z`e(LpXJG>w%JB<)s3xDZ%LcpHtmMkoOmj%AGX$@D@U{iAlGC2Ju#)?I(N%Cg+>cK5 zyuM;79x|i{H6pSH$cGQ8=_%0*3!|pde+{ zyggiI45lO=AW~u{Phdp9SSG?2eBm$g{wKAKWKeg_0g8i-a(-jwd`amE_~PyXbcaCI zVKw`c&{_$sA0Y7BIfn|7U{BU$ZzqOLihR=F{=70XX?EsYf0@v3n2hylwTVZn9U9_P znABU? zdntX?$%>3lWLo4+Lk9q)zsv0e;t{8I6^4n$^wO<cX{bqos7@39CwMr>( zf9B7J*lkIP-wqFX)AFQYWyUy*InVSoZsF8QdowT5>}9TrDE-@^w6k*`2twASuhxvo zGQ6&@zN8K<(;I)EhSuXwzNyZst#}I2wi}}VM24=wb@YyQ`_x`#oQp7O33tjQ0QMaW zJnpc690luic}#*23*{!dZKXNuxP*480hImdi71=`DUWNC_M=hu-n*2 z9#f#phn8W(*cMAWuPanmRE+{N=P7__PH-JEM0{a5*+s01TP(T5PCofy$ zL0$dmBrngU=?UjmE|Y4YOp@1&1aCDNi)Av`E6>5`s z|F$wjU>eB>CreajMNZ1ayA&d(+xn>S+Gs{caQzO8b{^F)@0YPRLq% zs#f&^;C1khN+StrZs#?s4cu?0qQv*Qv^hYQ??v|LRiZQqdV@!;7K5@VP$Ro)%=BAm zDLtUQO!RgWlI#5N77$==zlAlb+OhZ$-Ek194Wi?prlF&)cVIxGObcK9*;2D_;LfX1 zBqaQ_ys&$HtMVZahon2Kg?A^P0Z?W$Z?Z1e!_%&nT+z zzn?;l61>*x5!qI}T^EMdD`B<~$05jy@j3TA8@OUle4Aw^`EfeKz=zpBWc?m^ zxdu7o-q^b?68zX8ww@co6rl8yaYVjMBobzObryems5PWC4k*5C1)aj<&8n@~4w>TSMmAvcH(ViN7FjuUQ&3w;nGb~v z)4gnA#1P)+wFNZYM;zH17%ol$5@ruTz8x(neqRa-FDl_g+pIB!J}KF(U=9ncV|o4A zjYf83gM&yDl|x9#U}kjjW&GJ-QnPdZjs0XyO3ubohWV#;x)v_5Wu2lWpn zlu|H<-mCwAFRFaM!WS%BzkgkC>T_VZ`}Z531LpTevVz>~=h|V3#|`@c*E!C(75%E;zjIs-mk<5@Rv&Ab z3Dlq-!C84?aP?nX@fSDIRZIJrDc8RAEsP4xPiNjvWXA-Ci{r;B%=^i?;va>rz$bS1k-1`kBK&m@*T_cOH#` z?9pTJY7H*U>3k@C^AVa<=(_7rrPOM4JFUWH&ZHoep@o2VS{bSq5KzC~x~j=jf*T ztVmL-Y<3d+!XE{nfrQ}jFyvU3#zAbsbE->H5eY}DAxhawAm>o4k#S5-(fUFAm)@b( zgSDOOr5j5UzV3_)BUQi;l=s z#x~Zh`aqW@&HElax$cLXy-O44b{3goS--l1P!0tu*xe;%F19IkVt8`iHPdGtDqBaQ zsL+XUdwlf%Mc*fom#Wu$DV4AqfXZ0bzTM<(IA2T!x=#iB?x#yVN zS4ZLS?Dm|8o#4vy$tpv>>xO7tNpQm*Xm$N1vKPsJ>HQEm6KxR6g#_1zp0C>1R|Pzk zTwBT+tXXn8p|dC6whhpUIO2D)FBAb3;UC(|M+=-kS zFBOqk(Y7%?+1BCXfiAiL{tb269z#0y0va2EErGE1q2s~A%aTKfiHWE<-m}PO{sL07RuPY-29@>e zF6dF16cK}Fu@zLb@6g*!_~h<-^kqXhk%gLKTZIG!!VC0*C0&I__YQ9zm_1CJczW`2 z2fk$cQ1v1x%WcY9fU+>`26?c+`SZA~WbN0*+PVz*kSfe9VDOwuF4dC3Fqv3{*IXG1 z5qvVlWA=**PMC7*msV4A+kxbYKFo)b1RU9irD34y>Dnj36bUary^o7n{n=mowlz^& zOBI`A_m8e^0OC(MVy`Vicp;md{gqjWT|kgl_hmNVe>|hI!l=IyRgcTIw7|_wDN>YN zER)(~US4bO(%hUH^3$awHbX1W*RNmw@9w!PruO(8mqr>keBKN6}4(QF7j<C?l{>aryCZfE32)si-CvY_7>5q zgy+$c6ReFG&?zM`4(J=#5}f(zQ>^QgE7{P#x7Q8?LrUpH$~*iP3a+lz*l%1ZJ=zJp zrr3t5^#phO674()!0WBYv(>B6ZCu2C*M+adTH$xBYE*NEu-Wm4iH9@c=fieT z^BcZ0sOlQoe2863;4{|h5nDzf(hDk*PftH54^XJcx!<%MGLh>BuIO({u+?K!JDqBw z_ERqQ982y~m$0o*i8L)vu^*7X zEWwDunQp3^kChz9%@9MA!vH>qcy8Bq?&{hJ-{U0PSd@5AA&p=FNOKm;>!!~`7pkfu z8}4_9p28;gA@Tj+P=3mtUjMLwhOvZ55Z`W3%q+gnOvVS_neHY4TxNbuE6}z6K3(ZT zO>lrBOSle=-d*eiDs?&HN&ys&e3wJR5QGVda%N4(H0l z(kUf3Iac4TF^$THSh8LwH3)jIa>xv&_Yj4V?%qHT@6U*S@-*Hz-|Vb>AzhW+yldBn zD^tAU?JaH16c+||p6-}=n$)c3Ki?BIljk;Hq~WjLjEBv!rUh1CLGKa269ktv&VU)Q z1ONw?=K&jg?Eh~{ zQBo!ApaNL2%#D|jL$szy({=TmRVodx)r5KT3T({eX7X{<&P;Vavy(IAv+3b(B@JBS z5k%$Oqa#QCzT8`X@m0CN-Ltq1n@b~k*HF8M?9r_NffgTJZ^n-sD29hAYaYX2yyf9Y zvTFHm`mh->1>f4!pErg5>ucmh9mrSn&}U@nHeV=8F~S_i^Fhx`rwh24p8}Ph6N8S@ z4NVL2g;!)ppNX&&HjW;}WtE6#tlgoFR&h~Erptu)Oy4%22J&MhJL*nX;%8v3ic<3q zrT8H%SL&3vjBB%tyk&*D9kAY&df}pfRJeCcYnRWdoce~D?;J|x=MpLhvpZH zi9-CRInAmzD4pcZs#^0|D2eHzx>)b@xFN}1CCt?4K*xRmlg67|15e!7E8iH(hA^ha z0UR$`8Nx_pczbL`6~RKc%e5sl}3c-Y{zvD%fQ9{N2YRQk3lJQ{>x%2(y_@qV#~+;|&4VQR@HJdP>9|xrUkX zziCWJ=VQ~mPG}}~Hu!UGk$KPfz?XwGtz$*XD_zFZ`e`zEuj{52pGf7y{YaWDekRo?KJ72SSJ?`3{{! z>llabN3n8l)=SwhD)ny-ipp?yr~eB>r%qep}h~?+CWnCi*FPHcP~Gh zm+L=Q{E>jstS&#zIElI4kEtTs z+0UL)_hClLsq*o>;;fH7|-xq<`?@0k_4;7JMrV z|AYg0P3^xmvIuT?CI`XvyN*)Rwv`3?i%@yr6Kk1_gfCc`z|KI`@ICVN=+ltO(b&DO9k~U-XV;;UO-E}3;Hn%ikLQlEAxWNJ zYaIY4RLr9G=0G!kdd@Mm5Rlnow^eJSnvE)ewOIdMqGcfSLqpe9DL^Z9zKYU9+u`fN|X zS>7uPWN7yst5AFp==AY0;_YFjyb8f#tXQ0H24iR3vxr&2HzfyX|07I)y&j%N4ACrV zI|ttu3V$;~TDB+2t0+pTNW%ocvl=YZ<{%# zVtizo%PQ)k<)_JmKFt}drTlT5_r^@nUA0GH&X%oUBz|{4d~mlA9GHJbSNthJ!xP#u zGSGu>5WKm+43WGzTM{@(efkYN=|tQOG$Q8y7JxcRyY3UJJJwncI_6}rZF$~^LB?4y z5PPgtsFnQmNg+W*KDt)MT9%MkLcjli-|#|hjVY|W4NQM#%e{MkQu^LT^5GZX!J?A$xw za;R-TyXntiShFRrt%Jc*bjCoK)G=-O%4>}6PPWq*Nh{nwO4K%Xm@%^W3K@Ix zQfFDs3_q_9L!=katC8$hnB#JT%KYEIKCxpPd<(y@miYGnB6SInYie|KRJCgxN0?VS z1qe+rB71&7Q)>os8RwCov{>e+GtkEhctnb%?=b`HCS+@x2tOE_SvQahm+neyNyrBr4s41xXw^Q`O66i8VZzF1s)ecS2|fZ3tdR9F3-@VpNG zlo8*}AsfBQq1|L087&P(h0tdL0r8(C47@V6S}N@fzzR4E2?nhBT&l@?G-dRMxp8)C zLm;H%DqdK!ykh=(Idi7TojkmY?$z=f8ck9((U^ZDL4#IGL|{#0@@86Hs*M{JWx9A= zex9QZVMD0axY(`kdMA83#Y9q5m5s;*X69cR1*00IBkmH2qC@&u)n}2xhp36OCUDeMws+ZIDy*k&SsqL z)!lX(C!2n1pssq^T^h6RiU#<0FAjhG-N~pz>P+60KC>4sVFasY)tz=wza`2uJh<@SkVP4GPO|6bvATDk>_iin(6K(%%yN5V790Hp_JhqxIa|de|#<(L>nah9>hZ z2(j!S)ii-Tk#i~N61iNn&pQBYgvVW=`{gvArTNdg_2AY31lG@Qt^A*o$P2?F!gct{ zloK(knYUmB4dJTGFGrXvt1&~myzyxq9I?|#$8YVQha3LPhOy(LoVj-M&NRX2n) zD8J|LU2-Yu2I-O%L_lf?(UA=eeUn?e_hWDUIBCF zoVjPtoX-^g2qx9*k>4-=M7<+cZ0YH6HVY_Ikq5gpWPm?=-6^;FziE-XdmUlRYoq02 zpgUwbNBswRTjr0P2h?pI_)hxCfbYZ<{rdWv3id@yYapcrah4Dx(TE_BS;s50)45yQ zxLc7(Evj`{HhvQp`A~EzM8ssQz0se(K=o$s9a4WwvGKDgCXDz}iZcytg;I*o z`&h2hKeba;V?{g3r8u+Jp=bLg-WIkqQabw8%l`8RH9G&beF1BpwAk@Qi2`P(5(}tt zN1Z9w+H83Mew*oc<=&=tq;=gn!$Mm)GUr_G)f@LSI4n5&@6s5Kp*ajle`N^vB}-!* zpXjcw9W9k~dHv4qOCtHmhXNJ9%&|s4*)LW#CRfBzG4xk%d2AR`#KC9p8!X(n0FTcd z7)LShG~$jT5TWrqh?Ma zEIG6bN0A#x`_T4f*^~5UK(7T*va06xE3G@8-gJ2ysUOcy}Xmw^_M=XP`D9Z_e3rlkMvKKj2dWNB$sWBCBO7s=~& zRzMq85MsM{S*4{Kqfdlw6n&!d9@4*b_U86SshnxYsiWJ&2;p97cWt!%Q+Ra2%&mo5 z@|XK~FRO{D2(}SU+rH}K|IKlL*daB zpS^TCnULevr*jFU+bjYJ6RB-{G>sqR|H^s>Odp3Bp7?)V4-QNB@RuJf{cSO}BAt-2 z>|FdeyE*In%z4cJuC(#Z9nFU>;!X%iT3yA!pv?N~S20&tpmRf-!7_Y)%*S{- zqgO!?^C#zxLH*q_wKVCCRCPgW2eGIBK-v%V_A_~l#tarT?VRG=@MBxO3br>oZ*GRU zcr&)b3zI*k>w7XatDF562Dz}nBa}(5F)Sqj- zYRyX?$P!XPO4cC{e&A8V{*$*t<5SoH2j6q_E(PHI*RG|A*AUp`fDQ7QKfsr~|Cf7+ zKc90Js!^vQSub3BJD%5Bt0)hv9?ul9rRKL??laI%8DEaFm2zg^y zvoBhd*r=q-vXaj=sOT*!pARsUK+(v+F-uJ~mywaYh%VG7gZd2t1V0kJk-HSljN&rm z6jNn%yu;J^$w1R}^j`|{8u`rk;m}_QE|g)6q}=<)d4+UFYd1Fk+-aUJUjfqeDjpo) zinq?X5>$O6?K3WklMIKQ-eShoKF6ECIbw`2@(u^*2jnd~XK2x|0f7>0~#8~BEV}n zLUjZ`920Yxs%5I`RIr8n%xvY7hjk8!yvYFowN2k@c_klJ!&sB6<#fOIw2^eS-hx}V zb|U{K{g`!WGB!;R{fUD)A?(fxcZnEpa$4LTI^p@!KK3D5a@{|3#tg1+PnTM*8#qgU zRxv-((ki|Ps7~RkLPkAW1j<`2LfnT~3mMe8y3XT&5}kT5VCcsg5gVUo z*_C9T3|*|%s+;F(=Mq!@V(C=Di7^<%vXE1T-Sz}uFQ{b#FJ`FrS5w6gnkr{W5t!L~ zGIUw(Qz!qM-d?V{?a$l2dHqRLo$BkG3xCOmlTtkDtPjnS7KEUGqn$g7-}>>(1E*Y^ zr4j%DEw~9*@`7UmF{92LYal4^`SuMj(3{e$#bT@tc_6xaSE#sRpQh_v-f%IFjqYlAlt0Up}cA_)hRd~rNXvr#=O@gP-LjaaI z(l~AY)^I)UOTgbw>+Ji57Y0_+&4L#wDdZ9}<-2nTj;z5_51;Thc9&AFGYzb_cH~m| zuX5#ZqqY-{Ur<|`LjuomBC5Ssy&d~1A64J?2C6H9jdQ4B3#bGN?w$vCbzaGd^~RtZfCA!7pcmUi&; z$WS_r`M}G2qL;f$QSqp>(RKFymw713d(;7O?+s-6P1)<_*{d4^M7sco4@R`CjeKR} zcLY5f^D8u>_ha|SMbXo<`Uq#F|LPCt+CPdX%e`Ns@#dJ-OlasaeiG612Lw|Y+hL1%JQ)5VIqb1G2V^_+3kc3sj}T6#WCKGE|GtT3^>Qiqr`(!6g{)V0sNOT8ZFuJtJws-1TWZ!wWtg#oMoR{OVg$TL|DAq;7MTtPG;TuyG99URf^5NSmo zvRc78UZ2t=-ZgBv%)ZW}Q;S;rXNV?$eg1w6@*z{)UGjSTKA|t>atXa1C!f-GCDzRm z2}g)qaiO4eq!Lqn5k6dPE3xBBzuCZJqj6qQ>isy{B-fV(v^zZ+3$}pLXzE*jD~)+ zsh2T4SFcL^{pL%?xV2RBM=2y%9v=?Nir~mW!#zpUbd*?IGb-9xA`P$F^5RBq;AnJy zqhvO29kC&wn)0V|)SNmDW@xXU&Nq>#*7xXM*g0w06I1+Es7kX_xYHDW8Dwppfw`nJ z3p?MtFvy-=hPZmJk8ea8=>);wwR-IMHS6Njvl7%`!S!Pa(0#bf*cS} z;M*=Hal3GRxtTW3vwW9&X>^c70*bqk=({f8)u%S0F;9O)jlXZs!4F~{2=0jJI&89C zZCu%tp@RtQ?WEv`*dns(NdlkuwJhH9&}pjky{;BL;)dMqG=2|@_r|0DbN(6Mh!2fk zEKvK#OXAUCS(%q0AvybjTuI!%gAK=Gw3>5ps06QfJJXrZd1r=-7G=HwbavMZF8MO| z>%@`bAr^0X8W%*my`3>NrgJu6t(d8zZZto;q9eX}kJVNCfkMc1@tsuV_(aeSdb#-s zO0_{lMubRHZqrZ1!L8P`V9m!SjPKO178a(3&Ckvz?K@5IF37R!UMdL}ze+_5hYwzR zdA1pHhhQ*N+DYH_rRZKRPJ2r>T(q|T?$nx_Kcb1%7$=6Hl_?W+WV$MP>#PsgH-xRd z39;FxI)6OLw1$em_1D#w+kJWp;c66;7JaisFQlg7{LZt%VkJ=Vue)#M!ShU9+rKM* z-4A#=a$<$CiRA6)@Wl}yi*mR2VnZyVci;e&GBuPYX;Er@Db>3+x8vVPtvSWRnw`r~ z+!u`oa~olvT@j-xX_(m}#V)=@vL9Z8IKH8!TD1*7tTeX6RNg1vo54rNrxHGS9mLg~ z>N%QC5UdJSh-T<|#Ev<@O51|yCkRyB!cX@y^~JZsXIIL~44N2x=w6AI%ZSls)twTf z84CrxBr&Pe4rC^Yxx^oqlq2y%`kuwcXL?ChFttBD9=m>AEi1EX*1*gy0%KroHDIOL z$!6x(%$$ootM-&gz3q8?oBiMRG+N-_t|b6tbC~eumO0p8YJQl2--=7?QxZzAx8U8- ziNf4GgF$5vDiri8?&|Bh4`q8Mks0;=^VDqn`OkVI7EypSuL`!BVampGU0ILj(eNv4 zPqT^e#sMR+IzcBXE;uIS8|v}z zR0QWsC>#*W#mGk+MOJ8qRKQro@z;BgfvToc&#&jR1yR)2tK6@=*V*kjLylsjrBP^G zKgP?Ou)Mm z*9^fg)NN-{^7k&Sg;sDRYtQ3i(^F9tXp|HJKT6EGw9zXS z&V4I7OwTL8Yh};qH1AefP;vbC%uk$~hJ7p1;^eJ|7|cW8fvhc#d;445n;WZko<#0% zamu#eb}JL?g?=`t-^tQBHIWZCKKOPK*d*!cd~#pt=YLJyu`up^wrKa-FSGO8EjBX) zyS{RkrVyH7TG#s*&yVO-TzIg3kP@aI1O9iNqlAHHO?rZOv-`grg{rI0-*Hz^qw8}- zz)_yR?L@U(IMjlt;0a{JbW$f|w7c=sFl-+mgZAs-TNZ+FHA`NYFMbLp3+ z5Ip?*v}X~EJFcDGZIAAbE?K{prj)6BeUrZ%lTK1ko}G#!4QmuozZF+uU0K2tVWXd* zaB7yujz)|I##3W{@;|}sYjvOazR7xjNP#v z>i(V(3%Xfl+3HhbH+3R;QcSrfUtRrIuL!|+I{7mYGxGK}=TS0KF?9F4kc7KhqEwSkpUOX~H_~1nv3Ae;Tzo$>ZSYyowwz*D zY)#hVHl~tBH1k&q-JOs5)!a_r7e>DjF)qHAW{~exwwo}Dksua-^n0r^Tl;sCq1C?B z_cW)7b=d19u6H@RkdJ*8#X9L`*Lm&Txx;sve@4N^QEBFw!!V5x98~HaLA>@$EQ^d& zD9PJ%ysd4owUw2PEHo<(13D+AUsER+u?I<&7aPtPPPhT+)0z={xx;d0)#?;*KsP*n-){ovm32 z7jb>*cFTx^yI0;dH?u{8Nfq;4eTiSY%6&Mm+3S$t+2zZ_mqkCUz_4=0RkF@OOkrzN zMKTT$&+wU5`t=CxLBO|FE1Hv+4diuEOc%whki$oCgOlwGRVvwjWl&6tv8!{v7^Fli zetW#y3O^&~!N)zz_)RUPFyiOsX7xkZ@i%4)h=E4+3^Ep26+wpv%P;$9P?TE%5mU$y zp!AFLPJ_McPG*s!S1`UPzLY~5KNp!|V11Mn8jwA8dX4?wOiMx)F?*Y5qPlb{D!P~& z7De?Av^H}2@#}Ywp0oIpZAScBb!}A)NKTLeC^hNX9Dj5E{&J=yS;M6e{&Ptq;8@7N z32S#Zky%lwRaN4 zD_wLvt-E=fJzEt9oO=!Bp)#Ku`(nRVDuW5mHNSU&+Fg;Wu3bW?QV#`p$Lh*k_GxHg z0=vKNgR%v?EEHtY_o~VS^@?h#<7nKTN zdYFln=6z>`&!VrD`D=9%Q&d1sB+qE=#tm7FIiTyN)l z*K4o$_;4^-gd%h=yPX;JS?z6J1p`(<){kHr!vl}QFauFr!vUm@io1f1{T>sE2aAir zE#{txjLbKyyOK4_%yAWI?GV&3_eOiuZ;z71y|J7GpGi46e5@;wL<`g)*IhQEqgLSw7E z3e?6dDGGEkU6pU7GbP3cILbYDe+44V6vaTLVdMIanY9Z2xc~fZbbQ=<1v9gJ0u^+r zm!j%x)-()02%KALL1Y z7Z)FmuA~m$XT(|et?{=yLODM?gZ45l1yS9p88*-ULO1l-yPi#N@%Qd!jk^tjktTwb z9GDp!3*}I6#c^knL7Jl0Gw)7X4*wLSp|kBuf?|%BXAgve4CQGf{xB76EmvK9kHCp; za$5ZF%4~wnp~;$;Nux025T2pQVH9UE14RELZkLG}%W&c^y~+qOv^83$L3x7igw=RS zC3Aq7b%!tt$w=R!LZ3)Hq**7eXH<=Iqf?yDmqH||3+J#&9&}K__J_hkh|G==zjjas zksrrw+1D1Mhb1#!#PtMW3Oa_zCt2Y^dWpRFwrJkG9p`Tf!q^0+A8D+SX9egXs41-z z52!LZgm-zvTa!>m0%IzGUcHpRHMD{>wdu@a6Xc+=4YOh-PKy0NqZ*OAKPcZ(NT|SD z`Juq{2g+I})Y)WnZpS5tNe-w>rPm+K_XUrAa~S57OR-7Akc^w@U%QY5r2*z$y`JX+ zpnZZz=`%?VjJy2B z7s9GW5O8H4@#pdA*bE~aa-M3WimJ&d*?`sGMCwmFQ%h$~ZUYI$jg;MEwyg2`5Q!1M z7St}Ayq+kEumAh^J)NYPWd`f1W!v&s^2fs%S@|GP95~bhlUyKp@!RH1(4?q(5D&RC3-=|GrTHP3FHfFW*T^A^_>Bk>C-mtKFU# zRvgum_y7H=z^bG7I+Vw1k-qPX8UaYq_(ECk9qrKbFO|O4+tU)JPB(4;2XJ`b4KOJo z_v-w;jps;PRRENo;i&fEILXE<@8)u85-K&PT%Lb>Sm^P*8Gb*LCP;033$>P0bfTut zf7GgLg5a{74;A_5zORjmev>Tz-!kCO=MDdKuH3U%R{Q)vQ&IQ57Klw^*tSsvExiYA zO!dat7oqhV)>)}fCVb2m5+bNXf9#4L)+sPC?mi*T{4w-j!>=^-HS7LvhjrR35b&S>gIb!0;#c<1 z@`)YY(dd-ZVV=-x@t)z@LO|?X^FA*Eh!nQ9-jTfkk*so^Z$EhLza>Lh9`jL8BKSbs zwKhLgwHn6hSXQrmloFCG>Y`+V13su>2nzV99DIuqM}`%gioTx+vDifl+3ZWJ<^P$s z$2N?8*7L9XPph{i{IVHm^yy}Wt+OWkRymObyPpd}D4Vg&&%ZilDzf!cAE4c4Ghey# z7dcAuZhoA>?k-jElOrcc_%LdKOAWNa7Z?Zeo)V9uVLhgv;X?}l)WlZoE`}|?A&bUP z4owI350}eZIXFxe^y8N`8x2@U-m7 z)3qNMqqBN4H?_ZGBJpHS6&NIjcB9EL8P-MD$P7=GZvl&lTD{Nr{Y3phgCcxL1!C!! zMW=UUg=UuIX2HI+ zab9c@vPssP!kbnNG+%YMl6Y&=jAzrHFD494=D25u(}(YTyGZu3c)I%AS?CXcf4p)~ zv+i9z`4-|(kc9CSFH~bMJ47_J$Jotqxcb}xl*RetMWthzm~N#O17p?;brt^7SO_xN z)&j)bzoYO`X1=I_@{I*hSo@G>U3%^%o`I!oD>sNDNE{MoYia(zEo$DKqh!6#%t7(8 zsSZ(aGTMXB{fpnc`c}UKLSMiFTN%@kv0r6FMf-EuHK?&8zTYL+t1Ge@waq(Yk1rYi z7A!;(d$P*lM~FH;+}U*lgB}N`R(2*=v+6sK2(E~G-{ieXoFiWUF%tMXmr}!k$W(U5 zE51q%Oy5zQEjTt1C)566nSQczphUAmO0Yy%VK^YWXC<`R*uTpKP4FAPWor}WI8-jZ zf6Cty-kRw7G}7RSRNFOsibZih>HH?K#p%_O_K zwnv1a@S`$9&;%pk*He*jJWqi7M^24N01)Iij|!FOum$U?+?p{CF6dSc&l9LdQVZq( zQb2pv{X^XQHSm_ya~_fB28LpG6G<&v3GpuKrD=9?_B?Qp#+0?Lmk1&q8+PRa;yl@M z1q&WKuUEr(K(|EnGNi$B9&Mg(Yi&s?S63G^r%Zoy&ai%1t}KmM%ACH-f=z!uIUdFZ*(#7v}RViyXavCk!0#%r09PBQ27Q-(65h9PfiSWHhTL! zkwr0C_*U1y1%E4ZqmKGrT$>{i4XbIyO{rFPI}?+4F5U9|;kZz%f6^Bfi52IdSVj=u z;E{S82Ta~rsYsQ7&UydAz0lu#gQJOD=p;&<0RxTMrMDWJRrtd@M6ZHhlQQf{^`rse zwHnW&sKS6d{AR?*iShqf2v9&f@~64M(0XP14IMJ17=5L~Z9*^-dm(8)Lk2ERccnIV zN8UOWB6fzF$2=5|DJ^k}{$*UuF5~z<`gWs*b~$Tp$)ho^{H_20Y|Zf$zB$j)e9^&@)< z9v>U)kzZ~jF|R4W9RxO z9D{lIMw(LBY*R_#lnyBrRBcWsQ41(ZS64JIYaX_my86ww-w#D%#xC|05cGoqV`a0$ zC1|oW#fgABZFRYJbH3nXqO_hZzg9*9!! z*s4W;=AkY7V3w=akL>)9Zde?qA(|Hw2K#E!{f*F%FZz2Q(yS44TF0~s$_%4r+{c`% zI8ofzdXzS25MSUUX3^e-_r>(S9D>HRi4dGs(*#fy3L(aZ%d=iF)%&SlJrtGQn_ZJ$ z-m!XH8e&j&J(Dpohr7El;H^@&LAG8%O~2F03F!;YzGyfW;wJAq$o*nhWSCy-17c+D zrT{Aq5xzt0z>Fz{PbK|c@pzVAGyW*%GQF5n3E|=5Qp1g=1OIAq;2&%xqEtMU>{PGVe$;lY6JY_M9)tmn5 z2%s9_q1;|rd8O+{P`-XE_p4vEEyf2Q#Peepo_{7!Z6p@}U2{=f`7i;Zkdme+nlh0a z5KUeZHJapo{(XsXWwH{*PqBU%{R*B6;Ubt}zn&R}XqMEnj_plOPPSK?4SD5&OcS7E z1>XC?Uolgym@qex&>Kz=S~)9`A8W=g;x^Q#&4Rv(?BMMwi!wsUtg}DYWU#g`*@nW_ zvj#sxM4a{Yf;QZ4DaWt+tj}5!IgoS{3aa`&?W3IMde)nl+xb5>}>jN0{0x9i_fRHGCyG-F2O=8>1pk7 z#u(fgnH4&Jqal05X$^+~C)G*Iu>0}MIC#@?3Y?rUr9fF;`A+M+ZDH9%u%nC~`Ta5}^x2K;H}XzcuNdBS@|eHn}&u z6c)B(M=Dl7Olz0u(6v0q3H@rR(_QzS;i&skv0AqB|9nKU zCE#F#*3?l6AeT!{?T%DhCky**G_}(S-;nrJl0MpMo~TUNbFZW*|vA5 zC+K|Ehiisb+JqcD4Pm>?uVj~05tGX+wOucn;3k&-gb<_rU=6QU#}~-kYsF^o89NpC zw^g|;*Pca&C8_HS;yDh7s z7?_-7zz$>RAd_01XZ2jFpJ)$lD{a@GJ4l{iNa8D48>Vx(C(e%E%>^tx6QknFp=IOF*IRa*>XO9>j=4F1^8XtZ(?SzaK<<9o#px@3n zvw{rE&&u_7R>e8~nkME6R#a9_PAP~O)?y)P7#iSZ)!?me1}Bjm5QuLZ_G8e}sw+1!)&Fm*R`JmtUL&%L1KSJd=3-#Zp%h!4qYrzv2tdMRUxOuxmaJs&H zm%H0#L>+d9n%ydL;3C}MC%`ApjI4S)FV>QKAqG(eR6cjkK(qy*3BnA`X`~Y8!aOQF z>sXD)+fu(b2-GDE9N30kg8OMWFwrWV!2;tm@kucaXPpyihMw%^SlP#|q$)J_*FzmO zI=>Fd{g817oTCIn-eF!ASuf+kC4TsBz+Evv!a4CmvptPZd&yKFGtc{}m}6sna4x@dDV*o|X;n!qE}uPB@K=B`^LhD+=6=8HxOZY{5z zUjIA*#5l*r^LxOKYO(F2gjcd#DK&mE*y-QF<^ImvId0?!>?KS>QL;#@A?4x)mLENx z^PqQ6#zdg^UvYkY?`)HddWl}Iu%6BCFRn3&B-)Ba0ikFThEH~Oayu`jII-f6(~&vF z^rf;Rr#dBB#_ULX0$LK*__E8$%4Vp*99=p=oF~4$t?+fs(hQ$&cE>gl=34ntm7}R3 zE+qapjtEOo$NHK3d4OUTykRdphxiDXuifz8) zo5JvrXeSb3CU@SE-Lzyhl0FmLQm3G|=Ah#5MhV_dioS~6dHL-nmaZRrmqoW1gB zF~s|$CXmCu)f1;jFs4yW*|dFu0ra77d>b7^-wm8V?j9nHuHN{fWo)i4Oejpf-Clvc z#M_$&?bZd_X8C+GDH<(!d$dJ5Q1#`l_YVYLuNt(SU(j-VMFnN2{O41qfD?m--M^k- z;FV`*{|2}-(O`X_(p_BC76XjXsbxE*7rx^Ncs))U;*}8`zCQff<~;RVP_mi*!DN+v z)rK&NgTTULN%CHbs`Zk9XFVCClSwQklhq#`6w^lbSQ`BsB6dF6zVbZCJT&a8JP?m5 zcLDGU`Tyq?_MCKL`bTD;_UqkB#yVreuu!j18^uR~40HxAQb-R<1(E-;!@7yi?YsL- zbCw8keobm$`$?f5dkIh1Mf4u&TO@z?hJNwbV{@06*l)-jTq=(shX!ex&G%>c~X$S zJy2N{q7*`aYcSoqsUE%43(d!g3<#WC#48 z!V+GkI6mhQIR@BJ0r&6oLDjjAA|kL+U?%izclPnLgqVzx0epWK3R3>%iT-c&QA_Suq}1ZQ|o2p;7`^kthj$mmlkN33={& zkZ;J}9hiJ!FVCN$QNsQJcPD z=}lSl0ZILs@&IS306>UI1+2oocgOr_$y{##$xW)zSp;=o$wd6a|2SL2>l@hSgZgW) zrGNjT0k~mlCF?LZ<{p&5rOiceyxD7e^h4v*kFEbM@7T)hOH*I<_(3sg(MRBnCuOA8 z>zDx)qV6$%&hVAo%++cWLu>0ihah%CFsx={IFoMJ&dSiF8!K_XN?nq&#b<#sH^sO0 z#RKYkf=ZD0DLpX7HULc8`f4S*JuRatg>%9Xv;l!OqK@B0`1% zdYF}xG%SuJ>hL_% z&wQ_^W=ysW7ZACNiP~FJyMfFZL%t1l|BlcvH3mLMUB{OxI1J5xO-7qL$3>@U-Z!(7 z=}waxLC!m)-mC^bHy00~Rz--~`zUg^Q_Abt-+(5lrnS~O5cy>a$7bnffmRW=i`>(v zwTiLVc0KqvHQ1lv&EvSCF=pX)b{<8W&J-_Ts3m=f>Q<*HY%DI-kPwqrDJj1Ytv|XV zPle)P3j;53zR?kW5Vjl7YRUJ$Q9BN8`Zm%z@__=xtzF&kSzI~U2SAHkR^=CBc;^>w z`VOlv6Cv`1OOuTowY&Q_1}hrEj!O_%a>i(ks(!Vw?3r;!PynCDZAP4Z*@E+vH{qA8 zh!*cBZ)@$$*0neEpJOkyi7w4>L#6j+~&qA(EiAsPoMhgO-ER zH7n;auY&RS9;l-}M*kfT>ToCiWQ2Fi_6dU?Xy2n~t%B)3z+lCFdTTL*xDHbYQv z59Zc~_BXaG+nm=oDSjn_We-EiRjYqjyq{D%!KkV|9TuL2G_bI;dSB{&ZH*}t)J>o?D}UeD+}9=J$#Wp_o~ONO)Ww!9W81yy%M+*?2! z*p^Whh~ZLLn$?zZ8TBTZ*2mP^J7t$CcV7#b5*IEcfyR!EixEe-zE@Xt`_W;ijZTAe zaZj+UJHj?fwTs#0btSt{WZ2JL@s>YU3p*;OrFL5I^ockjknIYLI0IW2dnUYdxR`_h z?T5r#rUOh6E4OJXIzCKk zszOyR=)&h4em$kMvxCk}p^P*dpR;gud7&qb!Td?x;nvwEF|;MZq3vo8Q?%7fULDBx-NIv+Xs6E7 zLKCifz#yor!OrkNTlE>bUXM2rW8jH+B~$d-bU^JElR#?zIbmhBA?2H!xrz4HhP3sq z)HBQ*MFdkvfAr4Bun=X?f)|Rs0{@dQh$uBZQrk$@9->kIxoSZ&qZm=={zQCa!}?$|S9ew5{+%0jw+h1~^p7mOuObBl#`w5)u<}^ z<~KS82Rj}!Nc$~LTWudn36Fg=38XBa6`XaaD`-jvXzj&iA3oaM&xVZuoiAuf7sL%l zv@TLQgq)+8Qs9treFjE?!q_(YFHr26k{-UYb{L@)vOgMBNM)PV%*d?%^zmHT%xrF~ zHJ&8jLVdHx9{2ZDULjO6((tz-{BN2Gov;6)h5;KyNwjG7japHgKzs|BtziXL7+Er= zuWuj(pTZX!HmLQ!{aptd=^Xo^$^qqal~zL^+$yf|$JQ5t7mr6?FB->0zLik4R_Azl z9@wRJQ1nA>%{K2F(-57<8h9)a9fw~WpI(5ZMxy8VNsj*pkI|amsru5u+esfaLK?&g zB{`WR6nob0ZCATj`N5$7PiS06r19Q(_G@~s$j;i;uT-dEZxaL0`8i0m_$z1sG#(EG zM%vwbvHzLo<@_eIBq?tBQ5#K@{zJ3(*j}B)yz_>)5t~7hm;1vZ6{C`dnp!amJz)LB zzf4buKePB_vHS*o)-p?G74-b&?l1gWpBl3Zn7<9B-pzo5=ev)a6Eej=rdEYu3v%kjm=}4ei5DFi@k>t1j+*Bm7NRvFsbghL~uTc#IRR|a4{_7h+h)x}vU;Gq9 zXn$Q4lv%PVYTp!d=gAVTDZ9!R!0~M^RqBtN(5Kndgpb~*BI}sWuU)18{h*J zd74bONwkUbJaL067f2B;c&JdoJKinQSf2KJpuEgD-!v#I{*&2yQ1)|FyViRwwQgw% zcBO$00Z%HUd*t6<2z242`iUXrR^18Il|6_{0`X&No!SHOx^foC**Pi-z>hn{48*@< z+xhi$#msB_&qw`_UnW%J)iK8g|4Q#-37}NSc~TO6H88Q#4@K4{CLw_agDvXto4HN7 zOln`p@OEn%9UO=heTZKd()#>*@UppkB_X!Sn%H zxhT0URrU>eh`!aw^W03^l>lo&0+O)#7s3rR4q5(-%yQmL3&8VVqeMH&G2#{f=Y~^h z&6m7Vc9KD;eArM?h=>6Z#B`2P3$U&$6@Yc6rO-Z_PfDo-0wE!=#BAST=DwO=zyO%ilcDFSjTw3}R}s`COv|Td`Y-K7^cg#R zs`p_vOc``zVxVB?*Q%Pj2}q~Hi242BXpx=N3aWB0!U{BwMO*NYaNbr)aMpFa%v`v{ zO&Wp+r2CThhSN4dNsbil+GxYzNdlN~`xKt=*xP*Fu~R0|(t7@Tq(?y=Ijvzp`>FE5 zTS_Tkr!Jh6fe0q%i%7~O!CbwoL9IAg9x?DeoT?Xs)}B(2*I@;q$tiZwP>6q!ygSn) zPmx3xoZifpIwY}|FXgp&r79YR(wpfp8e6r$*-16&WgXL#e)BTlU(bjH24?^w4ICk) z@Qg>5(RBRS4+}{!8HF$H#I%$y7bQ#)NLN#x`_=5|PeCEf`IH?aqIV0YaF2Zp$>MKI z8+7}k=x_7U;`W9_XG@vV-hpMT^DN7O<6yaF8t-0(=1M`+lv=3t^l_yfAYqGvD=37I_Oy$L(EL^XLFtFrs>Vi)@GN1 z&^j_ME=n$*S?|v1ESX7zRozF|%Ob}T#P{!Vw$Im=_wA484#xIInp=djUfuPU33mUz zt^0L5Q|PVaS1G7c{)sIAPb7WCKcg@>>XGV9bL-~C9}9^(n`4X7noeHkJw9qQ!UXPI zKgRCF+OYpjle(IkX<$zB#AUvFIJJFeBdh*{#m$8q1^OCvqY!OS1p;BLD6rvjh{_D~sn!<)<(0AMAm8(a0e={GK2bNc+TvLsH}kj$gIe zU7c*ijK7L|XYQ#@TUHz3igZ9NfQvHacuC-f`}PBvHGiV80UY?lMg9*~!nSk~qCU%b zgY=ye8USNtkJm1#ckG>80LWvo^3I|N)0ka3X>%_4P9Tfhp|MReo#68}30X9|*_yU_ zPw#^yK`zJzqBR^b?*S&tGhktM@HP z_CGC0|Mqx8lspKO|CSxh+>V{mUCv2jH|GxQ)r&qIwl!!MO>yjrW_^&f&%S_>mDbDV4<;vpC$kKokw`TMZW^( zz;9#(?^`BEmKxm^-Z&S5>s5W6&umazy~hS^8)q|jwcu0q84gg$qO^k+fb|jpG=v#f zbrSGBP)?SPFP++H$Poa$SOB|3o9KHmiFw2LA*8m6pGd36_N%)e)hFxw{922>ifbQ4H$nDL z;S;>M=Cu-~!%qr-N%oePKy_crF`6g6r?o1=P(jZ+z$y^lfy$u9UJvk_f?4!0@7{&zH+xjjI&VQ332O-OAQP9kzID@OeScT~ zJQsafLIPL%g{{U;5|Ein;<}{X)?#>lSU+t*QuknO0epY8=yt`cRqmS)BO+$k?!AZ= znCE2%$GP9e4u(y!X07vBy~I8vI~rPG^hL~7hSP{AfC)Y(AoW@tMfGqxN`TO7M$!xH z4pRmydZLL`COIzPX>`)%)c)%;eN($8QoNfTgxLe|nyn#3&A34;>Q)};E>i&%3jXka z{17}51f&KIzu&?lT%hvWjb0^Pxmn}o$q=?nY@}AsuQ^Hg@=vn4KBc?`)cHl!^eTEf z@MfEHTK}0jGvdaH9#IVf4s#Ek1vY0(DwbNR`TvUh3a_ZTsP8*;Gc-seDIg^sGlU2z zsgg>UA|*(}3=M*Sl!SDN(jXz-APv$DBAwDOGw=0zzxQwWSc}D)wT5%|IXmue*Vz}F z<$_FzYvRW{9>t)$eemgDzJs3kuXC0W2oQ=^m@BP%$qw>B@-iRY2YJk=3Xzi9Zw7=- zkxN_@V)EyGcOPLZ8H?Hz=lGlsF6y@`?Zmy8KY4#43js%C(D>4uZcpkao#efydWWhS z+JRk-dDgz1u{}F zvt{gPtWm*pZ%7zf#&)bPWUzX--~$hf7}r1%kN6{^uKJ*)jgE51eXzEGWpCVEjdzH^ zY2jXZ^EnTu@jA_o(dn4{;&YI`o!Pf#>X#bqOP#DU4R*IChmMzX6Ap}aZ|FN*e!O27i7&hIl_pBp{}7&Bc@SmGO=EhQ5#-{yM^CzP&o z)ak*4->osVQojUKw*?`eap!xK%@m&lI85qfd$Xn9ec$f-&T0Z>$U*JD+8wTz z#e|bRpZBlLuKp3bT>uV~rLM%TvLInX{unC+0~@P!qBVlI!^%Q5%b;GgD0uG?1n3~2 z&g~eyn&QYN?3ytWLziAi=*u;42TW#B60#$RQQ6bRj9|bq`k`p}qA6 zxt%}VJCQ1B-u;Jo`5IIh1!q8Soz%qy_fb-v%5c*`ll%zgv{s#TlF_>9xv;B2K_CuZ zijh~M{AjCpd|qip-mm=bD7yJ?`lT9^)#?|{nS4;;2z>I)%%W5Ti|0PWKr22yZgwve zGH5QhnT*R(Db4VL%)!n0{~V)pQo-}RVgBQTv=}Jdd<-1g0ch}38|pih{#^=b0DYk2 z$24?_BnAAZnH6{2PF&_LgL2eG`)q4)qNW3kWp6%$d^y3xX}a?045Kzdg?F^)8$0_u zWfA9EJGaF(jhN1j9u;UWEuWqh$X-10qB5_G%2r)4b0=u^2CZ7W{d;L|n+Fd2Cl}>% z|H{#)4xhzF2JUNM%U+sb5r-ft56iZ@GZ($n{FOcTX4=6_hemgka7XKhRN9IsxSby) zKUUawmm;j<&VW4NlnGLCH{>+Es!dBeU-O>sy?28mz0s3Uuq`E-&4zVvqK5z0CZApZ z9Y##~{_T}u7Tu@Vs6_!z+>r4QQ*%1N53btpux*z))M{?3S86NpMx6vQoMA7{H>RI>V^be2t@d6TS;}71K&IbH2 zeF2I|A22L_5u8M)-LzgnQ5RYpwj8LfR!T;hXbF$25~Qel5b__5xMYn?!gmn~561M* z@pBzgk|fG7nO4+DN$Jp6dPA7!v8L z%Cgx&A=j+HS818!HzKE(l^Bv|yT>X@OPd53vn*Z~dY(<#zt!ksM7fVpXV>q;6 zmqJ;O_{0dRb6mW<#nZ?%Rh(F}GGD({p5Em}5%LjnxV6_%rd3ve+wO*Th}6g--B9}X zo~LH1o=fRTbxRufmDe9cwE~NAp1^p;@Hg-hvzUwVQZw9 z?5sTFZWHvLnlVvCW_Zh z(!agjlHEo%1#~%{uhhlH@MX!@x@;H@tY5AmWp~g;G-7ffI*L&^S6yi&UqH7bBlCU@ zu^C3Z0;CMVU@mV^j0viimh2xF*E2yb(%cxK+%~grrodTT8ek5c{_`P=QpcAD@8V{iEq!x;ITg;Lrl_8LV*d~ZmVH-%IXhi2(J`;Ps2ECeG$s@ z5;XxYY@KF;lvmi#E8SxSu%*9%)2p^e%A4p3WFSQ|)ecfk#yh;P6!+0F?O?Gy+Yi@o zO=w@3>ABH<<6rQ`jIPY$3l%2zzMZ9|*`*yJ6V%A!CiCEqHdn&|!xjx>x4uszYe3smIu*Mz?<79n1Ip6oHX%3*yzR#BbZ_LjSH-a zJ)362oTksWOqzgbq3@F9mU@)uVg{r>9pr-V>)R)RnSbzv*`PwC)YVhBue)vjQ`PU2 z^{STz(RRhP;ZUyMCTDkv_f#pkmTUgGn@%;j!&A!`;bJ!6hq&Pl6UEiy^EcKrtL7|A ztI9w`#0e;DNK>g&fkr8Fx5tU9FMYNP)&Xs{`<`e({{otRVWCCFag_ zTUyVVo009{A=r;0(AZNjsQnxHR1KZy5HJY3? z56H)J!?S z2w!e;1TIeC1FAO)ORQClJLW{t7@<>^}5oaczoD6GTn{St%6I(ng>hEjFt$&*mg%8~YQh?5{@Km?O`-l1PXm=evY;FMMCS zfc20J7>mmZYb(t2`qrU0X5s>D1@EpBWJNCKg$Ys+!{2|&6Zv;*xd-UqR%43p$k-L) zKI`VF6se1=uSSZn5Uh_M>~I@9&uxc*^%dwLv5BCD2#o*4QREW zE!mH*y?**o`|Xn0@Oy01GMU>ZN!2 z{o-Y8u!Q~m44*CNpTp^tWn}L-Fmd!@28>TrQPlaTAV}kl1h^>#Ynr zzPG5%LB`{tb|s%#3{U9Lv;d%Z_2{t$yEEue9JT)>UiRN2N6!Z&);WBYG#i{^F9>D( zn9>=eDm-SPW9E~%E*g&~rrS^K!a`9?{jvEY`93*eb zcXKT5T)=lR@NbyQqUD(#+sm7hsUf7-YxFIcJQxInDxi?63Tj^W27 zr^(LHh<_1ZdEI?}p^DuvU5yTumiGK#$IWRK9+#CC9zKCxd@pb?{eu|bPpTf>l47eO z^U8J9hvik;tn4866cDg)m_e}{$CBmI$L>3%^fk9LIW6LDA+e!E@9QPAq41fsnk2$k>U|DcL9RJUftxfa6~ zc&{kiYtIO9vh-kDzt)!T5#6Uz!4Im^57Vo$ z&cjo2DPHB=oyhwkEG&Upw>YX&jm8zBH)Vfk_>dM;{JW9-S%9$lTHn?XbUY}JMnw<5 z0%eK9Ho#b2;2x7;RISb!eYzk^$mI1+Ct|Hx5v$SNGRd~|BRtLwZAG>m@%Ae04XeAL z_xNLExpn_agbv;MSQ682!fW@oI!`SQN#|p|^4B6uGNXR-TY4e(rEn$QxJM^L{~Jh& z%R(5egWTTc1*SRp7&v9YfkSmON3SIeF6)||Tvq=%YB72VOkQ`f{TcuHjpEA6@l$Y@ zb^V*UUCc6Z*2@QW^Rdmr`>8)>T|-Y8`o^kD(N?WbAvEN(T`DByJ$EHqOf$W)re8W^Wc7Q}C zj)O=c7Q{IL2j}1fI>E@$Z=zt&3VB?$!VSvmljhYPkdVgZyhaYnX~ z&SZWmviLTKi;GL9m}zvyt^%dRJEao5%M4jJorIrhjHnIeM^1dE%_6$l9+R z1N!Tw16imt#|@3u-I-YJT&$V^IPf& z9M0f3*_H3ILF)WfHQ11(1vwvlhPwA==I@Kkba7!6hHg6M4(SJ0L7I zat2}P&sQKU-5Qw8RC{7Pri8mA}#*#5u}Ap#N5G2!4&fH|wk?UjFd2 zjN^*R$i+ZfElcWQu}O}*xw|ffp9+{!_0?eIKFHFF#?RIkxdM|OgIEIM-1}ZTJ#2;b_C!T(KAZTQgeMM zPUm5N#xto?mmy#;d`dxrOif**^;=+cSzB;yFP60>@!FVaUlKRNvqVnU(q`)a{nkW< z-XENOwB$SgG1%Bt;O`qolu(hma!a#hT%$TtOwjz{U>9oQ(f!vM+l4O`0P7c*n}ghN zmFp=J=K|iVLfjH=YQuL=rx5w&u$|j!MB`3fof!-Ldm&j}9D_sot8LifrD?c6!f#!( z_FD6sjk4L8#B}_?<@sN{z51OY?VU}9v7N*cD*b18KY#OOwRqjwJj%daJ5PfP3RUbB z;*@ZM4#__FG)LGcHwfXgZld?SY4wOZ>J7Cv>T6qywHCwd7SkgASAdo2$qkXvLi2nK z)^oC|7ZKgKl38AZB9CxZ|Hky)_(hMNWyQh{kIfJ#jo*X>vS|J5i=A}Nmd{_**L3qG zzcfT;1U$UWl2+&?X@-AtsIIEg+sk4E(^ZMhZ}&!TN3=72r~^7Wy{GZCQ zyt|e#KUSCZX>MDKHj-+q-54p}Gxv9{A(DwTz5&U_Im+SJ3ksMw?Wb4x5Ru%nO?ron zx54r@L~zR^#i8$A5kxXa_-7j9>9tQ+64&=Ol7Cs?y=}q?lRNN+xIb0w|8mv#gJ8if ze&}f4xz+KNBl+Km2tUuxG3R+-7!l%?DjI>VK90YD&7LJC;V%73DC+n>?%L@|+LKSuQJ>1c>erMu!hzq;qcIe-TjmNm%dBa$3vo9FnDt(+=D#aggEdmo9Oheu;qqo+c9r`YeeK|N zW;Y;-xTs6#aNE&pHD8Uk%k-_BbJZEyi#Drw$@%&A0tOK)Z4WNmq@n%b9T_4=Jo@GZ z=6*acxAkUgt1Ww~%6{&{(ZS-s$@vQf6-PT(0x#QCeqGT&^~N@Wzu>neq#Yq=mT_Mh z&KL-CNvbZ{{%kJ#$A|GuL!a8d>`!ILt; z^beZdEKK2JtMz793DrWvlkygp{m6{fC}=TLDK|`s z57{{yh>)VTl71~L{s+H8)KnmjrbAHtzxa=3fW=tKCp<8Mz9})#_r6;R0Iw!Y69s}p zp;_2o*Co}%K8#yb|Bn{6`NNF8r7Rc%opyVB>l?ZCwAWQbztmWq?YP!GTJY?d8t|F( zmP#FI^cE3LDh3a7f74}N`Bs~`DK|j_pfrEnN4BxE8mC7Nh(*|kx?Pa-I8D9Lwo%RZ z7l#jyous^NY$@Sws;UgNC9sr`br!X5+z9k&0`DB=FmIAHWgm66@O--xaW^LA*UZc% zV)?R15)Do$F*8BEw2;G^)l_v9cP)@4S(y!I4Y)il2J0g6D(+V5UrR^6ZG}J95x}hs z;lHgGEb*=5}(Vezf3$yEjo0a zZIp32gW3HVi`0bH#TT;hkPR{GzPO9l;}6aPxZ_S|p17S}xK)wSw}PPYTD}_gTr&Fp zda`lf?68RQ_j|sJU6$OONK}Rq1~6A22Wir5z|S@ah*Nv!dDQ^gP(=#smE1w-xe&Pk zgZQ!|^{>_>07wzDi%Ec9qpb4=)K)E|cu|^>QDAL4FXJ$_`X1_c@(tnB15YSHgi-tt z>VuN+;PW!kAF<5|i@zj`zfOnuKOb~XRG0&Y zkPY6m4MFtgHxfeQEOFT=((Uhc(eg6KSFPW@pO6MQyMY@BQ6U1JbggZ?(ZjlyR%PfD z7(DzKAVY<=EtesPUzzdP%+aYM)DhUwL?TV2`bqYZ3zgs{o-MF%!WwW}s}OA;;SqT^ z;SS8Gh2rlnOJ>Fa!Z-2FdR5B~>I_}E%3N@$>r*xF7?|E3gACGi+89|?vi?%YJQ0Zw zq|j)N@@`2Dr$C3}iLnV52NyBf2`$%TV42QV(jjpSWs8L%I6mtS;CSGXbbK72%};$J z8Rp8SP_AQ~&`-3lph~!9MsM-%Qh3m9&^r1E22O!IQ`0l{T=@M+gH}i8*RzPkPXJc) z7DVTLakW*TBi3PYuxFR~aWPb;=Rj%@3nm4wkVUUJB|qr41EXI>cnOQK!;ndx_{Kw( z^qy(F#i=%)ojdYe8$@s=<;v#3AHy-Yv_bV{fz8!1mHI?M>Uet$;1ZY`I`@WqdxK)& zVylQ0ofi;Z!CdVkP3jNrDOGa@Kz#*NMZAplPRJ~2$)kn|OHcBI}OB-!2=^jFX z=HncLRFp(4$*-za7Uhv49S?1C_?_hore*lf-($rsQ0P>UdH$l%fx0@Vm?Xqx&)ov( z5hhYULvS5);Zr=-%Octf0`r_atFQsYl;p?7F?M)bds;u9WjdXp@#?86xD^}nwqM0h zX)3oK^b6g2I(2$i?nC!gO;{=%tZrbMBZ6@g&sUf@(MnaQY_oJG6T3QLT&oZ- zi?0N-OXPFmuW@r>cRE5i4OqMNF0}PQ2Mm|=8-}|KL37k)iKSjs#o;hAVby{r3fj45G`?u#abP>Z|*DfZqt(gXc?=#C`jrZKZ zAYt^&gn;!U3qWzDkk;Y@3-`A*3t;`L76WJM^XQNi@D7_l1(#7|gO(EXBsWwS(6Xrd z4l^{1IhJAP9vxrK5bCf<`zOz6=2XN%#Z4AlTz9hxX8HUjSTaPIEND_#GukM-OJLvI z3Gj^681!@ke_kLHl{}OPdGE2*HGQ%F^$JKVhJrhAUGqJ7qiZNv7@ral>z}DaCGmvW z@c=`3w=_8=Pxe`fn(%fC*@Z0%5wx_A4V``=r`sINq~h2ZEQj=>H=pwwGw%SJac2iL z0xutMH!M6OpeG5FI(~v7GBT{Wy5Rg>3J^}4IEEmIwrg+9#Aa#jWg|w@4-nbp0p_-& z-^t6OD8Cs$`e@SuYzJ{xdjP8VG+4Gdj3f<@^a?Ssg(rx874xrQdRp-}h>i9I4AMjC zUM;@=3gKbGhb)4H4BkFAddzbGQds^soKZj8^SS&cU>z++UqwaiG!y#|(FjDC;u)qq!7Ont z-DKg@MtS}jId?iJxoBmJV~aUUx?$bwoN?wLS<$K|!&B-S0_Bk@{0EXeD}AcXKginl zA)HSH)l%X0#72&@O_(vLJx-GV(9#|IGV%U>FY056RJzft6sGJJWJHQ8*+O665b3I+r}<#<3sbB*MNu=CZqi&HOOg@GjWuLJ4tZOPv3s zUU?{%p~hbKU1DV2YwpHs;kFq0dtg+0YLlCxLfiE(mb-wh5*D8F)acR%1|Jz|^vz4D zBIm-wwgX9dtZPJ=8?)1N0Tam`!nLFV4v2SI)q;0_`7mu z)I5cM8g?7Wb-(iN>xftQPdEFKz8=KO3_Lz&wfRmG{YZZ;J9fxKR=athALUJf>s3*z z*9mJFdA@-bCce3}`ZtWhpf_S;=V8?K)QaXnNZilj?RyLrIg`hkZ==6kUBZ39so$MFSvyI|-#%QyFeoUxXp{M$Ip^80$& z&DRUj;On0#^3zJJxcbT-4qwK$(hM$aAFgdulaG=YDu=AmbZi;*>=3{vXp7uwoelaV zD7!sQy^9L9@bs3g+2O^f))@Z1o{i;cP7_QKlhyv#;I*g4>YZ+X?JQq_vLC6mOA0PX z#NkB57Yp{3;82>|>ZgTBHGc70V2yg;lbM{zW-mb(%yr@_iEU3W;5(ucLYMpQpOV#e(Mnoyl5Tu>CAmNIo9$7{Wva}39ucVeEiiCtTslG zRIMGWaIiZ&ZjuTQ1(Sp=g^w0`1rtT}K2zF)1-ZyL^d{~XX0aGyC>M-JxC`AIT42?p_e}2K`{i+SqzWwI zQ-Up}Bcjm(RXxR6W~xW?IQ)z}zY_O1yANc>3nGeJwWx_7ueP8ue&c-`Vc{V}MJr6N z7oxJTqiuaj6}7>J)`*{UZgSoZXMpPM^fB;wn+>~zwB%upJ{4;F_c2-T(G$NSaa)H=n;(Ptk$*C<44rNr~MFIA;6;3}SQ#&IOu=ZRS$E z$5I%gqHOcGsp-Fa5jbNnZ2x$LwNl(N5RU0m?QK4bR(CjX$r|9?W$gpL3Ct3Y8u^;H zJukPUpcUgb(I!+PaICad4qFYwt)ap`HXu0l(LJ7-CMNF9Y!MO=o)6N+AqlglG!?~# z(^7wENwVH@;{dO#V;HFuA*^Lk>$nygo<_8;xR1bOWsk}G1pYk*mu;4q%`jYc1Q+Tv zF>Kf_Xz-_1(h_R=wkEEJIIdEsDJt96mNd5kdi$u(*!(R+MMq#ixiCG_-!*^~PL~{= zYVuDsyfhK1^PS5jNhI6>E<}&+!{&YMggLG4V2QTERC1|c%77so;H>vifBr<(nueN~ zlG))Q#6*T4EhMw@z8ZUk7Vq?)qY@-F24FicrzP+=)p>BOzn8^oE+MI0HfHfT3trA+ zbK%w$j+ny@&9R!J#BiHjL^HpV6vxfb8bIfBM&;GG{Q?J8Uc4K@4ENFFA{pElgR>6s zPFc9UWQcL4-gnPf=nv>pZB)C^sm`(Hu4Z$7#brYN45_&{zk#2U&#hb*$9ZWWg^Sxc z*L(G{JT-mM83%z^J#sdJXx)*+%s1}Tw2Do)vOyha<1edmiIa<4SRCxEqp_gj5gV_0 zyKvsgU7?b7zSXG`3rQ_rjO5Cpi!4QRkZ4GEY3lhE1QE@TMUqEAU-b!vgRoeQ81GmU zNs7x9-(R~ev@AqaWbK0UAe&z{pw;aO%^hU4Zg<~Dr`oHS+phexaC1uKVJhJ^fq*&j zS{S#NQ-jQ55w60sbRBprhcpbS=pF{L-snkqP*&*oWKj3A^{6%v5zy?^$e1+iMOlyh z#b4v;=X+j&w_YTK;nTz-tPj%21s81pr*Aup+$~2?b?|adF;k-|jyx zI!(8`%HqGTgAvX_253rB2dZ3UgU7(|$fPyk?qxNtEs{`2tneofkzWU)>i5aiT{9?w z#w*K)I}F#pc3-jDfUQH5pD@cIk@=~NMr0Cgx3~W9uYdjPh}XVI^Jgzl1ah#Mf&d)l zV{yD?a`Kml%)W>q11tGTIo2o~4bjS6ray~?h0h6=dh~)}- z{jEBcA_mkGYl?Z+B^R?ZQ?_bL456sEC?(Lc8@3&+_C=`jz^{b;(SQZE%2C5~2hVMu zF^0M;GR7#?4-s~d0}ya10@+4~Tq}z9n>mGrj=t-M?B=yN`n>cTBcnHJ<`UgBx&Yjw zKc%6|oteN6z_BGoG9xK7ArEkiMSU!}Sr!&ypXGEPyK=swz>(1+A(1u?aQ*t+^Cqv<4R#%k;E14M+!apOIi8RZ z&H++DWZ8hZI(uNyW3|6sm4Qw;x8JBh72U(O1)DGUHyYQ^D1xI;-I&q!m5vIb07p*9Mk?2p2uw*+7%7r zfA;tCVQoXVc7m9I33>htw$Aj)Gh$pMf#1m4MuEnOz}hftlPx!&y04${;&J-X$Q={= zLTY2TKYwKC$ucA!fAjZ(%VwH!^I^HH*YHy^y7$~yRql!7m3REhnUowOb2 z;5OfIrCI#iW9@+E$!+)BT75^~ShSCnt zvT2K|=Pz=8IujKQ=PJV zpxu+o^CgVC{qQnO7c;^OktXb2uaN-UwHc7OgPaNQ6q>H;3p=9hd&#%3AJA)HrL87s zSeigbgV-o>m*`4C2ye5{RCVl>;8Nq((7ZsvQP!4@5k|TuSrj24RtMa&u%UZ2P>Y+yEuPt) z=C!yr^r6{@TY4+l@Bnld1iXJeJ(VcpavmUMiwE50Qb!2ANv{l5a3_I7mj(zD+NOwM)A@WPu(X z3R&|!hmgoSs2Rd1Yf>EOakPE_hi{e@KuDkCtj006R@d0E{Nc($eNgK$Iurp`rs=jS z_?0aS;n;c>we0rrnORcatuN95*x+w8a7dXye&HT$Hb$IH{R^t{*7_#A$D(HM=1>8pJ>?#RWqV=wK6 zByd_RJz>3ayiD$)6?sa;Y}Ix!0AaiqJPyq&fdWq09G+%dYJ8(H%`@v`x_pf9dRK#S z#1j5knBXe$SoIOe7&?e046km@%8zI_D$Vm1{D=El- zHJGy-xdK(&`%4cXfg1E^RY~hT8Y;cHI{?{%;+BcV<6b&Sbz6NLVC>6Led^^AB}F6V zx3gF$F*2dlhfO#B)6thZ7S3vdH}NR<$>*ClPK?rqwR>fg5N)1jRNx+HY(0cZKqAr> zkuWL*&v={q_pg~wF7X=?4t2VS(%rIE`fQyYiq;I*d zyQXd{us%E7b)C_N80!JHIrm4kbE=^F%0T~y`?rC6W^Wud0FhA0!GnHnv8TdcOZlj* zQDQ<5TP|Z?`@24dK1y2;A@x@|>4`t0@5&)N(TkTSzz=xF@hdLKm4d8dW*)&)SG_WC z>O8HH8vhonD&SBgKea1R0p|#m)9Qf{!X|@igPR{3=h&2*0W~-q7E#64;>JVtP* zYZh{_s+donOb4BK@-!CuAr^iAqyVA68hz4-Up zt~x8C@LpPQiaOBujEJ5DPg!OZ_|iOSHofPiEVxxu;5^UD!*v5WtxG>FTbjhT8s>eg z0RfQKb;BaAMx^=iu8W)(^Bn04|3)I?V&*t=?!|h5~G2==EYk@W# zr`s}iZ71Y;f9np$>301sAPKDC36K_)+ zVDOm};&lA^-|FhP+4BG3R~)p`%CNnY(+Z!M5GdnVIn&tu(qF|cEdT)4)R5qIb2Xad zr_$`Hgh8Wzr`Ee@=ZsC09C7d z?diBwij`9#V;|4DVvK^z4VXplUzL^(!06Ud%;41nWS8CI>-ck~hbs0gsw%4oUf4($ zKCFy07jZC>{o-L~Y3rwj$Q5T&zSzm`vF83bK=uxcqA{1<)n?T|tGp_~_q#@7P3wiP z5`1<@fgc3~05WKoLbx>$3>-c`k2(~i5-28re8ltf#xOXa>%1+pwBdM3{CnS+N^6;L zN0HSWw?>sDGO({*hJbq_hvTT>t39dJd)rIJ&n~#U5i3FsyG}n=UN`m-e7noMi{w^5T<;bKLZ&F9f6 z6}6eJh6c*8km{(Cqw9df(FXJggYYF{<|Erj#Yn!rEY<>&J1c%DiJmSkr`uYzMF$s`?y3y^~mJix@wNN#Xd2XXWeJ=i0RYE zv|zHVK1to_cI;)A-;#-Baw)yyxNx~5u~ELX7n~0FF2=M&2Wl|?lPo) zR6FDW;jj3^-Z@2^(-ErJ)0T9B)4RHkq0EB*$vTUke-DK&x#}m`zgO0aAc9>lSnsHU zqbhDRt3o)k+dFYpNa8axL-3IPnxTHlI^Ha+(RW^dS7oMk0OL2ymyb@j7T?joT+T15 z!x8z|eswQXD&Yv)g+FP^r^ZtL&pstuTYOEBXh8}1L6wQf9_Jo`QTU!Mi<-AnBsU4j zM^aCDB6>z7UD|Vq{8v_jmVcdr&lhH5aoi1La33K};DP2nQ+MD62-Xa>X&~li5dhG{ zvYJTy)Ors}@zuEWeXy=$vk6Uli$A$Ghpoe-QUz?}Xf#<;kkq}aR#{z2Q2q{bseDy( zb=6P!ibxD*?%v-MY*(~Z3IM(hyzkQJev=VUg7Rt6R<#WXe4TC_@_f3@bfbcxC-I${ zk{L1wxO?eK4z^T;k;!94jsgy)Z0Z-!DAZQK6mr_bw&a)^H99&DnJ>{Cm2ptS)dQJ& zHr9`x-z*Fl&69^~h!sKX4*{tmy^F1RPNgsyx{u(JUi*=V(qKtPV@&#ox zt`ng6V&V*MSk!-L*7Hx3vT?e;l$U!)x~I9vl~Ykgvryl*N_3O2YmdZD zpzTt6t@;ehH}&_A-`Kv4VbTn5rt^z6D-WMUZFs#_L&{&u;W&f49=o2 z!Od0Ly~Fh|;aSd) zg%YpPQ@5#W1ST#ztAnyYvKWeE$i+pZ*002uJ=9^RGP_1Ett3lZt79mu5aXy0HqEGR z*-@(MShHntwFsC>O_ZHU&^~VUM81t^UKxAyN|9`q$$$tLFZmvSOv_SctM$WwaM_C- zBsJ)~Lp~6PSdEQ+3qnhIZ1va(U!0iF9zo0s%ys_-Q*xE|y7TSuFp_qHz`^i){EAVu ztH{DTNlDd$0PkXBGw04R3Fj0URgyg|c4|#L5Rs4+?9bVRf8*m1^6v$wQ9Y08W7-oZ z1xlkJcT$n*$Nkiyqo7o>)J}}3`(MthCDNvopa*++0TlVy5}ZXJ{`%_f?_R4O!8fL-I48gZr7y`f_^@mz2WlH8j{{xsY0UiJV literal 0 HcmV?d00001 diff --git a/test/words_repository_test.dart b/test/words_repository_test.dart index fa68dc0..e9a3fc0 100644 --- a/test/words_repository_test.dart +++ b/test/words_repository_test.dart @@ -63,25 +63,30 @@ void main() { expect(repo.getPreviousWord(0).sessionNumber, equals(999)); }); - test('History entries can be added, retrieved, and cleared', () async { + test('History entries can be added, updated with AI interpretation, and cleared', () async { await repo.clearHistory(); expect(repo.history.isEmpty, isTrue); final entry = HistoryEntry( - id: '1', + id: 'hist_1', timestamp: DateTime(2026, 8, 31, 12, 0), word: 'огонь', sessionNumber: 777, partOfSpeech: 'существительное', symbolicMeaning: 'Очищающее пламя трансформации', readingType: 'Жребий момента', + question: 'В чем суть текущего вызова?', ); await repo.addHistoryEntry(entry); expect(repo.history.length, equals(1)); expect(repo.history.first.word, equals('огонь')); + expect(repo.history.first.question, equals('В чем суть текущего вызова?')); expect(repo.history.first.formattedSessionNumber, equals('777')); - expect(repo.history.first.formattedDate, contains('31.08.2026 12:00:00')); + + // Update with AI interpretation + await repo.updateHistoryEntry('hist_1', aiInterpretation: 'Огонь призывает отпустить старые страхи'); + expect(repo.history.first.aiInterpretation, equals('Огонь призывает отпустить старые страхи')); await repo.clearHistory(); expect(repo.history.isEmpty, isTrue);