diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d06999db34c..a92044f15b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -88,7 +88,9 @@ jobs: fi - name: Test with pytest skipping openai tests if: matrix.python-version != '3.10' && matrix.os == 'ubuntu-latest' + # Remove the line below once https://github.com/docker/docker-py/issues/3256 is merged run: | + pip install "requests<2.32.0" pytest test --ignore=test/agentchat/contrib --skip-openai --durations=10 --durations-min=1.0 - name: Test with pytest skipping openai and docker tests if: matrix.python-version != '3.10' && matrix.os != 'ubuntu-latest' diff --git a/.github/workflows/contrib-openai.yml b/.github/workflows/contrib-openai.yml index 1bf71115d6b..b1b3e35e478 100644 --- a/.github/workflows/contrib-openai.yml +++ b/.github/workflows/contrib-openai.yml @@ -74,7 +74,43 @@ jobs: with: file: ./coverage.xml flags: unittests - + AgentEvalTest: + strategy: + matrix: + os: [ubuntu-latest] + python-version: ["3.10"] + runs-on: ${{ matrix.os }} + environment: openai1 + steps: + # checkout to pr branch + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install packages and dependencies + run: | + docker --version + python -m pip install --upgrade pip wheel + pip install -e . + python -c "import autogen" + pip install pytest-cov>=5 pytest-asyncio + - name: Coverage + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }} + AZURE_OPENAI_API_BASE: ${{ secrets.AZURE_OPENAI_API_BASE }} + OAI_CONFIG_LIST: ${{ secrets.OAI_CONFIG_LIST }} + run: | + pytest test/agentchat/contrib/agent_eval/test_agent_eval.py + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + flags: unittests CompressionTest: strategy: matrix: diff --git a/.github/workflows/contrib-tests.yml b/.github/workflows/contrib-tests.yml index f8dd1d46186..38fab877402 100644 --- a/.github/workflows/contrib-tests.yml +++ b/.github/workflows/contrib-tests.yml @@ -107,7 +107,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y tesseract-ocr poppler-utils - pip install unstructured[all-docs]==0.13.0 + pip install --no-cache-dir unstructured[all-docs]==0.13.0 - name: Install packages and dependencies for RetrieveChat run: | pip install -e .[retrievechat] @@ -125,6 +125,35 @@ jobs: file: ./coverage.xml flags: unittests + AgentEvalTest: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + python-version: ["3.10"] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install packages and dependencies for all tests + run: | + python -m pip install --upgrade pip wheel + pip install pytest-cov>=5 + - name: Install packages and dependencies for AgentEval + run: | + pip install -e . + - name: Coverage + run: | + pytest test/agentchat/contrib/agent_eval/ --skip-openai + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + flags: unittests + CompressionTest: runs-on: ${{ matrix.os }} strategy: diff --git a/README.md b/README.md index fabbff99b63..e78d4b91aad 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ [![Discord](https://img.shields.io/discord/1153072414184452236?logo=discord&style=flat)](https://aka.ms/autogen-dc) [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40pyautogen)](https://twitter.com/pyautogen) +[![NuGet version](https://badge.fury.io/nu/AutoGen.Core.svg)](https://badge.fury.io/nu/AutoGen.Core) # AutoGen [📚 Cite paper](#related-papers). @@ -14,13 +15,19 @@

--> +:fire: May 13, 2024: [The Economist](https://www.economist.com/science-and-technology/2024/05/13/todays-ai-models-are-impressive-teams-of-them-will-be-formidable) published an article about multi-agent systems (MAS) following a January 2024 interview with [Chi Wang](https://github.com/sonichi). + +:fire: May 11, 2024: [AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation](https://openreview.net/pdf?id=uAjxFFing2) received the best paper award in [ICLR 2024 LLM Agents Workshop](https://llmagents.github.io/). + +:fire: Apr 26, 2024: [AutoGen.NET](https://microsoft.github.io/autogen-for-net/) is available for .NET developers! + :fire: Apr 17, 2024: Andrew Ng cited AutoGen in [The Batch newsletter](https://www.deeplearning.ai/the-batch/issue-245/) and [What's next for AI agentic workflows](https://youtu.be/sal78ACtGTc?si=JduUzN_1kDnMq0vF) at Sequoia Capital's AI Ascent (Mar 26). :fire: Mar 3, 2024: What's new in AutoGen? 📰[Blog](https://microsoft.github.io/autogen/blog/2024/03/03/AutoGen-Update); 📺[Youtube](https://www.youtube.com/watch?v=j_mtwQiaLGU). :fire: Mar 1, 2024: the first AutoGen multi-agent experiment on the challenging [GAIA](https://huggingface.co/spaces/gaia-benchmark/leaderboard) benchmark achieved the No. 1 accuracy in all the three levels. -:tada: Jan 30, 2024: AutoGen is highlighted by Peter Lee in Microsoft Research Forum [Keynote](https://t.co/nUBSjPDjqD). + :tada: Dec 31, 2023: [AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation Framework](https://arxiv.org/abs/2308.08155) is selected by [TheSequence: My Five Favorite AI Papers of 2023](https://thesequence.substack.com/p/my-five-favorite-ai-papers-of-2023). @@ -28,13 +35,13 @@ -:tada: Nov 8, 2023: AutoGen is selected into [Open100: Top 100 Open Source achievements](https://www.benchcouncil.org/evaluation/opencs/annual.html) 35 days after spinoff. +:tada: Nov 8, 2023: AutoGen is selected into [Open100: Top 100 Open Source achievements](https://www.benchcouncil.org/evaluation/opencs/annual.html) 35 days after spinoff from [FLAML](https://github.com/microsoft/FLAML). -:tada: Nov 6, 2023: AutoGen is mentioned by Satya Nadella in a [fireside chat](https://youtu.be/0pLBvgYtv6U). + -:tada: Nov 1, 2023: AutoGen is the top trending repo on GitHub in October 2023. + -:tada: Oct 03, 2023: AutoGen spins off from FLAML on GitHub and has a major paper update (first version on Aug 16). + diff --git a/autogen/agentchat/chat.py b/autogen/agentchat/chat.py index b527f8e0bae..dd489c03625 100644 --- a/autogen/agentchat/chat.py +++ b/autogen/agentchat/chat.py @@ -195,7 +195,9 @@ def initiate_chats(chat_queue: List[Dict[str, Any]]) -> List[ChatResult]: r.summary for i, r in enumerate(finished_chats) if i not in finished_chat_indexes_to_exclude_from_carryover ] - __post_carryover_processing(chat_info) + if not chat_info.get("silent", False): + __post_carryover_processing(chat_info) + sender = chat_info["sender"] chat_res = sender.initiate_chat(**chat_info) finished_chats.append(chat_res) @@ -236,7 +238,10 @@ async def _dependent_chat_future( if isinstance(_chat_carryover, str): _chat_carryover = [_chat_carryover] chat_info["carryover"] = _chat_carryover + [finished_chats[pre_id].summary for pre_id in finished_chats] - __post_carryover_processing(chat_info) + + if not chat_info.get("silent", False): + __post_carryover_processing(chat_info) + sender = chat_info["sender"] chat_res_future = asyncio.create_task(sender.a_initiate_chat(**chat_info)) call_back_with_args = partial(_on_chat_future_done, chat_id=chat_id) diff --git a/autogen/agentchat/contrib/agent_eval/README.md b/autogen/agentchat/contrib/agent_eval/README.md new file mode 100644 index 00000000000..6588a1ec611 --- /dev/null +++ b/autogen/agentchat/contrib/agent_eval/README.md @@ -0,0 +1,7 @@ +Agents for running the AgentEval pipeline. + +AgentEval is a process for evaluating a LLM-based system's performance on a given task. + +When given a task to evaluate and a few example runs, the critic and subcritic agents create evaluation criteria for evaluating a system's solution. Once the criteria has been created, the quantifier agent can evaluate subsequent task solutions based on the generated criteria. + +For more information see: [AgentEval Integration Roadmap](https://github.com/microsoft/autogen/issues/2162) diff --git a/autogen/agentchat/contrib/agent_eval/agent_eval.py b/autogen/agentchat/contrib/agent_eval/agent_eval.py new file mode 100644 index 00000000000..b48c65a66d2 --- /dev/null +++ b/autogen/agentchat/contrib/agent_eval/agent_eval.py @@ -0,0 +1,101 @@ +from typing import Dict, List, Literal, Optional, Union + +import autogen +from autogen.agentchat.contrib.agent_eval.criterion import Criterion +from autogen.agentchat.contrib.agent_eval.critic_agent import CriticAgent +from autogen.agentchat.contrib.agent_eval.quantifier_agent import QuantifierAgent +from autogen.agentchat.contrib.agent_eval.subcritic_agent import SubCriticAgent +from autogen.agentchat.contrib.agent_eval.task import Task + + +def generate_criteria( + llm_config: Optional[Union[Dict, Literal[False]]] = None, + task: Task = None, + additional_instructions: str = "", + max_round=2, + use_subcritic: bool = False, +): + """ + Creates a list of criteria for evaluating the utility of a given task. + Args: + llm_config (dict or bool): llm inference configuration. + task (Task): The task to evaluate. + additional_instructions (str): Additional instructions for the criteria agent. + max_round (int): The maximum number of rounds to run the conversation. + use_subcritic (bool): Whether to use the subcritic agent to generate subcriteria. + Returns: + list: A list of Criterion objects for evaluating the utility of the given task. + """ + critic = CriticAgent( + system_message=CriticAgent.DEFAULT_SYSTEM_MESSAGE + "\n" + additional_instructions, + llm_config=llm_config, + ) + + critic_user = autogen.UserProxyAgent( + name="critic_user", + max_consecutive_auto_reply=0, # terminate without auto-reply + human_input_mode="NEVER", + code_execution_config={"use_docker": False}, + ) + + agents = [critic_user, critic] + + if use_subcritic: + subcritic = SubCriticAgent( + llm_config=llm_config, + ) + agents.append(subcritic) + + groupchat = autogen.GroupChat( + agents=agents, messages=[], max_round=max_round, speaker_selection_method="round_robin" + ) + critic_manager = autogen.GroupChatManager(groupchat=groupchat, llm_config=llm_config) + + critic_user.initiate_chat(critic_manager, message=task.get_sys_message()) + criteria = critic_user.last_message() + content = criteria["content"] + # need to strip out any extra code around the returned json + content = content[content.find("[") : content.rfind("]") + 1] + criteria = Criterion.parse_json_str(content) + return criteria + + +def quantify_criteria( + llm_config: Optional[Union[Dict, Literal[False]]] = None, + criteria: List[Criterion] = None, + task: Task = None, + test_case: str = "", + ground_truth: str = "", +): + """ + Quantifies the performance of a system using the provided criteria. + Args: + llm_config (dict or bool): llm inference configuration. + criteria ([Criterion]): A list of criteria for evaluating the utility of a given task. + task (Task): The task to evaluate. + test_case (str): The test case to evaluate. + ground_truth (str): The ground truth for the test case. + Returns: + dict: A dictionary where the keys are the criteria and the values are the assessed performance based on accepted values for each criteria. + """ + quantifier = QuantifierAgent( + llm_config=llm_config, + ) + + quantifier_user = autogen.UserProxyAgent( + name="quantifier_user", + max_consecutive_auto_reply=0, # terminate without auto-reply + human_input_mode="NEVER", + code_execution_config={"use_docker": False}, + ) + + quantifier_user.initiate_chat( # noqa: F841 + quantifier, + message=task.get_sys_message() + + "Evaluation dictionary: " + + Criterion.write_json(criteria) + + "actual test case to evaluate: " + + test_case, + ) + quantified_results = quantifier_user.last_message() + return {"actual_success": ground_truth, "estimated_performance": quantified_results["content"]} diff --git a/autogen/agentchat/contrib/agent_eval/criterion.py b/autogen/agentchat/contrib/agent_eval/criterion.py new file mode 100644 index 00000000000..5efd121ec07 --- /dev/null +++ b/autogen/agentchat/contrib/agent_eval/criterion.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +import json +from typing import List + +import pydantic_core +from pydantic import BaseModel +from pydantic.json import pydantic_encoder + + +class Criterion(BaseModel): + """ + A class that represents a criterion for agent evaluation. + """ + + name: str + description: str + accepted_values: List[str] + sub_criteria: List[Criterion] = list() + + @staticmethod + def parse_json_str(criteria: str): + """ + Create a list of Criterion objects from a json string. + Args: + criteria (str): Json string that represents the criteria + returns: + [Criterion]: A list of Criterion objects that represents the json criteria information. + """ + return [Criterion(**crit) for crit in json.loads(criteria)] + + @staticmethod + def write_json(criteria): + """ + Create a json string from a list of Criterion objects. + Args: + criteria ([Criterion]): A list of Criterion objects. + Returns: + str: A json string that represents the list of Criterion objects. + """ + return json.dumps([crit.model_dump() for crit in criteria], indent=2) diff --git a/autogen/agentchat/contrib/agent_eval/critic_agent.py b/autogen/agentchat/contrib/agent_eval/critic_agent.py new file mode 100644 index 00000000000..2f5e5598ba6 --- /dev/null +++ b/autogen/agentchat/contrib/agent_eval/critic_agent.py @@ -0,0 +1,41 @@ +from typing import Optional + +from autogen.agentchat.conversable_agent import ConversableAgent + + +class CriticAgent(ConversableAgent): + """ + An agent for creating list of criteria for evaluating the utility of a given task. + """ + + DEFAULT_SYSTEM_MESSAGE = """You are a helpful assistant. You suggest criteria for evaluating different tasks. They should be distinguishable, quantifiable and not redundant. + Convert the evaluation criteria into a list where each item is a criteria which consists of the following dictionary as follows + {"name": name of the criterion, "description": criteria description , "accepted_values": possible accepted inputs for this key} + Make sure "accepted_values" include the acceptable inputs for each key that are fine-grained and preferably multi-graded levels and "description" includes the criterion description. + Output just the criteria string you have created, no code. + """ + + DEFAULT_DESCRIPTION = "An AI agent for creating list criteria for evaluating the utility of a given task." + + def __init__( + self, + name="critic", + system_message: Optional[str] = DEFAULT_SYSTEM_MESSAGE, + description: Optional[str] = DEFAULT_DESCRIPTION, + **kwargs, + ): + """ + Args: + name (str): agent name. + system_message (str): system message for the ChatCompletion inference. + Please override this attribute if you want to reprogram the agent. + description (str): The description of the agent. + **kwargs (dict): Please refer to other kwargs in + [ConversableAgent](../../conversable_agent#__init__). + """ + super().__init__( + name=name, + system_message=system_message, + description=description, + **kwargs, + ) diff --git a/autogen/agentchat/contrib/agent_eval/quantifier_agent.py b/autogen/agentchat/contrib/agent_eval/quantifier_agent.py new file mode 100644 index 00000000000..02a8f650fab --- /dev/null +++ b/autogen/agentchat/contrib/agent_eval/quantifier_agent.py @@ -0,0 +1,36 @@ +from typing import Optional + +from autogen.agentchat.conversable_agent import ConversableAgent + + +class QuantifierAgent(ConversableAgent): + """ + An agent for quantifying the performance of a system using the provided criteria. + """ + + DEFAULT_SYSTEM_MESSAGE = """"You are a helpful assistant. You quantify the output of different tasks based on the given criteria. + The criterion is given in a json list format where each element is a distinct criteria. + The each element is a dictionary as follows {"name": name of the criterion, "description": criteria description , "accepted_values": possible accepted inputs for this key} + You are going to quantify each of the crieria for a given task based on the task description. + Return a dictionary where the keys are the criteria and the values are the assessed performance based on accepted values for each criteria. + Return only the dictionary, no code.""" + + DEFAULT_DESCRIPTION = "An AI agent for quantifing the performance of a system using the provided criteria." + + def __init__( + self, + name="quantifier", + system_message: Optional[str] = DEFAULT_SYSTEM_MESSAGE, + description: Optional[str] = DEFAULT_DESCRIPTION, + **kwargs, + ): + """ + Args: + name (str): agent name. + system_message (str): system message for the ChatCompletion inference. + Please override this attribute if you want to reprogram the agent. + description (str): The description of the agent. + **kwargs (dict): Please refer to other kwargs in + [ConversableAgent](../../conversable_agent#__init__). + """ + super().__init__(name=name, system_message=system_message, description=description, **kwargs) diff --git a/autogen/agentchat/contrib/agent_eval/subcritic_agent.py b/autogen/agentchat/contrib/agent_eval/subcritic_agent.py new file mode 100755 index 00000000000..fa994ee7bda --- /dev/null +++ b/autogen/agentchat/contrib/agent_eval/subcritic_agent.py @@ -0,0 +1,42 @@ +from typing import Optional + +from autogen.agentchat.conversable_agent import ConversableAgent + + +class SubCriticAgent(ConversableAgent): + """ + An agent for creating subcriteria from a given list of criteria for evaluating the utility of a given task. + """ + + DEFAULT_SYSTEM_MESSAGE = """You are a helpful assistant to the critic agent. You suggest sub criteria for evaluating different tasks based on the criteria provided by the critic agent (if you feel it is needed). + They should be distinguishable, quantifiable, and related to the overall theme of the critic's provided criteria. + You operate by taking in the description of the criteria. You then create a new key called sub criteria where you provide the sub criteria for the given criteria. + The value of the sub_criteria is a dictionary where the keys are the subcriteria and each value is as follows {"description": sub criteria description , "accepted_values": possible accepted inputs for this key} + Do this for each criteria provided by the critic (removing the criteria's accepted values). "accepted_values" include the acceptable inputs for each key that are fine-grained and preferably multi-graded levels. "description" includes the criterion description. + Once you have created the sub criteria for the given criteria, you return the json (make sure to include the contents of the critic's dictionary in the final dictionary as well). + Make sure to return a valid json and no code""" + + DEFAULT_DESCRIPTION = "An AI agent for creating subcriteria from a given list of criteria." + + def __init__( + self, + name="subcritic", + system_message: Optional[str] = DEFAULT_SYSTEM_MESSAGE, + description: Optional[str] = DEFAULT_DESCRIPTION, + **kwargs, + ): + """ + Args: + name (str): agent name. + system_message (str): system message for the ChatCompletion inference. + Please override this attribute if you want to reprogram the agent. + description (str): The description of the agent. + **kwargs (dict): Please refer to other kwargs in + [ConversableAgent](../../conversable_agent#__init__). + """ + super().__init__( + name=name, + system_message=system_message, + description=description, + **kwargs, + ) diff --git a/autogen/agentchat/contrib/agent_eval/task.py b/autogen/agentchat/contrib/agent_eval/task.py new file mode 100644 index 00000000000..9f96fbf79e2 --- /dev/null +++ b/autogen/agentchat/contrib/agent_eval/task.py @@ -0,0 +1,37 @@ +import json + +from pydantic import BaseModel + + +class Task(BaseModel): + """ + Class representing a task for agent completion, includes example agent execution for criteria generation. + """ + + name: str + description: str + successful_response: str + failed_response: str + + def get_sys_message(self): + return f"""Task: {self.name}. + Task description: {self.description} + Task successful example: {self.successful_response} + Task failed example: {self.failed_response} + """ + + @staticmethod + def parse_json_str(task: str): + """ + Create a Task object from a json object. + Args: + json_data (dict): A dictionary that represents the task. + Returns: + Task: A Task object that represents the json task information. + """ + json_data = json.loads(task) + name = json_data.get("name") + description = json_data.get("description") + successful_response = json_data.get("successful_response") + failed_response = json_data.get("failed_response") + return Task(name, description, successful_response, failed_response) diff --git a/autogen/agentchat/contrib/capabilities/context_handling.py b/autogen/agentchat/contrib/capabilities/context_handling.py index 173811842eb..44b10259f1b 100644 --- a/autogen/agentchat/contrib/capabilities/context_handling.py +++ b/autogen/agentchat/contrib/capabilities/context_handling.py @@ -8,8 +8,8 @@ from autogen import ConversableAgent, token_count_utils warn( - "Context handling with TransformChatHistory is deprecated. " - "Please use TransformMessages from autogen/agentchat/contrib/capabilities/transform_messages.py instead.", + "Context handling with TransformChatHistory is deprecated and will be removed in `0.2.30`. " + "Please use `TransformMessages`, documentation can be found at https://microsoft.github.io/autogen/docs/topics/handling_long_contexts/intro_to_transform_messages", DeprecationWarning, stacklevel=2, ) diff --git a/autogen/agentchat/contrib/capabilities/transforms.py b/autogen/agentchat/contrib/capabilities/transforms.py index 8303843e881..bc56efd74d2 100644 --- a/autogen/agentchat/contrib/capabilities/transforms.py +++ b/autogen/agentchat/contrib/capabilities/transforms.py @@ -8,6 +8,7 @@ from autogen import token_count_utils from autogen.cache import AbstractCache, Cache +from autogen.oai.openai_utils import filter_config from .text_compressors import LLMLingua, TextCompressor @@ -130,6 +131,8 @@ def __init__( max_tokens: Optional[int] = None, min_tokens: Optional[int] = None, model: str = "gpt-3.5-turbo-0613", + filter_dict: Optional[Dict] = None, + exclude_filter: bool = True, ): """ Args: @@ -140,11 +143,17 @@ def __init__( min_tokens (Optional[int]): Minimum number of tokens in messages to apply the transformation. Must be greater than or equal to 0 if not None. model (str): The target OpenAI model for tokenization alignment. + filter_dict (None or dict): A dictionary to filter out messages that you want/don't want to compress. + If None, no filters will be applied. + exclude_filter (bool): If exclude filter is True (the default value), messages that match the filter will be + excluded from token truncation. If False, messages that match the filter will be truncated. """ self._model = model self._max_tokens_per_message = self._validate_max_tokens(max_tokens_per_message) self._max_tokens = self._validate_max_tokens(max_tokens) self._min_tokens = self._validate_min_tokens(min_tokens, max_tokens) + self._filter_dict = filter_dict + self._exclude_filter = exclude_filter def apply_transform(self, messages: List[Dict]) -> List[Dict]: """Applies token truncation to the conversation history. @@ -169,10 +178,15 @@ def apply_transform(self, messages: List[Dict]) -> List[Dict]: for msg in reversed(temp_messages): # Some messages may not have content. - if not isinstance(msg.get("content"), (str, list)): + if not _is_content_right_type(msg.get("content")): processed_messages.insert(0, msg) continue + if not _should_transform_message(msg, self._filter_dict, self._exclude_filter): + processed_messages.insert(0, msg) + processed_messages_tokens += _count_tokens(msg["content"]) + continue + expected_tokens_remained = self._max_tokens - processed_messages_tokens - self._max_tokens_per_message # If adding this message would exceed the token limit, truncate the last message to meet the total token @@ -282,6 +296,8 @@ def __init__( min_tokens: Optional[int] = None, compression_params: Dict = dict(), cache: Optional[AbstractCache] = Cache.disk(), + filter_dict: Optional[Dict] = None, + exclude_filter: bool = True, ): """ Args: @@ -293,6 +309,10 @@ def __init__( dictionary. cache (None or AbstractCache): The cache client to use to store and retrieve previously compressed messages. If None, no caching will be used. + filter_dict (None or dict): A dictionary to filter out messages that you want/don't want to compress. + If None, no filters will be applied. + exclude_filter (bool): If exclude filter is True (the default value), messages that match the filter will be + excluded from compression. If False, messages that match the filter will be compressed. """ if text_compressor is None: @@ -303,6 +323,8 @@ def __init__( self._text_compressor = text_compressor self._min_tokens = min_tokens self._compression_args = compression_params + self._filter_dict = filter_dict + self._exclude_filter = exclude_filter self._cache = cache # Optimizing savings calculations to optimize log generation @@ -334,7 +356,10 @@ def apply_transform(self, messages: List[Dict]) -> List[Dict]: processed_messages = messages.copy() for message in processed_messages: # Some messages may not have content. - if not isinstance(message.get("content"), (str, list)): + if not _is_content_right_type(message.get("content")): + continue + + if not _should_transform_message(message, self._filter_dict, self._exclude_filter): continue if _is_content_text_empty(message["content"]): @@ -397,7 +422,7 @@ def _cache_set( self, content: Union[str, List[Dict]], compressed_content: Union[str, List[Dict]], tokens_saved: int ): if self._cache: - value = (tokens_saved, json.dumps(compressed_content)) + value = (tokens_saved, compressed_content) self._cache.set(self._cache_key(content), value) def _cache_key(self, content: Union[str, List[Dict]]) -> str: @@ -427,6 +452,10 @@ def _count_tokens(content: Union[str, List[Dict[str, Any]]]) -> int: return token_count +def _is_content_right_type(content: Any) -> bool: + return isinstance(content, (str, list)) + + def _is_content_text_empty(content: Union[str, List[Dict[str, Any]]]) -> bool: if isinstance(content, str): return content == "" @@ -434,3 +463,10 @@ def _is_content_text_empty(content: Union[str, List[Dict[str, Any]]]) -> bool: return all(_is_content_text_empty(item.get("text", "")) for item in content) else: return False + + +def _should_transform_message(message: Dict[str, Any], filter_dict: Optional[Dict[str, Any]], exclude: bool) -> bool: + if not filter_dict: + return True + + return len(filter_config([message], filter_dict, exclude)) > 0 diff --git a/autogen/agentchat/contrib/compressible_agent.py b/autogen/agentchat/contrib/compressible_agent.py index 9c4e78af852..cbedb17ceed 100644 --- a/autogen/agentchat/contrib/compressible_agent.py +++ b/autogen/agentchat/contrib/compressible_agent.py @@ -13,8 +13,8 @@ logger = logging.getLogger(__name__) warn( - "Context handling with CompressibleAgent is deprecated. " - "Please use `TransformMessages`, documentation can be found at https://microsoft.github.io/autogen/docs/reference/agentchat/contrib/capabilities/transform_messages", + "Context handling with CompressibleAgent is deprecated and will be removed in `0.2.30`. " + "Please use `TransformMessages`, documentation can be found at https://microsoft.github.io/autogen/docs/topics/handling_long_contexts/intro_to_transform_messages", DeprecationWarning, stacklevel=2, ) diff --git a/autogen/agentchat/contrib/gpt_assistant_agent.py b/autogen/agentchat/contrib/gpt_assistant_agent.py index 0f5de8adcb5..40a28bfbcfa 100644 --- a/autogen/agentchat/contrib/gpt_assistant_agent.py +++ b/autogen/agentchat/contrib/gpt_assistant_agent.py @@ -11,6 +11,7 @@ from autogen.agentchat.agent import Agent from autogen.agentchat.assistant_agent import AssistantAgent, ConversableAgent from autogen.oai.openai_utils import create_gpt_assistant, retrieve_assistants_by_name, update_gpt_assistant +from autogen.runtime_logging import log_new_agent, logging_enabled logger = logging.getLogger(__name__) @@ -65,6 +66,8 @@ def __init__( super().__init__( name=name, system_message=instructions, human_input_mode="NEVER", llm_config=openai_client_cfg, **kwargs ) + if logging_enabled(): + log_new_agent(self, locals()) # GPTAssistantAgent's azure_deployment param may cause NotFoundError (404) in client.beta.assistants.list() # See: https://github.com/microsoft/autogen/pull/1721 @@ -169,10 +172,11 @@ def __init__( # Tools are specified but overwrite_tools is False; do not update the assistant's tools logger.warning("overwrite_tools is False. Using existing tools from assistant API.") + self.update_system_message(self._openai_assistant.instructions) # lazily create threads self._openai_threads = {} self._unread_index = defaultdict(int) - self.register_reply(Agent, GPTAssistantAgent._invoke_assistant, position=2) + self.register_reply([Agent, None], GPTAssistantAgent._invoke_assistant, position=2) def _invoke_assistant( self, diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index bfd38a54d60..c3394a96bb6 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -937,6 +937,7 @@ def my_summary_method( One example key is "summary_prompt", and value is a string of text used to prompt a LLM-based agent (the sender or receiver agent) to reflect on the conversation and extract a summary when summary_method is "reflection_with_llm". The default summary_prompt is DEFAULT_SUMMARY_PROMPT, i.e., "Summarize takeaway from the conversation. Do not add any introductory phrases. If the intended request is NOT properly addressed, please point it out." + Another available key is "summary_role", which is the role of the message sent to the agent in charge of summarizing. Default is "system". message (str, dict or Callable): the initial message to be sent to the recipient. Needs to be provided. Otherwise, input() will be called to get the initial message. - If a string or a dict is provided, it will be used as the initial message. `generate_init_message` is called to generate the initial message for the agent based on this string and the context. If dict, it may contain the following reserved fields (either content or tool_calls need to be provided). @@ -1168,8 +1169,13 @@ def _reflection_with_llm_as_summary(sender, recipient, summary_args): raise ValueError("The summary_prompt must be a string.") msg_list = recipient.chat_messages_for_summary(sender) agent = sender if recipient is None else recipient + role = summary_args.get("summary_role", None) + if role and not isinstance(role, str): + raise ValueError("The summary_role in summary_arg must be a string.") try: - summary = sender._reflection_with_llm(prompt, msg_list, llm_agent=agent, cache=summary_args.get("cache")) + summary = sender._reflection_with_llm( + prompt, msg_list, llm_agent=agent, cache=summary_args.get("cache"), role=role + ) except BadRequestError as e: warnings.warn( f"Cannot extract summary using reflection_with_llm: {e}. Using an empty str as summary.", UserWarning @@ -1178,7 +1184,12 @@ def _reflection_with_llm_as_summary(sender, recipient, summary_args): return summary def _reflection_with_llm( - self, prompt, messages, llm_agent: Optional[Agent] = None, cache: Optional[AbstractCache] = None + self, + prompt, + messages, + llm_agent: Optional[Agent] = None, + cache: Optional[AbstractCache] = None, + role: Union[str, None] = None, ) -> str: """Get a chat summary using reflection with an llm client based on the conversation history. @@ -1187,10 +1198,14 @@ def _reflection_with_llm( messages (list): The messages generated as part of a chat conversation. llm_agent: the agent with an llm client. cache (AbstractCache or None): the cache client to be used for this conversation. + role (str): the role of the message, usually "system" or "user". Default is "system". """ + if not role: + role = "system" + system_msg = [ { - "role": "system", + "role": role, "content": prompt, } ] @@ -2391,6 +2406,8 @@ def register_function(self, function_map: Dict[str, Union[Callable, None]]): self._assert_valid_name(name) if func is None and name not in self._function_map.keys(): warnings.warn(f"The function {name} to remove doesn't exist", name) + if name in self._function_map: + warnings.warn(f"Function '{name}' is being overridden.", UserWarning) self._function_map.update(function_map) self._function_map = {k: v for k, v in self._function_map.items() if v is not None} @@ -2427,6 +2444,9 @@ def update_function_signature(self, func_sig: Union[str, Dict], is_remove: None) self._assert_valid_name(func_sig["name"]) if "functions" in self.llm_config.keys(): + if any(func["name"] == func_sig["name"] for func in self.llm_config["functions"]): + warnings.warn(f"Function '{func_sig['name']}' is being overridden.", UserWarning) + self.llm_config["functions"] = [ func for func in self.llm_config["functions"] if func.get("name") != func_sig["name"] ] + [func_sig] @@ -2466,7 +2486,9 @@ def update_tool_signature(self, tool_sig: Union[str, Dict], is_remove: None): f"The tool signature must be of the type dict. Received tool signature type {type(tool_sig)}" ) self._assert_valid_name(tool_sig["function"]["name"]) - if "tools" in self.llm_config.keys(): + if "tools" in self.llm_config: + if any(tool["function"]["name"] == tool_sig["function"]["name"] for tool in self.llm_config["tools"]): + warnings.warn(f"Function '{tool_sig['function']['name']}' is being overridden.", UserWarning) self.llm_config["tools"] = [ tool for tool in self.llm_config["tools"] diff --git a/autogen/agentchat/groupchat.py b/autogen/agentchat/groupchat.py index 86492455080..83c426272a2 100644 --- a/autogen/agentchat/groupchat.py +++ b/autogen/agentchat/groupchat.py @@ -1,3 +1,5 @@ +import copy +import json import logging import random import re @@ -12,6 +14,7 @@ from ..io.base import IOStream from ..runtime_logging import log_new_agent, logging_enabled from .agent import Agent +from .chat import ChatResult from .conversable_agent import ConversableAgent logger = logging.getLogger(__name__) @@ -36,6 +39,7 @@ class GroupChat: Then select the next role from {agentlist} to play. Only return the role." - select_speaker_prompt_template: customize the select speaker prompt (used in "auto" speaker selection), which appears last in the message context and generally includes the list of agents and guidance for the LLM to select the next agent. If the string contains "{agentlist}" it will be replaced with a comma-separated list of agent names in square brackets. The default value is: "Read the above conversation. Then select the next role from {agentlist} to play. Only return the role." + To ignore this prompt being used, set this to None. If set to None, ensure your instructions for selecting a speaker are in the select_speaker_message_template string. - select_speaker_auto_multiple_template: customize the follow-up prompt used when selecting a speaker fails with a response that contains multiple agent names. This prompt guides the LLM to return just one agent name. Applies only to "auto" speaker selection method. If the string contains "{agentlist}" it will be replaced with a comma-separated list of agent names in square brackets. The default value is: "You provided more than one name in your text, please return just the name of the next speaker. To determine the speaker use these prioritised rules: 1. If the context refers to themselves as a speaker e.g. "As the..." , choose that speaker's name @@ -98,15 +102,15 @@ def custom_speaker_selection_func( agents: List[Agent] messages: List[Dict] - max_round: Optional[int] = 10 - admin_name: Optional[str] = "Admin" - func_call_filter: Optional[bool] = True + max_round: int = 10 + admin_name: str = "Admin" + func_call_filter: bool = True speaker_selection_method: Union[Literal["auto", "manual", "random", "round_robin"], Callable] = "auto" - max_retries_for_selecting_speaker: Optional[int] = 2 + max_retries_for_selecting_speaker: int = 2 allow_repeat_speaker: Optional[Union[bool, List[Agent]]] = None allowed_or_disallowed_speaker_transitions: Optional[Dict] = None speaker_transitions_type: Literal["allowed", "disallowed", None] = None - enable_clear_history: Optional[bool] = False + enable_clear_history: bool = False send_introductions: bool = False select_speaker_message_template: str = """You are in a role play game. The following roles are available: {roles}. @@ -222,8 +226,8 @@ def __post_init__(self): if self.select_speaker_message_template is None or len(self.select_speaker_message_template) == 0: raise ValueError("select_speaker_message_template cannot be empty or None.") - if self.select_speaker_prompt_template is None or len(self.select_speaker_prompt_template) == 0: - raise ValueError("select_speaker_prompt_template cannot be empty or None.") + if self.select_speaker_prompt_template is not None and len(self.select_speaker_prompt_template) == 0: + self.select_speaker_prompt_template = None if self.role_for_select_speaker_messages is None or len(self.role_for_select_speaker_messages) == 0: raise ValueError("role_for_select_speaker_messages cannot be empty or None.") @@ -327,7 +331,13 @@ def select_speaker_msg(self, agents: Optional[List[Agent]] = None) -> str: return return_msg def select_speaker_prompt(self, agents: Optional[List[Agent]] = None) -> str: - """Return the floating system prompt selecting the next speaker. This is always the *last* message in the context.""" + """Return the floating system prompt selecting the next speaker. + This is always the *last* message in the context. + Will return None if the select_speaker_prompt_template is None.""" + + if self.select_speaker_prompt_template is None: + return None + if agents is None: agents = self.agents @@ -621,23 +631,35 @@ def validate_speaker_name(recipient, messages, sender, config) -> Tuple[bool, Un remove_other_reply_funcs=True, ) + # NOTE: Do we have a speaker prompt (select_speaker_prompt_template is not None)? If we don't, we need to feed in the last message to start the nested chat + # Agent for selecting a single agent name from the response speaker_selection_agent = ConversableAgent( "speaker_selection_agent", system_message=self.select_speaker_msg(agents), - chat_messages={checking_agent: messages}, + chat_messages=( + {checking_agent: messages} + if self.select_speaker_prompt_template is not None + else {checking_agent: messages[:-1]} + ), llm_config=selector.llm_config, human_input_mode="NEVER", # Suppresses some extra terminal outputs, outputs will be handled by select_speaker_auto_verbose ) + # Create the starting message + if self.select_speaker_prompt_template is not None: + start_message = { + "content": self.select_speaker_prompt(agents), + "override_role": self.role_for_select_speaker_messages, + } + else: + start_message = messages[-1] + # Run the speaker selection chat result = checking_agent.initiate_chat( speaker_selection_agent, cache=None, # don't use caching for the speaker selection chat - message={ - "content": self.select_speaker_prompt(agents), - "override_role": self.role_for_select_speaker_messages, - }, + message=start_message, max_turns=2 * max(1, max_attempts), # Limiting the chat to the number of attempts, including the initial one clear_history=False, @@ -708,6 +730,8 @@ def validate_speaker_name(recipient, messages, sender, config) -> Tuple[bool, Un remove_other_reply_funcs=True, ) + # NOTE: Do we have a speaker prompt (select_speaker_prompt_template is not None)? If we don't, we need to feed in the last message to start the nested chat + # Agent for selecting a single agent name from the response speaker_selection_agent = ConversableAgent( "speaker_selection_agent", @@ -717,11 +741,20 @@ def validate_speaker_name(recipient, messages, sender, config) -> Tuple[bool, Un human_input_mode="NEVER", # Suppresses some extra terminal outputs, outputs will be handled by select_speaker_auto_verbose ) + # Create the starting message + if self.select_speaker_prompt_template is not None: + start_message = { + "content": self.select_speaker_prompt(agents), + "override_role": self.role_for_select_speaker_messages, + } + else: + start_message = messages[-1] + # Run the speaker selection chat result = await checking_agent.a_initiate_chat( speaker_selection_agent, cache=None, # don't use caching for the speaker selection chat - message=self.select_speaker_prompt(agents), + message=start_message, max_turns=2 * max(1, max_attempts), # Limiting the chat to the number of attempts, including the initial one clear_history=False, @@ -914,6 +947,7 @@ def __init__( max_consecutive_auto_reply: Optional[int] = sys.maxsize, human_input_mode: Optional[str] = "NEVER", system_message: Optional[Union[str, List]] = "Group chat manager.", + silent: bool = False, **kwargs, ): if ( @@ -937,6 +971,8 @@ def __init__( # Store groupchat self._groupchat = groupchat + self._silent = silent + # Order of register_reply is important. # Allow sync chat if initiated using initiate_chat self.register_reply(Agent, GroupChatManager.run_chat, config=groupchat, reset_config=GroupChat.reset) @@ -989,6 +1025,7 @@ def run_chat( speaker = sender groupchat = config send_introductions = getattr(groupchat, "send_introductions", False) + silent = getattr(self, "_silent", False) if send_introductions: # Broadcast the intro @@ -1043,7 +1080,7 @@ def run_chat( reply["content"] = self.clear_agents_history(reply, groupchat) # The speaker sends the message without requesting a reply - speaker.send(reply, self, request_reply=False) + speaker.send(reply, self, request_reply=False, silent=silent) message = self.last_message(speaker) if self.client_cache is not None: for a in groupchat.agents: @@ -1064,6 +1101,7 @@ async def a_run_chat( speaker = sender groupchat = config send_introductions = getattr(groupchat, "send_introductions", False) + silent = getattr(self, "_silent", False) if send_introductions: # Broadcast the intro @@ -1108,7 +1146,7 @@ async def a_run_chat( if reply is None: break # The speaker sends the message without requesting a reply - await speaker.a_send(reply, self, request_reply=False) + await speaker.a_send(reply, self, request_reply=False, silent=silent) message = self.last_message(speaker) if self.client_cache is not None: for a in groupchat.agents: @@ -1116,6 +1154,290 @@ async def a_run_chat( a.previous_cache = None return True, None + def resume( + self, + messages: Union[List[Dict], str], + remove_termination_string: str = None, + silent: Optional[bool] = False, + ) -> Tuple[ConversableAgent, Dict]: + """Resumes a group chat using the previous messages as a starting point. Requires the agents, group chat, and group chat manager to be established + as per the original group chat. + + Args: + - messages Union[List[Dict], str]: The content of the previous chat's messages, either as a Json string or a list of message dictionaries. + - remove_termination_string str: Remove the provided string from the last message to prevent immediate termination + - silent (bool or None): (Experimental) whether to print the messages for this conversation. Default is False. + + Returns: + - Tuple[ConversableAgent, Dict]: A tuple containing the last agent who spoke and their message + """ + + # Convert messages from string to messages list, if needed + if isinstance(messages, str): + messages = self.messages_from_string(messages) + elif isinstance(messages, list) and all(isinstance(item, dict) for item in messages): + messages = copy.deepcopy(messages) + else: + raise Exception("Messages is not of type str or List[Dict]") + + # Clean up the objects, ensuring there are no messages in the agents and group chat + + # Clear agent message history + for agent in self._groupchat.agents: + if isinstance(agent, ConversableAgent): + agent.clear_history() + + # Clear Manager message history + self.clear_history() + + # Clear GroupChat messages + self._groupchat.reset() + + # Validation of message and agents + + try: + self._valid_resume_messages(messages) + except: + raise + + # Load the messages into the group chat + for i, message in enumerate(messages): + + if "name" in message: + message_speaker_agent = self._groupchat.agent_by_name(message["name"]) + else: + # If there's no name, assign the group chat manager (this is an indication the ChatResult messages was used instead of groupchat.messages as state) + message_speaker_agent = self + message["name"] = self.name + + # If it wasn't an agent speaking, it may be the manager + if not message_speaker_agent and message["name"] == self.name: + message_speaker_agent = self + + # Add previous messages to each agent (except their own messages and the last message, as we'll kick off the conversation with it) + if i != len(messages) - 1: + for agent in self._groupchat.agents: + if agent.name != message["name"]: + self.send(message, self._groupchat.agent_by_name(agent.name), request_reply=False, silent=True) + + # Add previous message to the new groupchat, if it's an admin message the name may not match so add the message directly + if message_speaker_agent: + self._groupchat.append(message, message_speaker_agent) + else: + self._groupchat.messages.append(message) + + # Last speaker agent + last_speaker_name = message["name"] + + # Last message to check for termination (we could avoid this by ignoring termination check for resume in the future) + last_message = message + + # Get last speaker as an agent + previous_last_agent = self._groupchat.agent_by_name(name=last_speaker_name) + + # If we didn't match a last speaker agent, we check that it's the group chat's admin name and assign the manager, if so + if not previous_last_agent and ( + last_speaker_name == self._groupchat.admin_name or last_speaker_name == self.name + ): + previous_last_agent = self + + # Termination removal and check + self._process_resume_termination(remove_termination_string, messages) + + if not silent: + iostream = IOStream.get_default() + iostream.print( + f"Prepared group chat with {len(messages)} messages, the last speaker is", + colored(last_speaker_name, "yellow"), + flush=True, + ) + + # Update group chat settings for resuming + self._groupchat.send_introductions = False + + return previous_last_agent, last_message + + async def a_resume( + self, + messages: Union[List[Dict], str], + remove_termination_string: str = None, + silent: Optional[bool] = False, + ) -> Tuple[ConversableAgent, Dict]: + """Resumes a group chat using the previous messages as a starting point, asynchronously. Requires the agents, group chat, and group chat manager to be established + as per the original group chat. + + Args: + - messages Union[List[Dict], str]: The content of the previous chat's messages, either as a Json string or a list of message dictionaries. + - remove_termination_string str: Remove the provided string from the last message to prevent immediate termination + - silent (bool or None): (Experimental) whether to print the messages for this conversation. Default is False. + + Returns: + - Tuple[ConversableAgent, Dict]: A tuple containing the last agent who spoke and their message + """ + + # Convert messages from string to messages list, if needed + if isinstance(messages, str): + messages = self.messages_from_string(messages) + elif isinstance(messages, list) and all(isinstance(item, dict) for item in messages): + messages = copy.deepcopy(messages) + else: + raise Exception("Messages is not of type str or List[Dict]") + + # Clean up the objects, ensuring there are no messages in the agents and group chat + + # Clear agent message history + for agent in self._groupchat.agents: + if isinstance(agent, ConversableAgent): + agent.clear_history() + + # Clear Manager message history + self.clear_history() + + # Clear GroupChat messages + self._groupchat.reset() + + # Validation of message and agents + + try: + self._valid_resume_messages(messages) + except: + raise + + # Load the messages into the group chat + for i, message in enumerate(messages): + + if "name" in message: + message_speaker_agent = self._groupchat.agent_by_name(message["name"]) + else: + # If there's no name, assign the group chat manager (this is an indication the ChatResult messages was used instead of groupchat.messages as state) + message_speaker_agent = self + message["name"] = self.name + + # If it wasn't an agent speaking, it may be the manager + if not message_speaker_agent and message["name"] == self.name: + message_speaker_agent = self + + # Add previous messages to each agent (except their own messages and the last message, as we'll kick off the conversation with it) + if i != len(messages) - 1: + for agent in self._groupchat.agents: + if agent.name != message["name"]: + await self.a_send( + message, self._groupchat.agent_by_name(agent.name), request_reply=False, silent=True + ) + + # Add previous message to the new groupchat, if it's an admin message the name may not match so add the message directly + if message_speaker_agent: + self._groupchat.append(message, message_speaker_agent) + else: + self._groupchat.messages.append(message) + + # Last speaker agent + last_speaker_name = message["name"] + + # Last message to check for termination (we could avoid this by ignoring termination check for resume in the future) + last_message = message + + # Get last speaker as an agent + previous_last_agent = self._groupchat.agent_by_name(name=last_speaker_name) + + # If we didn't match a last speaker agent, we check that it's the group chat's admin name and assign the manager, if so + if not previous_last_agent and ( + last_speaker_name == self._groupchat.admin_name or last_speaker_name == self.name + ): + previous_last_agent = self + + # Termination removal and check + self._process_resume_termination(remove_termination_string, messages) + + if not silent: + iostream = IOStream.get_default() + iostream.print( + f"Prepared group chat with {len(messages)} messages, the last speaker is", + colored(last_speaker_name, "yellow"), + flush=True, + ) + + # Update group chat settings for resuming + self._groupchat.send_introductions = False + + return previous_last_agent, last_message + + def _valid_resume_messages(self, messages: List[Dict]): + """Validates the messages used for resuming + + args: + messages (List[Dict]): list of messages to resume with + + returns: + - bool: Whether they are valid for resuming + """ + # Must have messages to start with, otherwise they should run run_chat + if not messages: + raise Exception( + "Cannot resume group chat as no messages were provided. Use GroupChatManager.run_chat or ConversableAgent.initiate_chat to start a new chat." + ) + + # Check that all agents in the chat messages exist in the group chat + for message in messages: + if message.get("name"): + if ( + not self._groupchat.agent_by_name(message["name"]) + and not message["name"] == self._groupchat.admin_name # ignore group chat's name + and not message["name"] == self.name # ignore group chat manager's name + ): + raise Exception(f"Agent name in message doesn't exist as agent in group chat: {message['name']}") + + def _process_resume_termination(self, remove_termination_string: str, messages: List[Dict]): + """Removes termination string, if required, and checks if termination may occur. + + args: + remove_termination_string (str): termination string to remove from the last message + + returns: + None + """ + + last_message = messages[-1] + + # Replace any given termination string in the last message + if remove_termination_string: + if messages[-1].get("content") and remove_termination_string in messages[-1]["content"]: + messages[-1]["content"] = messages[-1]["content"].replace(remove_termination_string, "") + + # Check if the last message meets termination (if it has one) + if self._is_termination_msg: + if self._is_termination_msg(last_message): + logger.warning("WARNING: Last message meets termination criteria and this may terminate the chat.") + + def messages_from_string(self, message_string: str) -> List[Dict]: + """Reads the saved state of messages in Json format for resume and returns as a messages list + + args: + - message_string: Json string, the saved state + + returns: + - List[Dict]: List of messages + """ + try: + state = json.loads(message_string) + except json.JSONDecodeError: + raise Exception("Messages string is not a valid JSON string") + + return state + + def messages_to_string(self, messages: List[Dict]) -> str: + """Converts the provided messages into a Json string that can be used for resuming the chat. + The state is made up of a list of messages + + args: + - messages (List[Dict]): set of messages to convert to a string + + returns: + - str: Json representation of the messages which can be persisted for resuming later + """ + + return json.dumps(messages) + def _raise_exception_on_async_reply_functions(self) -> None: """Raise an exception if any async reply functions are registered. diff --git a/autogen/code_utils.py b/autogen/code_utils.py index e1bc951f099..98ed6067066 100644 --- a/autogen/code_utils.py +++ b/autogen/code_utils.py @@ -6,8 +6,10 @@ import subprocess import sys import time +import venv from concurrent.futures import ThreadPoolExecutor, TimeoutError from hashlib import md5 +from types import SimpleNamespace from typing import Any, Callable, Dict, List, Optional, Tuple, Union import docker @@ -719,3 +721,19 @@ def implement( # cost += metrics["gen_cost"] # if metrics["succeed_assertions"] or i == len(configs) - 1: # return responses[metrics["index_selected"]], cost, i + + +def create_virtual_env(dir_path: str, **env_args) -> SimpleNamespace: + """Creates a python virtual environment and returns the context. + + Args: + dir_path (str): Directory path where the env will be created. + **env_args: Any extra args to pass to the `EnvBuilder` + + Returns: + SimpleNamespace: the virtual env context object.""" + if not env_args: + env_args = {"with_pip": True} + env_builder = venv.EnvBuilder(**env_args) + env_builder.create(dir_path) + return env_builder.ensure_directories(dir_path) diff --git a/autogen/coding/local_commandline_code_executor.py b/autogen/coding/local_commandline_code_executor.py index ed92cd527be..29172bbe922 100644 --- a/autogen/coding/local_commandline_code_executor.py +++ b/autogen/coding/local_commandline_code_executor.py @@ -1,4 +1,5 @@ import logging +import os import re import subprocess import sys @@ -6,6 +7,7 @@ from hashlib import md5 from pathlib import Path from string import Template +from types import SimpleNamespace from typing import Any, Callable, ClassVar, Dict, List, Optional, Union from typing_extensions import ParamSpec @@ -64,6 +66,7 @@ class LocalCommandLineCodeExecutor(CodeExecutor): def __init__( self, timeout: int = 60, + virtual_env_context: Optional[SimpleNamespace] = None, work_dir: Union[Path, str] = Path("."), functions: List[Union[FunctionWithRequirements[Any, A], Callable[..., Any], FunctionWithRequirementsStr]] = [], functions_module: str = "functions", @@ -82,8 +85,22 @@ def __init__( PowerShell (pwsh, powershell, ps1), HTML, CSS, and JavaScript. Execution policies determine whether each language's code blocks are executed or saved only. + ## Execution with a Python virtual environment + A python virtual env can be used to execute code and install dependencies. This has the added benefit of not polluting the + base environment with unwanted modules. + ```python + from autogen.code_utils import create_virtual_env + from autogen.coding import LocalCommandLineCodeExecutor + + venv_dir = ".venv" + venv_context = create_virtual_env(venv_dir) + + executor = LocalCommandLineCodeExecutor(virtual_env_context=venv_context) + ``` + Args: timeout (int): The timeout for code execution, default is 60 seconds. + virtual_env_context (Optional[SimpleNamespace]): The virtual environment context to use. work_dir (Union[Path, str]): The working directory for code execution, defaults to the current directory. functions (List[Union[FunctionWithRequirements[Any, A], Callable[..., Any], FunctionWithRequirementsStr]]): A list of callable functions available to the executor. functions_module (str): The module name under which functions are accessible. @@ -105,6 +122,7 @@ def __init__( self._timeout = timeout self._work_dir: Path = work_dir + self._virtual_env_context: Optional[SimpleNamespace] = virtual_env_context self._functions = functions # Setup could take some time so we intentionally wait for the first code block to do it. @@ -196,7 +214,11 @@ def _setup_functions(self) -> None: required_packages = list(set(flattened_packages)) if len(required_packages) > 0: logging.info("Ensuring packages are installed in executor.") - cmd = [sys.executable, "-m", "pip", "install"] + required_packages + if self._virtual_env_context: + py_executable = self._virtual_env_context.env_exe + else: + py_executable = sys.executable + cmd = [py_executable, "-m", "pip", "install"] + required_packages try: result = subprocess.run( cmd, cwd=self._work_dir, capture_output=True, text=True, timeout=float(self._timeout) @@ -269,9 +291,18 @@ def _execute_code_dont_check_setup(self, code_blocks: List[CodeBlock]) -> Comman program = _cmd(lang) cmd = [program, str(written_file.absolute())] + env = os.environ.copy() + + if self._virtual_env_context: + path_with_virtualenv = rf"{self._virtual_env_context.bin_path}{os.pathsep}{env['PATH']}" + env["PATH"] = path_with_virtualenv + if WIN32: + activation_script = os.path.join(self._virtual_env_context.bin_path, "activate.bat") + cmd = [activation_script, "&&", *cmd] + try: result = subprocess.run( - cmd, cwd=self._work_dir, capture_output=True, text=True, timeout=float(self._timeout) + cmd, cwd=self._work_dir, capture_output=True, text=True, timeout=float(self._timeout), env=env ) except subprocess.TimeoutExpired: logs_all += "\n" + TIMEOUT_MSG diff --git a/autogen/logger/file_logger.py b/autogen/logger/file_logger.py new file mode 100644 index 00000000000..466ed62c849 --- /dev/null +++ b/autogen/logger/file_logger.py @@ -0,0 +1,214 @@ +from __future__ import annotations + +import json +import logging +import os +import threading +import uuid +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union + +from openai import AzureOpenAI, OpenAI +from openai.types.chat import ChatCompletion + +from autogen.logger.base_logger import BaseLogger +from autogen.logger.logger_utils import get_current_ts, to_dict + +from .base_logger import LLMConfig + +if TYPE_CHECKING: + from autogen import Agent, ConversableAgent, OpenAIWrapper + from autogen.oai.gemini import GeminiClient + +logger = logging.getLogger(__name__) + + +class FileLogger(BaseLogger): + def __init__(self, config: Dict[str, Any]): + self.config = config + self.session_id = str(uuid.uuid4()) + + curr_dir = os.getcwd() + self.log_dir = os.path.join(curr_dir, "autogen_logs") + os.makedirs(self.log_dir, exist_ok=True) + + self.log_file = os.path.join(self.log_dir, self.config.get("filename", "runtime.log")) + try: + with open(self.log_file, "a"): + pass + except Exception as e: + logger.error(f"[file_logger] Failed to create logging file: {e}") + + self.logger = logging.getLogger(__name__) + self.logger.setLevel(logging.INFO) + file_handler = logging.FileHandler(self.log_file) + self.logger.addHandler(file_handler) + + def start(self) -> str: + """Start the logger and return the session_id.""" + try: + self.logger.info(f"Started new session with Session ID: {self.session_id}") + except Exception as e: + logger.error(f"[file_logger] Failed to create logging file: {e}") + finally: + return self.session_id + + def log_chat_completion( + self, + invocation_id: uuid.UUID, + client_id: int, + wrapper_id: int, + request: Dict[str, Union[float, str, List[Dict[str, str]]]], + response: Union[str, ChatCompletion], + is_cached: int, + cost: float, + start_time: str, + ) -> None: + """ + Log a chat completion. + """ + thread_id = threading.get_ident() + try: + log_data = json.dumps( + { + "invocation_id": str(invocation_id), + "client_id": client_id, + "wrapper_id": wrapper_id, + "request": to_dict(request), + "response": str(response), + "is_cached": is_cached, + "cost": cost, + "start_time": start_time, + "end_time": get_current_ts(), + "thread_id": thread_id, + } + ) + + self.logger.info(log_data) + except Exception as e: + self.logger.error(f"[file_logger] Failed to log chat completion: {e}") + + def log_new_agent(self, agent: ConversableAgent, init_args: Dict[str, Any] = {}) -> None: + """ + Log a new agent instance. + """ + thread_id = threading.get_ident() + + try: + log_data = json.dumps( + { + "id": id(agent), + "agent_name": agent.name if hasattr(agent, "name") and agent.name is not None else "", + "wrapper_id": to_dict( + agent.client.wrapper_id if hasattr(agent, "client") and agent.client is not None else "" + ), + "session_id": self.session_id, + "current_time": get_current_ts(), + "agent_type": type(agent).__name__, + "args": to_dict(init_args), + "thread_id": thread_id, + } + ) + self.logger.info(log_data) + except Exception as e: + self.logger.error(f"[file_logger] Failed to log new agent: {e}") + + def log_event(self, source: Union[str, Agent], name: str, **kwargs: Dict[str, Any]) -> None: + """ + Log an event from an agent or a string source. + """ + from autogen import Agent + + # This takes an object o as input and returns a string. If the object o cannot be serialized, instead of raising an error, + # it returns a string indicating that the object is non-serializable, along with its type's qualified name obtained using __qualname__. + json_args = json.dumps(kwargs, default=lambda o: f"<>") + thread_id = threading.get_ident() + + if isinstance(source, Agent): + try: + log_data = json.dumps( + { + "source_id": id(source), + "source_name": str(source.name) if hasattr(source, "name") else source, + "event_name": name, + "agent_module": source.__module__, + "agent_class": source.__class__.__name__, + "json_state": json_args, + "timestamp": get_current_ts(), + "thread_id": thread_id, + } + ) + self.logger.info(log_data) + except Exception as e: + self.logger.error(f"[file_logger] Failed to log event {e}") + else: + try: + log_data = json.dumps( + { + "source_id": id(source), + "source_name": str(source.name) if hasattr(source, "name") else source, + "event_name": name, + "json_state": json_args, + "timestamp": get_current_ts(), + "thread_id": thread_id, + } + ) + self.logger.info(log_data) + except Exception as e: + self.logger.error(f"[file_logger] Failed to log event {e}") + + def log_new_wrapper( + self, wrapper: OpenAIWrapper, init_args: Dict[str, Union[LLMConfig, List[LLMConfig]]] = {} + ) -> None: + """ + Log a new wrapper instance. + """ + thread_id = threading.get_ident() + + try: + log_data = json.dumps( + { + "wrapper_id": id(wrapper), + "session_id": self.session_id, + "json_state": json.dumps(init_args), + "timestamp": get_current_ts(), + "thread_id": thread_id, + } + ) + self.logger.info(log_data) + except Exception as e: + self.logger.error(f"[file_logger] Failed to log event {e}") + + def log_new_client( + self, client: AzureOpenAI | OpenAI | GeminiClient, wrapper: OpenAIWrapper, init_args: Dict[str, Any] + ) -> None: + """ + Log a new client instance. + """ + thread_id = threading.get_ident() + + try: + log_data = json.dumps( + { + "client_id": id(client), + "wrapper_id": id(wrapper), + "session_id": self.session_id, + "class": type(client).__name__, + "json_state": json.dumps(init_args), + "timestamp": get_current_ts(), + "thread_id": thread_id, + } + ) + self.logger.info(log_data) + except Exception as e: + self.logger.error(f"[file_logger] Failed to log event {e}") + + def get_connection(self) -> None: + """Method is intentionally left blank because there is no specific connection needed for the FileLogger.""" + pass + + def stop(self) -> None: + """Close the file handler and remove it from the logger.""" + for handler in self.logger.handlers: + if isinstance(handler, logging.FileHandler): + handler.close() + self.logger.removeHandler(handler) diff --git a/autogen/logger/logger_factory.py b/autogen/logger/logger_factory.py index 8073c0c07d3..ed9567977bb 100644 --- a/autogen/logger/logger_factory.py +++ b/autogen/logger/logger_factory.py @@ -1,6 +1,7 @@ -from typing import Any, Dict, Optional +from typing import Any, Dict, Literal, Optional from autogen.logger.base_logger import BaseLogger +from autogen.logger.file_logger import FileLogger from autogen.logger.sqlite_logger import SqliteLogger __all__ = ("LoggerFactory",) @@ -8,11 +9,15 @@ class LoggerFactory: @staticmethod - def get_logger(logger_type: str = "sqlite", config: Optional[Dict[str, Any]] = None) -> BaseLogger: + def get_logger( + logger_type: Literal["sqlite", "file"] = "sqlite", config: Optional[Dict[str, Any]] = None + ) -> BaseLogger: if config is None: config = {} if logger_type == "sqlite": return SqliteLogger(config) + elif logger_type == "file": + return FileLogger(config) else: raise ValueError(f"[logger_factory] Unknown logger type: {logger_type}") diff --git a/autogen/logger/sqlite_logger.py b/autogen/logger/sqlite_logger.py index 6e95a571cd0..42db83d849d 100644 --- a/autogen/logger/sqlite_logger.py +++ b/autogen/logger/sqlite_logger.py @@ -18,6 +18,7 @@ if TYPE_CHECKING: from autogen import Agent, ConversableAgent, OpenAIWrapper + from autogen.oai.gemini import GeminiClient logger = logging.getLogger(__name__) lock = threading.Lock() @@ -316,7 +317,7 @@ def log_new_wrapper(self, wrapper: OpenAIWrapper, init_args: Dict[str, Union[LLM self._run_query(query=query, args=args) def log_new_client( - self, client: Union[AzureOpenAI, OpenAI], wrapper: OpenAIWrapper, init_args: Dict[str, Any] + self, client: Union[AzureOpenAI, OpenAI, GeminiClient], wrapper: OpenAIWrapper, init_args: Dict[str, Any] ) -> None: if self.con is None: return diff --git a/autogen/oai/client.py b/autogen/oai/client.py index 3edfa40d4ec..982d1c0d57f 100644 --- a/autogen/oai/client.py +++ b/autogen/oai/client.py @@ -435,7 +435,8 @@ def _register_default_client(self, config: Dict[str, Any], openai_config: Dict[s elif api_type is not None and api_type.startswith("google"): if gemini_import_exception: raise ImportError("Please install `google-generativeai` to use Google OpenAI API.") - self._clients.append(GeminiClient(**openai_config)) + client = GeminiClient(**openai_config) + self._clients.append(client) else: client = OpenAI(**openai_config) self._clients.append(OpenAIClient(client)) diff --git a/autogen/oai/gemini.py b/autogen/oai/gemini.py index fcf7e09c025..5c06a4def0c 100644 --- a/autogen/oai/gemini.py +++ b/autogen/oai/gemini.py @@ -5,8 +5,18 @@ llm_config={ "config_list": [{ "api_type": "google", - "model": "models/gemini-pro", - "api_key": os.environ.get("GOOGLE_API_KEY") + "model": "gemini-pro", + "api_key": os.environ.get("GOOGLE_API_KEY"), + "safety_settings": [ + {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_ONLY_HIGH"}, + {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_ONLY_HIGH"}, + {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_ONLY_HIGH"}, + {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_ONLY_HIGH"} + ], + "top_p":0.5, + "max_tokens": 2048, + "temperature": 1.0, + "top_k": 5 } ]} @@ -47,6 +57,17 @@ class GeminiClient: of AutoGen. """ + # Mapping, where Key is a term used by Autogen, and Value is a term used by Gemini + PARAMS_MAPPING = { + "max_tokens": "max_output_tokens", + # "n": "candidate_count", # Gemini supports only `n=1` + "stop_sequences": "stop_sequences", + "temperature": "temperature", + "top_p": "top_p", + "top_k": "top_k", + "max_output_tokens": "max_output_tokens", + } + def __init__(self, **kwargs): self.api_key = kwargs.get("api_key", None) if not self.api_key: @@ -93,12 +114,15 @@ def create(self, params: Dict) -> ChatCompletion: messages = params.get("messages", []) stream = params.get("stream", False) n_response = params.get("n", 1) - params.get("temperature", 0.5) - params.get("top_p", 1.0) - params.get("max_tokens", 4096) + + generation_config = { + gemini_term: params[autogen_term] + for autogen_term, gemini_term in self.PARAMS_MAPPING.items() + if autogen_term in params + } + safety_settings = params.get("safety_settings", {}) if stream: - # warn user that streaming is not supported warnings.warn( "Streaming is not supported for Gemini yet, and it will have no effect. Please set stream=False.", UserWarning, @@ -112,7 +136,9 @@ def create(self, params: Dict) -> ChatCompletion: gemini_messages = oai_messages_to_gemini_messages(messages) # we use chat model by default - model = genai.GenerativeModel(model_name) + model = genai.GenerativeModel( + model_name, generation_config=generation_config, safety_settings=safety_settings + ) genai.configure(api_key=self.api_key) chat = model.start_chat(history=gemini_messages[:-1]) max_retries = 5 @@ -142,7 +168,9 @@ def create(self, params: Dict) -> ChatCompletion: elif model_name == "gemini-pro-vision": # B. handle the vision model # Gemini's vision model does not support chat history yet - model = genai.GenerativeModel(model_name) + model = genai.GenerativeModel( + model_name, generation_config=generation_config, safety_settings=safety_settings + ) genai.configure(api_key=self.api_key) # chat = model.start_chat(history=gemini_messages[:-1]) # response = chat.send_message(gemini_messages[-1]) diff --git a/autogen/oai/openai_utils.py b/autogen/oai/openai_utils.py index 7e738b7bd61..1ed347f6271 100644 --- a/autogen/oai/openai_utils.py +++ b/autogen/oai/openai_utils.py @@ -16,7 +16,10 @@ NON_CACHE_KEY = ["api_key", "base_url", "api_type", "api_version"] DEFAULT_AZURE_API_VERSION = "2024-02-15-preview" OAI_PRICE1K = { - # https://openai.com/pricing + # https://openai.com/api/pricing/ + # gpt-4o + "gpt-4o": (0.005, 0.015), + "gpt-4o-2024-05-13": (0.005, 0.015), # gpt-4-turbo "gpt-4-turbo-2024-04-09": (0.01, 0.03), # gpt-4 @@ -376,11 +379,10 @@ def config_list_gpt4_gpt35( def filter_config( config_list: List[Dict[str, Any]], filter_dict: Optional[Dict[str, Union[List[Union[str, None]], Set[Union[str, None]]]]], + exclude: bool = False, ) -> List[Dict[str, Any]]: - """ - This function filters `config_list` by checking each configuration dictionary against the - criteria specified in `filter_dict`. A configuration dictionary is retained if for every - key in `filter_dict`, see example below. + """This function filters `config_list` by checking each configuration dictionary against the criteria specified in + `filter_dict`. A configuration dictionary is retained if for every key in `filter_dict`, see example below. Args: config_list (list of dict): A list of configuration dictionaries to be filtered. @@ -391,71 +393,68 @@ def filter_config( when it is found in the list of acceptable values. If the configuration's field's value is a list, then a match occurs if there is a non-empty intersection with the acceptable values. - - + exclude (bool): If False (the default value), configs that match the filter will be included in the returned + list. If True, configs that match the filter will be excluded in the returned list. Returns: list of dict: A list of configuration dictionaries that meet all the criteria specified in `filter_dict`. Example: - ```python - # Example configuration list with various models and API types - configs = [ - {'model': 'gpt-3.5-turbo'}, - {'model': 'gpt-4'}, - {'model': 'gpt-3.5-turbo', 'api_type': 'azure'}, - {'model': 'gpt-3.5-turbo', 'tags': ['gpt35_turbo', 'gpt-35-turbo']}, - ] - - # Define filter criteria to select configurations for the 'gpt-3.5-turbo' model - # that are also using the 'azure' API type - filter_criteria = { - 'model': ['gpt-3.5-turbo'], # Only accept configurations for 'gpt-3.5-turbo' - 'api_type': ['azure'] # Only accept configurations for 'azure' API type - } - - # Apply the filter to the configuration list - filtered_configs = filter_config(configs, filter_criteria) - - # The resulting `filtered_configs` will be: - # [{'model': 'gpt-3.5-turbo', 'api_type': 'azure', ...}] - - - # Define a filter to select a given tag - filter_criteria = { - 'tags': ['gpt35_turbo'], - } - - # Apply the filter to the configuration list - filtered_configs = filter_config(configs, filter_criteria) - - # The resulting `filtered_configs` will be: - # [{'model': 'gpt-3.5-turbo', 'tags': ['gpt35_turbo', 'gpt-35-turbo']}] - ``` - + ```python + # Example configuration list with various models and API types + configs = [ + {'model': 'gpt-3.5-turbo'}, + {'model': 'gpt-4'}, + {'model': 'gpt-3.5-turbo', 'api_type': 'azure'}, + {'model': 'gpt-3.5-turbo', 'tags': ['gpt35_turbo', 'gpt-35-turbo']}, + ] + # Define filter criteria to select configurations for the 'gpt-3.5-turbo' model + # that are also using the 'azure' API type + filter_criteria = { + 'model': ['gpt-3.5-turbo'], # Only accept configurations for 'gpt-3.5-turbo' + 'api_type': ['azure'] # Only accept configurations for 'azure' API type + } + # Apply the filter to the configuration list + filtered_configs = filter_config(configs, filter_criteria) + # The resulting `filtered_configs` will be: + # [{'model': 'gpt-3.5-turbo', 'api_type': 'azure', ...}] + # Define a filter to select a given tag + filter_criteria = { + 'tags': ['gpt35_turbo'], + } + # Apply the filter to the configuration list + filtered_configs = filter_config(configs, filter_criteria) + # The resulting `filtered_configs` will be: + # [{'model': 'gpt-3.5-turbo', 'tags': ['gpt35_turbo', 'gpt-35-turbo']}] + ``` Note: - If `filter_dict` is empty or None, no filtering is applied and `config_list` is returned as is. - If a configuration dictionary in `config_list` does not contain a key specified in `filter_dict`, it is considered a non-match and is excluded from the result. - If the list of acceptable values for a key in `filter_dict` includes None, then configuration dictionaries that do not have that key will also be considered a match. - """ - def _satisfies(config_value: Any, acceptable_values: Any) -> bool: - if isinstance(config_value, list): - return bool(set(config_value) & set(acceptable_values)) # Non-empty intersection - else: - return config_value in acceptable_values + """ if filter_dict: - config_list = [ - config - for config in config_list - if all(_satisfies(config.get(key), value) for key, value in filter_dict.items()) + return [ + item + for item in config_list + if all(_satisfies_criteria(item.get(key), values) != exclude for key, values in filter_dict.items()) ] return config_list +def _satisfies_criteria(value: Any, criteria_values: Any) -> bool: + if value is None: + return False + + if isinstance(value, list): + return bool(set(value) & set(criteria_values)) # Non-empty intersection + else: + return value in criteria_values + + def config_list_from_json( env_or_file: str, file_location: Optional[str] = "", @@ -782,3 +781,10 @@ def update_gpt_assistant(client: OpenAI, assistant_id: str, assistant_config: Di assistant_update_kwargs["file_ids"] = assistant_config["file_ids"] return client.beta.assistants.update(assistant_id=assistant_id, **assistant_update_kwargs) + + +def _satisfies(config_value: Any, acceptable_values: Any) -> bool: + if isinstance(config_value, list): + return bool(set(config_value) & set(acceptable_values)) # Non-empty intersection + else: + return config_value in acceptable_values diff --git a/autogen/runtime_logging.py b/autogen/runtime_logging.py index 1b9835eaa4b..ffc741482e6 100644 --- a/autogen/runtime_logging.py +++ b/autogen/runtime_logging.py @@ -3,16 +3,17 @@ import logging import sqlite3 import uuid -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union from openai import AzureOpenAI, OpenAI from openai.types.chat import ChatCompletion -from autogen.logger.base_logger import LLMConfig +from autogen.logger.base_logger import BaseLogger, LLMConfig from autogen.logger.logger_factory import LoggerFactory if TYPE_CHECKING: from autogen import Agent, ConversableAgent, OpenAIWrapper + from autogen.oai.gemini import GeminiClient logger = logging.getLogger(__name__) @@ -20,11 +21,27 @@ is_logging = False -def start(logger_type: str = "sqlite", config: Optional[Dict[str, Any]] = None) -> str: +def start( + logger: Optional[BaseLogger] = None, + logger_type: Literal["sqlite", "file"] = "sqlite", + config: Optional[Dict[str, Any]] = None, +) -> str: + """ + Start logging for the runtime. + Args: + logger (BaseLogger): A logger instance + logger_type (str): The type of logger to use (default: sqlite) + config (dict): Configuration for the logger + Returns: + session_id (str(uuid.uuid4)): a unique id for the logging session + """ global autogen_logger global is_logging - autogen_logger = LoggerFactory.get_logger(logger_type=logger_type, config=config) + if logger: + autogen_logger = logger + else: + autogen_logger = LoggerFactory.get_logger(logger_type=logger_type, config=config) try: session_id = autogen_logger.start() @@ -78,7 +95,9 @@ def log_new_wrapper(wrapper: OpenAIWrapper, init_args: Dict[str, Union[LLMConfig autogen_logger.log_new_wrapper(wrapper, init_args) -def log_new_client(client: Union[AzureOpenAI, OpenAI], wrapper: OpenAIWrapper, init_args: Dict[str, Any]) -> None: +def log_new_client( + client: Union[AzureOpenAI, OpenAI, GeminiClient], wrapper: OpenAIWrapper, init_args: Dict[str, Any] +) -> None: if autogen_logger is None: logger.error("[runtime logging] log_new_client: autogen logger is None") return diff --git a/autogen/token_count_utils.py b/autogen/token_count_utils.py index 589d7b404a7..b71dbc428a1 100644 --- a/autogen/token_count_utils.py +++ b/autogen/token_count_utils.py @@ -34,6 +34,8 @@ def get_max_token_limit(model: str = "gpt-3.5-turbo-0613") -> int: "gpt-4-0125-preview": 128000, "gpt-4-turbo-preview": 128000, "gpt-4-vision-preview": 128000, + "gpt-4o": 128000, + "gpt-4o-2024-05-13": 128000, } return max_token_limit[model] diff --git a/dotnet/.editorconfig b/dotnet/.editorconfig index 4da1adc5de6..5a604ce0096 100644 --- a/dotnet/.editorconfig +++ b/dotnet/.editorconfig @@ -141,7 +141,7 @@ csharp_preserve_single_line_statements = true csharp_preserve_single_line_blocks = true # Code block -csharp_prefer_braces = false:none +csharp_prefer_braces = true:warning # Using statements csharp_using_directive_placement = outside_namespace:error @@ -173,6 +173,11 @@ dotnet_diagnostic.CS1573.severity = none # disable CS1570: XML comment has badly formed XML dotnet_diagnostic.CS1570.severity = none +dotnet_diagnostic.IDE0035.severity = warning # Remove unreachable code +dotnet_diagnostic.IDE0161.severity = warning # Use file-scoped namespace + +csharp_style_var_elsewhere = true:suggestion # Prefer 'var' everywhere + # disable check for generated code [*.generated.cs] generated_code = true \ No newline at end of file diff --git a/dotnet/AutoGen.sln b/dotnet/AutoGen.sln index b46b8091cf5..be40e7b61b6 100644 --- a/dotnet/AutoGen.sln +++ b/dotnet/AutoGen.sln @@ -33,7 +33,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.Mistral", "src\Auto EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.Mistral.Tests", "test\AutoGen.Mistral.Tests\AutoGen.Mistral.Tests.csproj", "{15441693-3659-4868-B6C1-B106F52FF3BA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoGen.SemanticKernel.Tests", "test\AutoGen.SemanticKernel.Tests\AutoGen.SemanticKernel.Tests.csproj", "{1DFABC4A-8458-4875-8DCB-59F3802DAC65}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.SemanticKernel.Tests", "test\AutoGen.SemanticKernel.Tests\AutoGen.SemanticKernel.Tests.csproj", "{1DFABC4A-8458-4875-8DCB-59F3802DAC65}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.OpenAI.Tests", "test\AutoGen.OpenAI.Tests\AutoGen.OpenAI.Tests.csproj", "{D36A85F9-C172-487D-8192-6BFE5D05B4A7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.DotnetInteractive.Tests", "test\AutoGen.DotnetInteractive.Tests\AutoGen.DotnetInteractive.Tests.csproj", "{B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.Ollama", "src\AutoGen.Ollama\AutoGen.Ollama.csproj", "{9F9E6DED-3D92-4970-909A-70FC11F1A665}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.Ollama.Tests", "test\AutoGen.Ollama.Tests\AutoGen.Ollama.Tests.csproj", "{03E31CAA-3728-48D3-B936-9F11CF6C18FE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoGen.Ollama.Sample", "sample\AutoGen.Ollama.Sample\AutoGen.Ollama.Sample.csproj", "{93AA4D0D-6EE4-44D5-AD77-7F73A3934544}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoGen.SemanticKernel.Sample", "sample\AutoGen.SemanticKernel.Sample\AutoGen.SemanticKernel.Sample.csproj", "{52958A60-3FF7-4243-9058-34A6E4F55C31}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -93,6 +104,30 @@ Global {1DFABC4A-8458-4875-8DCB-59F3802DAC65}.Debug|Any CPU.Build.0 = Debug|Any CPU {1DFABC4A-8458-4875-8DCB-59F3802DAC65}.Release|Any CPU.ActiveCfg = Release|Any CPU {1DFABC4A-8458-4875-8DCB-59F3802DAC65}.Release|Any CPU.Build.0 = Release|Any CPU + {D36A85F9-C172-487D-8192-6BFE5D05B4A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D36A85F9-C172-487D-8192-6BFE5D05B4A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D36A85F9-C172-487D-8192-6BFE5D05B4A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D36A85F9-C172-487D-8192-6BFE5D05B4A7}.Release|Any CPU.Build.0 = Release|Any CPU + {B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Release|Any CPU.Build.0 = Release|Any CPU + {9F9E6DED-3D92-4970-909A-70FC11F1A665}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F9E6DED-3D92-4970-909A-70FC11F1A665}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F9E6DED-3D92-4970-909A-70FC11F1A665}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F9E6DED-3D92-4970-909A-70FC11F1A665}.Release|Any CPU.Build.0 = Release|Any CPU + {03E31CAA-3728-48D3-B936-9F11CF6C18FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03E31CAA-3728-48D3-B936-9F11CF6C18FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03E31CAA-3728-48D3-B936-9F11CF6C18FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03E31CAA-3728-48D3-B936-9F11CF6C18FE}.Release|Any CPU.Build.0 = Release|Any CPU + {93AA4D0D-6EE4-44D5-AD77-7F73A3934544}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93AA4D0D-6EE4-44D5-AD77-7F73A3934544}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93AA4D0D-6EE4-44D5-AD77-7F73A3934544}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93AA4D0D-6EE4-44D5-AD77-7F73A3934544}.Release|Any CPU.Build.0 = Release|Any CPU + {52958A60-3FF7-4243-9058-34A6E4F55C31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52958A60-3FF7-4243-9058-34A6E4F55C31}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52958A60-3FF7-4243-9058-34A6E4F55C31}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52958A60-3FF7-4243-9058-34A6E4F55C31}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -111,6 +146,12 @@ Global {6585D1A4-3D97-4D76-A688-1933B61AEB19} = {18BF8DD7-0585-48BF-8F97-AD333080CE06} {15441693-3659-4868-B6C1-B106F52FF3BA} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64} {1DFABC4A-8458-4875-8DCB-59F3802DAC65} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64} + {D36A85F9-C172-487D-8192-6BFE5D05B4A7} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64} + {B61388CA-DC73-4B7F-A7B2-7B9A86C9229E} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64} + {9F9E6DED-3D92-4970-909A-70FC11F1A665} = {18BF8DD7-0585-48BF-8F97-AD333080CE06} + {03E31CAA-3728-48D3-B936-9F11CF6C18FE} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64} + {93AA4D0D-6EE4-44D5-AD77-7F73A3934544} = {FBFEAD1F-29EB-4D99-A672-0CD8473E10B9} + {52958A60-3FF7-4243-9058-34A6E4F55C31} = {FBFEAD1F-29EB-4D99-A672-0CD8473E10B9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {93384647-528D-46C8-922C-8DB36A382F0B} diff --git a/dotnet/NuGet.config b/dotnet/NuGet.config index 2eb25136c6a..1d0cf4c2bc7 100644 --- a/dotnet/NuGet.config +++ b/dotnet/NuGet.config @@ -2,8 +2,6 @@ - - diff --git a/dotnet/eng/MetaInfo.props b/dotnet/eng/MetaInfo.props index 8aff3c60226..0444dadfd5e 100644 --- a/dotnet/eng/MetaInfo.props +++ b/dotnet/eng/MetaInfo.props @@ -1,7 +1,7 @@ - 0.0.13 + 0.0.14 AutoGen https://microsoft.github.io/autogen-for-net/ https://github.com/microsoft/autogen diff --git a/dotnet/eng/Version.props b/dotnet/eng/Version.props index b9fc4367194..ae213015471 100644 --- a/dotnet/eng/Version.props +++ b/dotnet/eng/Version.props @@ -10,7 +10,7 @@ 6.8.0 2.4.2 17.7.0 - 1.0.0-beta.23523.2 + 1.0.0-beta.24229.4 8.0.0 4.0.0 diff --git a/dotnet/sample/AutoGen.BasicSamples/AutoGen.BasicSample.csproj b/dotnet/sample/AutoGen.BasicSamples/AutoGen.BasicSample.csproj index 0cafff3c0d0..afc76164906 100644 --- a/dotnet/sample/AutoGen.BasicSamples/AutoGen.BasicSample.csproj +++ b/dotnet/sample/AutoGen.BasicSamples/AutoGen.BasicSample.csproj @@ -4,7 +4,6 @@ Exe $(TestTargetFramework) enable - enable True $(NoWarn);CS8981;CS8600;CS8602;CS8604;CS8618;CS0219;SKEXP0054;SKEXP0050;SKEXP0110 diff --git a/dotnet/sample/AutoGen.BasicSamples/CodeSnippet/OpenAICodeSnippet.cs b/dotnet/sample/AutoGen.BasicSamples/CodeSnippet/OpenAICodeSnippet.cs index 022f7e9f984..cf045221223 100644 --- a/dotnet/sample/AutoGen.BasicSamples/CodeSnippet/OpenAICodeSnippet.cs +++ b/dotnet/sample/AutoGen.BasicSamples/CodeSnippet/OpenAICodeSnippet.cs @@ -84,7 +84,7 @@ public async Task CreateOpenAIChatAgentAsync() new TextMessage(Role.Assistant, "Hello", from: "user"), ], from: "user"), - new Message(Role.Assistant, "Hello", from: "user"), // Message type is going to be deprecated, please use TextMessage instead + new TextMessage(Role.Assistant, "Hello", from: "user"), // Message type is going to be deprecated, please use TextMessage instead }; foreach (var message in messages) diff --git a/dotnet/sample/AutoGen.BasicSamples/Example03_Agent_FunctionCall.cs b/dotnet/sample/AutoGen.BasicSamples/Example03_Agent_FunctionCall.cs index 57b9ea76dcb..0ef8eaa48ae 100644 --- a/dotnet/sample/AutoGen.BasicSamples/Example03_Agent_FunctionCall.cs +++ b/dotnet/sample/AutoGen.BasicSamples/Example03_Agent_FunctionCall.cs @@ -77,20 +77,30 @@ public static async Task RunAsync() // talk to the assistant agent var upperCase = await agent.SendAsync("convert to upper case: hello world"); upperCase.GetContent()?.Should().Be("HELLO WORLD"); - upperCase.Should().BeOfType>(); + upperCase.Should().BeOfType(); upperCase.GetToolCalls().Should().HaveCount(1); upperCase.GetToolCalls().First().FunctionName.Should().Be(nameof(UpperCase)); var concatString = await agent.SendAsync("concatenate strings: a, b, c, d, e"); concatString.GetContent()?.Should().Be("a b c d e"); - concatString.Should().BeOfType>(); + concatString.Should().BeOfType(); concatString.GetToolCalls().Should().HaveCount(1); concatString.GetToolCalls().First().FunctionName.Should().Be(nameof(ConcatString)); var calculateTax = await agent.SendAsync("calculate tax: 100, 0.1"); calculateTax.GetContent().Should().Be("tax is 10"); - calculateTax.Should().BeOfType>(); + calculateTax.Should().BeOfType(); calculateTax.GetToolCalls().Should().HaveCount(1); calculateTax.GetToolCalls().First().FunctionName.Should().Be(nameof(CalculateTax)); + + // parallel function calls + var calculateTaxes = await agent.SendAsync("calculate tax: 100, 0.1; calculate tax: 200, 0.2"); + calculateTaxes.GetContent().Should().Be("tax is 10\ntax is 40"); // "tax is 10\n tax is 40 + calculateTaxes.Should().BeOfType(); + calculateTaxes.GetToolCalls().Should().HaveCount(2); + calculateTaxes.GetToolCalls().First().FunctionName.Should().Be(nameof(CalculateTax)); + + // send aggregate message back to llm to get the final result + var finalResult = await agent.SendAsync(calculateTaxes); } } diff --git a/dotnet/sample/AutoGen.BasicSamples/Example04_Dynamic_GroupChat_Coding_Task.cs b/dotnet/sample/AutoGen.BasicSamples/Example04_Dynamic_GroupChat_Coding_Task.cs index c5d9a01f971..47dd8ce66c9 100644 --- a/dotnet/sample/AutoGen.BasicSamples/Example04_Dynamic_GroupChat_Coding_Task.cs +++ b/dotnet/sample/AutoGen.BasicSamples/Example04_Dynamic_GroupChat_Coding_Task.cs @@ -40,7 +40,8 @@ public static async Task RunAsync() name: "groupAdmin", systemMessage: "You are the admin of the group chat", temperature: 0f, - config: gptConfig); + config: gptConfig) + .RegisterPrintMessage(); var userProxy = new UserProxyAgent(name: "user", defaultReply: GroupChatExtension.TERMINATE, humanInputMode: HumanInputMode.NEVER) .RegisterPrintMessage(); diff --git a/dotnet/sample/AutoGen.BasicSamples/Example05_Dalle_And_GPT4V.cs b/dotnet/sample/AutoGen.BasicSamples/Example05_Dalle_And_GPT4V.cs index 9fccd7ab385..2d21615ef71 100644 --- a/dotnet/sample/AutoGen.BasicSamples/Example05_Dalle_And_GPT4V.cs +++ b/dotnet/sample/AutoGen.BasicSamples/Example05_Dalle_And_GPT4V.cs @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Example05_Dalle_And_GPT4V.cs -using AutoGen; using AutoGen.Core; +using AutoGen.OpenAI; +using AutoGen.OpenAI.Extension; using Azure.AI.OpenAI; using FluentAssertions; using autogen = AutoGen.LLMConfigAPI; @@ -66,50 +67,39 @@ public static async Task RunAsync() File.Delete(imagePath); } - var dalleAgent = new AssistantAgent( - name: "dalle", - systemMessage: "You are a DALL-E agent that generate image from prompt, when conversation is terminated, return the most recent image url", - llmConfig: new ConversableAgentConfig - { - Temperature = 0, - ConfigList = gpt35Config, - FunctionContracts = new[] - { - instance.GenerateImageFunctionContract, - }, - }, + var generateImageFunctionMiddleware = new FunctionCallMiddleware( + functions: [instance.GenerateImageFunctionContract], functionMap: new Dictionary>> { { nameof(GenerateImage), instance.GenerateImageWrapper }, - }) + }); + var dalleAgent = new OpenAIChatAgent( + openAIClient: openAIClient, + modelName: "gpt-3.5-turbo", + name: "dalle", + systemMessage: "You are a DALL-E agent that generate image from prompt, when conversation is terminated, return the most recent image url") + .RegisterMessageConnector() + .RegisterStreamingMiddleware(generateImageFunctionMiddleware) .RegisterMiddleware(async (msgs, option, agent, ct) => { - // if last message contains [TERMINATE], then find the last image url and terminate the conversation - if (msgs.Last().GetContent()?.Contains("TERMINATE") is true) + if (msgs.Any(msg => msg.GetContent()?.ToLower().Contains("approve") is true)) { - var lastMessageWithImage = msgs.Last(msg => msg is ImageMessage) as ImageMessage; - var lastImageUrl = lastMessageWithImage.Url; - Console.WriteLine($"download image from {lastImageUrl} to {imagePath}"); - var httpClient = new HttpClient(); - var imageBytes = await httpClient.GetByteArrayAsync(lastImageUrl); - File.WriteAllBytes(imagePath, imageBytes); - - var messageContent = $@"{GroupChatExtension.TERMINATE} - -{lastImageUrl}"; - return new TextMessage(Role.Assistant, messageContent) - { - From = "dalle", - }; + return new TextMessage(Role.Assistant, $"The image satisfies the condition, conversation is terminated. {GroupChatExtension.TERMINATE}"); } - var reply = await agent.GenerateReplyAsync(msgs, option, ct); + var msgsWithoutImage = msgs.Where(msg => msg is not ImageMessage).ToList(); + var reply = await agent.GenerateReplyAsync(msgsWithoutImage, option, ct); if (reply.GetContent() is string content && content.Contains("IMAGE_GENERATION")) { var imageUrl = content.Split("\n").Last(); var imageMessage = new ImageMessage(Role.Assistant, imageUrl, from: reply.From); + Console.WriteLine($"download image from {imageUrl} to {imagePath}"); + var httpClient = new HttpClient(); + var imageBytes = await httpClient.GetByteArrayAsync(imageUrl, ct); + File.WriteAllBytes(imagePath, imageBytes); + return imageMessage; } else @@ -119,33 +109,25 @@ public static async Task RunAsync() }) .RegisterPrintMessage(); - var gpt4VAgent = new AssistantAgent( + var gpt4VAgent = new OpenAIChatAgent( + openAIClient: openAIClient, name: "gpt4v", + modelName: "gpt-4-vision-preview", systemMessage: @"You are a critism that provide feedback to DALL-E agent. Carefully check the image generated by DALL-E agent and provide feedback. -If the image satisfies the condition, then terminate the conversation by saying [TERMINATE]. +If the image satisfies the condition, then say [APPROVE]. Otherwise, provide detailed feedback to DALL-E agent so it can generate better image. The image should satisfy the following conditions: - There should be a cat and a mouse in the image -- The cat should be chasing after the mouse -", - llmConfig: new ConversableAgentConfig - { - Temperature = 0, - ConfigList = gpt4vConfig, - }) +- The cat should be chasing after the mouse") + .RegisterMessageConnector() .RegisterPrintMessage(); - IEnumerable conversation = new List() - { - new TextMessage(Role.User, "Hey dalle, please generate image from prompt: English short hair blue cat chase after a mouse") - }; - var maxRound = 20; await gpt4VAgent.InitiateChatAsync( receiver: dalleAgent, message: "Hey dalle, please generate image from prompt: English short hair blue cat chase after a mouse", - maxRound: maxRound); + maxRound: 10); File.Exists(imagePath).Should().BeTrue(); } diff --git a/dotnet/sample/AutoGen.Ollama.Sample/AutoGen.Ollama.Sample.csproj b/dotnet/sample/AutoGen.Ollama.Sample/AutoGen.Ollama.Sample.csproj new file mode 100644 index 00000000000..1dc94400869 --- /dev/null +++ b/dotnet/sample/AutoGen.Ollama.Sample/AutoGen.Ollama.Sample.csproj @@ -0,0 +1,24 @@ + + + Exe + $(TestTargetFramework) + enable + True + $(NoWarn);CS8981;CS8600;CS8602;CS8604;CS8618;CS0219;SKEXP0054;SKEXP0050;SKEXP0110 + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/dotnet/sample/AutoGen.Ollama.Sample/Chat_With_LLaMA.cs b/dotnet/sample/AutoGen.Ollama.Sample/Chat_With_LLaMA.cs new file mode 100644 index 00000000000..e1af08c574c --- /dev/null +++ b/dotnet/sample/AutoGen.Ollama.Sample/Chat_With_LLaMA.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Chat_With_LLaMA.cs + +using AutoGen.Core; +using AutoGen.Ollama.Extension; + +namespace AutoGen.Ollama.Sample; + +public class Chat_With_LLaMA +{ + public static async Task RunAsync() + { + using var httpClient = new HttpClient() + { + BaseAddress = new Uri("https://2xbvtxd1-11434.usw2.devtunnels.ms") + }; + + var ollamaAgent = new OllamaAgent( + httpClient: httpClient, + name: "ollama", + modelName: "llama3:latest", + systemMessage: "You are a helpful AI assistant") + .RegisterMessageConnector() + .RegisterPrintMessage(); + + var reply = await ollamaAgent.SendAsync("Can you write a piece of C# code to calculate 100th of fibonacci?"); + } +} diff --git a/dotnet/sample/AutoGen.Ollama.Sample/Chat_With_LLaVA.cs b/dotnet/sample/AutoGen.Ollama.Sample/Chat_With_LLaVA.cs new file mode 100644 index 00000000000..b1b310e3956 --- /dev/null +++ b/dotnet/sample/AutoGen.Ollama.Sample/Chat_With_LLaVA.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Chat_With_LLaVA.cs + +using AutoGen.Core; +using AutoGen.Ollama.Extension; + +namespace AutoGen.Ollama.Sample; + +public class Chat_With_LLaVA +{ + public static async Task RunAsync() + { + using var httpClient = new HttpClient() + { + BaseAddress = new Uri("https://2xbvtxd1-11434.usw2.devtunnels.ms") + }; + + var ollamaAgent = new OllamaAgent( + httpClient: httpClient, + name: "ollama", + modelName: "llava:latest", + systemMessage: "You are a helpful AI assistant") + .RegisterMessageConnector() + .RegisterPrintMessage(); + + var image = Path.Combine("images", "background.png"); + var binaryData = BinaryData.FromBytes(File.ReadAllBytes(image), "image/png"); + var imageMessage = new ImageMessage(Role.User, binaryData); + var textMessage = new TextMessage(Role.User, "what's in this image?"); + var reply = await ollamaAgent.SendAsync(chatHistory: [textMessage, imageMessage]); + + // You can also use MultiModalMessage to put text and image together in one message + // In this case, all the messages in the multi-modal message will be put into single piece of message + // where the text is the concatenation of all the text messages seperated by \n + // and the images are all the images in the multi-modal message + var multiModalMessage = new MultiModalMessage(Role.User, [textMessage, imageMessage]); + + reply = await ollamaAgent.SendAsync(chatHistory: [multiModalMessage]); + } +} diff --git a/dotnet/sample/AutoGen.Ollama.Sample/Program.cs b/dotnet/sample/AutoGen.Ollama.Sample/Program.cs new file mode 100644 index 00000000000..62c92eebe7e --- /dev/null +++ b/dotnet/sample/AutoGen.Ollama.Sample/Program.cs @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Program.cs + +using AutoGen.Ollama.Sample; + +await Chat_With_LLaVA.RunAsync(); diff --git a/dotnet/sample/AutoGen.Ollama.Sample/images/background.png b/dotnet/sample/AutoGen.Ollama.Sample/images/background.png new file mode 100644 index 00000000000..ca276f81f5b --- /dev/null +++ b/dotnet/sample/AutoGen.Ollama.Sample/images/background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:300b7c9d6ba0c23a3e52fbd2e268141ddcca0434a9fb9dcf7e58e7e903d36dcf +size 2126185 diff --git a/dotnet/sample/AutoGen.SemanticKernel.Sample/AutoGen.SemanticKernel.Sample.csproj b/dotnet/sample/AutoGen.SemanticKernel.Sample/AutoGen.SemanticKernel.Sample.csproj new file mode 100644 index 00000000000..6c226651292 --- /dev/null +++ b/dotnet/sample/AutoGen.SemanticKernel.Sample/AutoGen.SemanticKernel.Sample.csproj @@ -0,0 +1,17 @@ + + + + Exe + $(TestTargetFramework) + True + $(NoWarn);CS8981;CS8600;CS8602;CS8604;CS8618;CS0219;SKEXP0054;SKEXP0050;SKEXP0110 + enable + + + + + + + + + diff --git a/dotnet/sample/AutoGen.SemanticKernel.Sample/Create_Semantic_Kernel_Agent.cs b/dotnet/sample/AutoGen.SemanticKernel.Sample/Create_Semantic_Kernel_Agent.cs new file mode 100644 index 00000000000..3333cdd9ad9 --- /dev/null +++ b/dotnet/sample/AutoGen.SemanticKernel.Sample/Create_Semantic_Kernel_Agent.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Create_Semantic_Kernel_Agent.cs + +using AutoGen.Core; +using AutoGen.SemanticKernel.Extension; +using Microsoft.SemanticKernel; + +namespace AutoGen.SemanticKernel.Sample; + +public class Create_Semantic_Kernel_Agent +{ + public static async Task RunAsync() + { + var openAIKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new Exception("Please set OPENAI_API_KEY environment variable."); + var modelId = "gpt-3.5-turbo"; + var kernel = Kernel.CreateBuilder() + .AddOpenAIChatCompletion(modelId: modelId, apiKey: openAIKey) + .Build(); + + var skAgent = new SemanticKernelAgent( + kernel: kernel, + name: "assistant", + systemMessage: "You are a helpful AI assistant") + .RegisterMessageConnector() // register message connector so it support AutoGen built-in message types like TextMessage. + .RegisterPrintMessage(); // pretty print the message to the console + + await skAgent.SendAsync("Hey tell me a long tedious joke"); + } +} diff --git a/dotnet/sample/AutoGen.SemanticKernel.Sample/Create_Semantic_Kernel_Chat_Agent.cs b/dotnet/sample/AutoGen.SemanticKernel.Sample/Create_Semantic_Kernel_Chat_Agent.cs new file mode 100644 index 00000000000..0caea6f031f --- /dev/null +++ b/dotnet/sample/AutoGen.SemanticKernel.Sample/Create_Semantic_Kernel_Chat_Agent.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Create_Semantic_Kernel_Chat_Agent.cs + +using AutoGen.Core; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Agents; + +namespace AutoGen.SemanticKernel.Sample; + +public class Create_Semantic_Kernel_Chat_Agent +{ + public static async Task RunAsync() + { + var openAIKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new Exception("Please set OPENAI_API_KEY environment variable."); + var modelId = "gpt-3.5-turbo"; + var kernel = Kernel.CreateBuilder() + .AddOpenAIChatCompletion(modelId: modelId, apiKey: openAIKey) + .Build(); + + // The built-in ChatCompletionAgent from semantic kernel. + var chatAgent = new ChatCompletionAgent() + { + Kernel = kernel, + Name = "assistant", + Description = "You are a helpful AI assistant", + }; + + var messageConnector = new SemanticKernelChatMessageContentConnector(); + var skAgent = new SemanticKernelChatCompletionAgent(chatAgent) + .RegisterMiddleware(messageConnector) // register message connector so it support AutoGen built-in message types like TextMessage. + .RegisterPrintMessage(); // pretty print the message to the console + + await skAgent.SendAsync("Hey tell me a long tedious joke"); + } +} diff --git a/dotnet/sample/AutoGen.SemanticKernel.Sample/Program.cs b/dotnet/sample/AutoGen.SemanticKernel.Sample/Program.cs new file mode 100644 index 00000000000..5032f2d4330 --- /dev/null +++ b/dotnet/sample/AutoGen.SemanticKernel.Sample/Program.cs @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Program.cs + +using AutoGen.SemanticKernel.Sample; + +await Use_Kernel_Functions_With_Other_Agent.RunAsync(); diff --git a/dotnet/sample/AutoGen.SemanticKernel.Sample/Use_Bing_Search_With_Semantic_Kernel_Agent.cs b/dotnet/sample/AutoGen.SemanticKernel.Sample/Use_Bing_Search_With_Semantic_Kernel_Agent.cs new file mode 100644 index 00000000000..4cebc88291f --- /dev/null +++ b/dotnet/sample/AutoGen.SemanticKernel.Sample/Use_Bing_Search_With_Semantic_Kernel_Agent.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Use_Bing_Search_With_Semantic_Kernel_Agent.cs + +using AutoGen.Core; +using AutoGen.SemanticKernel.Extension; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Plugins.Web; +using Microsoft.SemanticKernel.Plugins.Web.Bing; + +namespace AutoGen.SemanticKernel.Sample; + +public class Use_Bing_Search_With_Semantic_Kernel_Agent +{ + public static async Task RunAsync() + { + var bingApiKey = Environment.GetEnvironmentVariable("BING_API_KEY") ?? throw new Exception("BING_API_KEY environment variable is not set"); + var bingSearch = new BingConnector(bingApiKey); + var webSearchPlugin = new WebSearchEnginePlugin(bingSearch); + + var openAIKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new Exception("Please set OPENAI_API_KEY environment variable."); + var modelId = "gpt-3.5-turbo"; + var kernelBuilder = Kernel.CreateBuilder() + .AddOpenAIChatCompletion(modelId: modelId, apiKey: openAIKey); + kernelBuilder.Plugins.AddFromObject(webSearchPlugin); + + var kernel = kernelBuilder.Build(); + + var skAgent = new SemanticKernelAgent( + kernel: kernel, + name: "assistant", + systemMessage: "You are a helpful AI assistant") + .RegisterMessageConnector() // register message connector so it support AutoGen built-in message types like TextMessage. + .RegisterPrintMessage(); // pretty print the message to the console + + await skAgent.SendAsync("Tell me more about gpt-4-o"); + } +} diff --git a/dotnet/sample/AutoGen.SemanticKernel.Sample/Use_Kernel_Functions_With_Other_Agent.cs b/dotnet/sample/AutoGen.SemanticKernel.Sample/Use_Kernel_Functions_With_Other_Agent.cs new file mode 100644 index 00000000000..d91d727668a --- /dev/null +++ b/dotnet/sample/AutoGen.SemanticKernel.Sample/Use_Kernel_Functions_With_Other_Agent.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Use_Kernel_Functions_With_Other_Agent.cs + +using AutoGen.Core; +using AutoGen.OpenAI; +using AutoGen.OpenAI.Extension; +using Azure.AI.OpenAI; +using Microsoft.SemanticKernel; + +namespace AutoGen.SemanticKernel.Sample; + +public class Use_Kernel_Functions_With_Other_Agent +{ + public static async Task RunAsync() + { + var openAIKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new Exception("Please set OPENAI_API_KEY environment variable."); + var modelId = "gpt-3.5-turbo"; + var kernelBuilder = Kernel.CreateBuilder(); + var kernel = kernelBuilder.Build(); + var getWeatherFunction = KernelFunctionFactory.CreateFromMethod( + method: (string location) => $"The weather in {location} is 75 degrees Fahrenheit.", + functionName: "GetWeather", + description: "Get the weather for a location."); + var plugin = kernel.CreatePluginFromFunctions("my_plugin", [getWeatherFunction]); + + // Create a middleware to handle the plugin functions + var kernelPluginMiddleware = new KernelPluginMiddleware(kernel, plugin); + + var openAIClient = new OpenAIClient(openAIKey); + var openAIAgent = new OpenAIChatAgent( + openAIClient: openAIClient, + name: "assistant", + modelName: modelId) + .RegisterMessageConnector() // register message connector so it support AutoGen built-in message types like TextMessage. + .RegisterMiddleware(kernelPluginMiddleware) // register the middleware to handle the plugin functions + .RegisterPrintMessage(); // pretty print the message to the console + + var toolAggregateMessage = await openAIAgent.SendAsync("Tell me the weather in Seattle"); + + // The aggregate message will be converted to [ToolCallMessage, ToolCallResultMessage] when flowing into the agent + // send the aggregated message to llm to generate the final response + var finalReply = await openAIAgent.SendAsync(toolAggregateMessage); + } +} diff --git a/dotnet/src/AutoGen.Core/Extension/MessageExtension.cs b/dotnet/src/AutoGen.Core/Extension/MessageExtension.cs index 47dbad55e30..3dbba9668f9 100644 --- a/dotnet/src/AutoGen.Core/Extension/MessageExtension.cs +++ b/dotnet/src/AutoGen.Core/Extension/MessageExtension.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // MessageExtension.cs +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -15,7 +16,9 @@ public static string FormatMessage(this IMessage message) { return message switch { +#pragma warning disable CS0618 // deprecated Message msg => msg.FormatMessage(), +#pragma warning restore CS0618 // deprecated TextMessage textMessage => textMessage.FormatMessage(), ImageMessage imageMessage => imageMessage.FormatMessage(), ToolCallMessage toolCallMessage => toolCallMessage.FormatMessage(), @@ -110,6 +113,8 @@ public static string FormatMessage(this AggregateMessage textMessage.Role == Role.System, +#pragma warning disable CS0618 // deprecated Message msg => msg.Role == Role.System, +#pragma warning restore CS0618 // deprecated _ => false, }; } /// /// Get the content from the message - /// if the message is a or , return the content - /// if the message is a and only contains one function call, return the result of that function call + /// if the message implements , return the content from the message by calling /// if the message is a where TMessage1 is and TMessage2 is and the second message only contains one function call, return the result of that function call /// for all other situation, return null. /// @@ -166,10 +172,11 @@ public static bool IsSystemMessage(this IMessage message) { return message switch { - TextMessage textMessage => textMessage.Content, + ICanGetTextContent canGetTextContent => canGetTextContent.GetContent(), + AggregateMessage aggregateMessage => string.Join("\n", aggregateMessage.Message2.ToolCalls.Where(x => x.Result is not null).Select(x => x.Result)), +#pragma warning disable CS0618 // deprecated Message msg => msg.Content, - ToolCallResultMessage toolCallResultMessage => toolCallResultMessage.ToolCalls.Count == 1 ? toolCallResultMessage.ToolCalls.First().Result : null, - AggregateMessage aggregateMessage => aggregateMessage.Message2.ToolCalls.Count == 1 ? aggregateMessage.Message2.ToolCalls.First().Result : null, +#pragma warning restore CS0618 // deprecated _ => null, }; } @@ -182,7 +189,9 @@ public static bool IsSystemMessage(this IMessage message) return message switch { TextMessage textMessage => textMessage.Role, +#pragma warning disable CS0618 // deprecated Message msg => msg.Role, +#pragma warning restore CS0618 // deprecated ImageMessage img => img.Role, MultiModalMessage multiModal => multiModal.Role, _ => null, @@ -191,8 +200,7 @@ public static bool IsSystemMessage(this IMessage message) /// /// Return the tool calls from the message if it's available. - /// if the message is a , return its tool calls - /// if the message is a and the function name and function arguments are available, return a list of tool call with one item + /// if the message implements , return the tool calls from the message by calling /// if the message is a where TMessage1 is and TMessage2 is , return the tool calls from the first message /// /// @@ -201,11 +209,13 @@ public static bool IsSystemMessage(this IMessage message) { return message switch { - ToolCallMessage toolCallMessage => toolCallMessage.ToolCalls, + ICanGetToolCalls canGetToolCalls => canGetToolCalls.GetToolCalls().ToList(), +#pragma warning disable CS0618 // deprecated Message msg => msg.FunctionName is not null && msg.FunctionArguments is not null - ? msg.Content is not null ? new List { new ToolCall(msg.FunctionName, msg.FunctionArguments, result: msg.Content) } - : new List { new ToolCall(msg.FunctionName, msg.FunctionArguments) } + ? msg.Content is not null ? [new ToolCall(msg.FunctionName, msg.FunctionArguments, result: msg.Content)] + : new List { new(msg.FunctionName, msg.FunctionArguments) } : null, +#pragma warning restore CS0618 // deprecated AggregateMessage aggregateMessage => aggregateMessage.Message1.ToolCalls, _ => null, }; diff --git a/dotnet/src/AutoGen.Core/GroupChat/Graph.cs b/dotnet/src/AutoGen.Core/GroupChat/Graph.cs index 78d92508611..02f4da50bae 100644 --- a/dotnet/src/AutoGen.Core/GroupChat/Graph.cs +++ b/dotnet/src/AutoGen.Core/GroupChat/Graph.cs @@ -8,19 +8,6 @@ namespace AutoGen.Core; -/// -/// Obsolete: please use -/// -[Obsolete("please use Graph")] -public class Workflow : Graph -{ - [Obsolete("please use Graph")] - public Workflow(IEnumerable transitions) - : base(transitions) - { - } -} - public class Graph { private readonly List transitions = new List(); diff --git a/dotnet/src/AutoGen.Core/GroupChat/GroupChat.cs b/dotnet/src/AutoGen.Core/GroupChat/GroupChat.cs index 3b6288ca0a7..cd17a21f8b9 100644 --- a/dotnet/src/AutoGen.Core/GroupChat/GroupChat.cs +++ b/dotnet/src/AutoGen.Core/GroupChat/GroupChat.cs @@ -110,7 +110,7 @@ public async Task SelectNextSpeakerAsync(IAgent currentSpeaker, IEnumera {string.Join(",", agentNames)} Each message will start with 'From name:', e.g: -From admin: +From {agentNames.First()}: //your message//."); var conv = this.ProcessConversationsForRolePlay(this.initializeMessages, conversationHistory); diff --git a/dotnet/src/AutoGen.Core/Message/IMessage.cs b/dotnet/src/AutoGen.Core/Message/IMessage.cs index 7b48f4f0d63..ad215d510e3 100644 --- a/dotnet/src/AutoGen.Core/Message/IMessage.cs +++ b/dotnet/src/AutoGen.Core/Message/IMessage.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // IMessage.cs +using System.Collections.Generic; + namespace AutoGen.Core; /// @@ -29,7 +31,7 @@ namespace AutoGen.Core; /// /// : an aggregate message type that contains two message types. /// This type is useful when you want to combine two message types into one unique message type. One example is when invoking a tool call and you want to return both and . -/// One example of how this type is used in AutoGen is +/// One example of how this type is used in AutoGen is and its return message /// /// /// @@ -41,6 +43,24 @@ public interface IMessage : IMessage, IStreamingMessage { } +/// +/// The interface for messages that can get text content. +/// This interface will be used by to get the content from the message. +/// +public interface ICanGetTextContent : IMessage, IStreamingMessage +{ + public string? GetContent(); +} + +/// +/// The interface for messages that can get a list of +/// +public interface ICanGetToolCalls : IMessage, IStreamingMessage +{ + public IEnumerable GetToolCalls(); +} + + public interface IStreamingMessage { string? From { get; set; } diff --git a/dotnet/src/AutoGen.Core/Message/ImageMessage.cs b/dotnet/src/AutoGen.Core/Message/ImageMessage.cs index 1239785c411..d2e2d080300 100644 --- a/dotnet/src/AutoGen.Core/Message/ImageMessage.cs +++ b/dotnet/src/AutoGen.Core/Message/ImageMessage.cs @@ -49,7 +49,9 @@ public ImageMessage(Role role, BinaryData data, string? from = null) public string BuildDataUri() { if (this.Data is null) + { throw new NullReferenceException($"{nameof(Data)}"); + } return $"data:{this.Data.MediaType};base64,{Convert.ToBase64String(this.Data.ToArray())}"; } diff --git a/dotnet/src/AutoGen.Core/Message/Message.cs b/dotnet/src/AutoGen.Core/Message/Message.cs index ec4751b9344..b31b413eca7 100644 --- a/dotnet/src/AutoGen.Core/Message/Message.cs +++ b/dotnet/src/AutoGen.Core/Message/Message.cs @@ -1,10 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Message.cs +using System; using System.Collections.Generic; namespace AutoGen.Core; +[Obsolete("This message class is deprecated, please use a specific AutoGen built-in message type instead. For more information, please visit https://microsoft.github.io/autogen-for-net/articles/Built-in-messages.html")] public class Message : IMessage { public Message( diff --git a/dotnet/src/AutoGen.Core/Message/TextMessage.cs b/dotnet/src/AutoGen.Core/Message/TextMessage.cs index ed4d7436dde..addd8728a92 100644 --- a/dotnet/src/AutoGen.Core/Message/TextMessage.cs +++ b/dotnet/src/AutoGen.Core/Message/TextMessage.cs @@ -3,7 +3,7 @@ namespace AutoGen.Core; -public class TextMessage : IMessage, IStreamingMessage +public class TextMessage : IMessage, IStreamingMessage, ICanGetTextContent { public TextMessage(Role role, string content, string? from = null) { @@ -44,9 +44,14 @@ public override string ToString() { return $"TextMessage({this.Role}, {this.Content}, {this.From})"; } + + public string? GetContent() + { + return this.Content; + } } -public class TextMessageUpdate : IStreamingMessage +public class TextMessageUpdate : IStreamingMessage, ICanGetTextContent { public TextMessageUpdate(Role role, string? content, string? from = null) { @@ -60,4 +65,9 @@ public TextMessageUpdate(Role role, string? content, string? from = null) public string? From { get; set; } public Role Role { get; set; } + + public string? GetContent() + { + return this.Content; + } } diff --git a/dotnet/src/AutoGen.Core/Message/ToolCallAggregateMessage.cs b/dotnet/src/AutoGen.Core/Message/ToolCallAggregateMessage.cs new file mode 100644 index 00000000000..7781b785ef8 --- /dev/null +++ b/dotnet/src/AutoGen.Core/Message/ToolCallAggregateMessage.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// FunctionCallAggregateMessage.cs + +using System.Collections.Generic; + +namespace AutoGen.Core; + +/// +/// An aggregate message that contains a tool call message and a tool call result message. +/// This message type is used by to return both and . +/// +public class ToolCallAggregateMessage : AggregateMessage, ICanGetTextContent, ICanGetToolCalls +{ + public ToolCallAggregateMessage(ToolCallMessage message1, ToolCallResultMessage message2, string? from = null) + : base(message1, message2, from) + { + } + + public string? GetContent() + { + return this.Message2.GetContent(); + } + + public IEnumerable GetToolCalls() + { + return this.Message1.GetToolCalls(); + } +} diff --git a/dotnet/src/AutoGen.Core/Message/ToolCallMessage.cs b/dotnet/src/AutoGen.Core/Message/ToolCallMessage.cs index 8dcd98ea0ec..396dba3d3a1 100644 --- a/dotnet/src/AutoGen.Core/Message/ToolCallMessage.cs +++ b/dotnet/src/AutoGen.Core/Message/ToolCallMessage.cs @@ -26,6 +26,8 @@ public ToolCall(string functionName, string functionArgs, string result) public string FunctionArguments { get; set; } + public string? ToolCallId { get; set; } + public string? Result { get; set; } public override string ToString() @@ -34,7 +36,7 @@ public override string ToString() } } -public class ToolCallMessage : IMessage +public class ToolCallMessage : IMessage, ICanGetToolCalls { public ToolCallMessage(IEnumerable toolCalls, string? from = null) { @@ -45,7 +47,7 @@ public ToolCallMessage(IEnumerable toolCalls, string? from = null) public ToolCallMessage(string functionName, string functionArgs, string? from = null) { this.From = from; - this.ToolCalls = new List { new ToolCall(functionName, functionArgs) }; + this.ToolCalls = new List { new ToolCall(functionName, functionArgs) { ToolCallId = functionName } }; } public ToolCallMessage(ToolCallMessageUpdate update) @@ -89,6 +91,11 @@ public override string ToString() return sb.ToString(); } + + public IEnumerable GetToolCalls() + { + return this.ToolCalls; + } } public class ToolCallMessageUpdate : IStreamingMessage diff --git a/dotnet/src/AutoGen.Core/Message/ToolCallResultMessage.cs b/dotnet/src/AutoGen.Core/Message/ToolCallResultMessage.cs index 99c7740849a..fa7357c941c 100644 --- a/dotnet/src/AutoGen.Core/Message/ToolCallResultMessage.cs +++ b/dotnet/src/AutoGen.Core/Message/ToolCallResultMessage.cs @@ -7,7 +7,7 @@ namespace AutoGen.Core; -public class ToolCallResultMessage : IMessage +public class ToolCallResultMessage : IMessage, ICanGetTextContent { public ToolCallResultMessage(IEnumerable toolCalls, string? from = null) { @@ -18,7 +18,7 @@ public ToolCallResultMessage(IEnumerable toolCalls, string? from = nul public ToolCallResultMessage(string result, string functionName, string functionArgs, string? from = null) { this.From = from; - var toolCall = new ToolCall(functionName, functionArgs); + var toolCall = new ToolCall(functionName, functionArgs) { ToolCallId = functionName }; toolCall.Result = result; this.ToolCalls = [toolCall]; } @@ -30,6 +30,15 @@ public ToolCallResultMessage(string result, string functionName, string function public string? From { get; set; } + public string? GetContent() + { + var results = this.ToolCalls + .Where(x => x.Result != null) + .Select(x => x.Result); + + return string.Join("\n", results); + } + public override string ToString() { var sb = new StringBuilder(); @@ -41,16 +50,4 @@ public override string ToString() return sb.ToString(); } - - private void Validate() - { - // each tool call must have a result - foreach (var toolCall in this.ToolCalls) - { - if (string.IsNullOrEmpty(toolCall.Result)) - { - throw new System.ArgumentException($"The tool call {toolCall} does not have a result"); - } - } - } } diff --git a/dotnet/src/AutoGen.Core/Middleware/FunctionCallMiddleware.cs b/dotnet/src/AutoGen.Core/Middleware/FunctionCallMiddleware.cs index 2bc02805538..d0788077b59 100644 --- a/dotnet/src/AutoGen.Core/Middleware/FunctionCallMiddleware.cs +++ b/dotnet/src/AutoGen.Core/Middleware/FunctionCallMiddleware.cs @@ -18,8 +18,7 @@ namespace AutoGen.Core; /// Otherwise, the message will be sent to the inner agent. In this situation /// if the reply from the inner agent is , /// and the tool calls is available in this middleware's function map, the tools from the reply will be invoked, -/// and a where TMessage1 is and TMessage2 is "/> -/// will be returned. +/// and a will be returned. /// /// If the reply from the inner agent is but the tool calls is not available in this middleware's function map, /// or the reply from the inner agent is not , the original reply from the inner agent will be returned. @@ -128,13 +127,13 @@ private async Task InvokeToolCallMessagesBeforeInvokingAg if (this.functionMap?.TryGetValue(functionName, out var func) is true) { var result = await func(functionArguments); - toolCallResult.Add(new ToolCall(functionName, functionArguments, result)); + toolCallResult.Add(new ToolCall(functionName, functionArguments, result) { ToolCallId = toolCall.ToolCallId }); } else if (this.functionMap is not null) { var errorMessage = $"Function {functionName} is not available. Available functions are: {string.Join(", ", this.functionMap.Select(f => f.Key))}"; - toolCallResult.Add(new ToolCall(functionName, functionArguments, errorMessage)); + toolCallResult.Add(new ToolCall(functionName, functionArguments, errorMessage) { ToolCallId = toolCall.ToolCallId }); } else { @@ -156,14 +155,14 @@ private async Task InvokeToolCallMessagesAfterInvokingAgentAsync(ToolC if (this.functionMap?.TryGetValue(fName, out var func) is true) { var result = await func(fArgs); - toolCallResult.Add(new ToolCall(fName, fArgs, result)); + toolCallResult.Add(new ToolCall(fName, fArgs, result) { ToolCallId = toolCall.ToolCallId }); } } if (toolCallResult.Count() > 0) { var toolCallResultMessage = new ToolCallResultMessage(toolCallResult, from: agent.Name); - return new AggregateMessage(toolCallMsg, toolCallResultMessage, from: agent.Name); + return new ToolCallAggregateMessage(toolCallMsg, toolCallResultMessage, from: agent.Name); } else { diff --git a/dotnet/src/AutoGen.DotnetInteractive/AutoGen.DotnetInteractive.csproj b/dotnet/src/AutoGen.DotnetInteractive/AutoGen.DotnetInteractive.csproj index 57fcb1fce16..72c67fe7801 100644 --- a/dotnet/src/AutoGen.DotnetInteractive/AutoGen.DotnetInteractive.csproj +++ b/dotnet/src/AutoGen.DotnetInteractive/AutoGen.DotnetInteractive.csproj @@ -19,7 +19,7 @@ - + @@ -27,14 +27,12 @@ - - + - - + diff --git a/dotnet/src/AutoGen.DotnetInteractive/DotnetInteractiveFunction.cs b/dotnet/src/AutoGen.DotnetInteractive/DotnetInteractiveFunction.cs index 5587694882c..bb5504cd548 100644 --- a/dotnet/src/AutoGen.DotnetInteractive/DotnetInteractiveFunction.cs +++ b/dotnet/src/AutoGen.DotnetInteractive/DotnetInteractiveFunction.cs @@ -12,57 +12,58 @@ namespace AutoGen.DotnetInteractive; public class DotnetInteractiveFunction : IDisposable { private readonly InteractiveService? _interactiveService = null; - private string? _notebookPath; + private string _notebookPath; private readonly KernelInfoCollection _kernelInfoCollection = new KernelInfoCollection(); + /// + /// Create an instance of " + /// + /// interactive service to use. + /// notebook path if provided. public DotnetInteractiveFunction(InteractiveService interactiveService, string? notebookPath = null, bool continueFromExistingNotebook = false) { this._interactiveService = interactiveService; - this._notebookPath = notebookPath; + this._notebookPath = notebookPath ?? Path.GetTempPath() + "notebook.ipynb"; this._kernelInfoCollection.Add(new KernelInfo("csharp")); this._kernelInfoCollection.Add(new KernelInfo("markdown")); - - if (this._notebookPath != null) + if (continueFromExistingNotebook == false) { - if (continueFromExistingNotebook == false) + // remove existing notebook + if (File.Exists(this._notebookPath)) { - // remove existing notebook - if (File.Exists(this._notebookPath)) - { - File.Delete(this._notebookPath); - } + File.Delete(this._notebookPath); + } - var document = new InteractiveDocument(); + var document = new InteractiveDocument(); - using var stream = File.OpenWrite(_notebookPath); - Notebook.Write(document, stream, this._kernelInfoCollection); - stream.Flush(); - stream.Dispose(); - } - else if (continueFromExistingNotebook == true && File.Exists(this._notebookPath)) + using var stream = File.OpenWrite(_notebookPath); + Notebook.Write(document, stream, this._kernelInfoCollection); + stream.Flush(); + stream.Dispose(); + } + else if (continueFromExistingNotebook == true && File.Exists(this._notebookPath)) + { + // load existing notebook + using var readStream = File.OpenRead(this._notebookPath); + var document = Notebook.Read(readStream, this._kernelInfoCollection); + foreach (var cell in document.Elements) { - // load existing notebook - using var readStream = File.OpenRead(this._notebookPath); - var document = Notebook.Read(readStream, this._kernelInfoCollection); - foreach (var cell in document.Elements) + if (cell.KernelName == "csharp") { - if (cell.KernelName == "csharp") - { - var code = cell.Contents; - this._interactiveService.SubmitCSharpCodeAsync(code, default).Wait(); - } + var code = cell.Contents; + this._interactiveService.SubmitCSharpCodeAsync(code, default).Wait(); } } - else - { - // create an empty notebook - var document = new InteractiveDocument(); + } + else + { + // create an empty notebook + var document = new InteractiveDocument(); - using var stream = File.OpenWrite(_notebookPath); - Notebook.Write(document, stream, this._kernelInfoCollection); - stream.Flush(); - stream.Dispose(); - } + using var stream = File.OpenWrite(_notebookPath); + Notebook.Write(document, stream, this._kernelInfoCollection); + stream.Flush(); + stream.Dispose(); } } diff --git a/dotnet/src/AutoGen.DotnetInteractive/InteractiveService.cs b/dotnet/src/AutoGen.DotnetInteractive/InteractiveService.cs index 0dc34f24e44..7490b64e126 100644 --- a/dotnet/src/AutoGen.DotnetInteractive/InteractiveService.cs +++ b/dotnet/src/AutoGen.DotnetInteractive/InteractiveService.cs @@ -5,7 +5,6 @@ using System.Reactive.Linq; using System.Reflection; using Microsoft.DotNet.Interactive; -using Microsoft.DotNet.Interactive.App.Connection; using Microsoft.DotNet.Interactive.Commands; using Microsoft.DotNet.Interactive.Connection; using Microsoft.DotNet.Interactive.Events; @@ -41,7 +40,7 @@ public InteractiveService(string installingDirectory) public async Task StartAsync(string workingDirectory, CancellationToken ct = default) { - this.kernel = await this.CreateKernelAsync(workingDirectory, ct); + this.kernel = await this.CreateKernelAsync(workingDirectory, true, ct); return true; } @@ -84,7 +83,51 @@ public async Task StartAsync(string workingDirectory, CancellationToken ct return await this.SubmitCommandAsync(command, ct); } - private async Task CreateKernelAsync(string workingDirectory, CancellationToken ct = default) + public bool RestoreDotnetInteractive() + { + this.WriteLine("Restore dotnet interactive tool"); + // write RestoreInteractive.config from embedded resource to this.workingDirectory + var assembly = Assembly.GetAssembly(typeof(InteractiveService))!; + var resourceName = "AutoGen.DotnetInteractive.RestoreInteractive.config"; + using (var stream = assembly.GetManifestResourceStream(resourceName)!) + using (var fileStream = File.Create(Path.Combine(this.installingDirectory, "RestoreInteractive.config"))) + { + stream.CopyTo(fileStream); + } + + // write dotnet-tool.json from embedded resource to this.workingDirectory + + resourceName = "AutoGen.DotnetInteractive.dotnet-tools.json"; + using (var stream2 = assembly.GetManifestResourceStream(resourceName)!) + using (var fileStream2 = File.Create(Path.Combine(this.installingDirectory, "dotnet-tools.json"))) + { + stream2.CopyTo(fileStream2); + } + + var psi = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = $"tool restore --configfile RestoreInteractive.config", + WorkingDirectory = this.installingDirectory, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + using var process = new Process { StartInfo = psi }; + process.OutputDataReceived += this.PrintProcessOutput; + process.ErrorDataReceived += this.PrintProcessOutput; + process.Start(); + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); + process.WaitForExit(); + + return process.ExitCode == 0; + } + + private async Task CreateKernelAsync(string workingDirectory, bool restoreWhenFail = true, CancellationToken ct = default) { try { @@ -139,13 +182,13 @@ await rootProxyKernel.SendAsync( return compositeKernel; } - catch (CommandLineInvocationException ex) when (ex.Message.Contains("Cannot find a tool in the manifest file that has a command named 'dotnet-interactive'")) + catch (CommandLineInvocationException) when (restoreWhenFail) { var success = this.RestoreDotnetInteractive(); if (success) { - return await this.CreateKernelAsync(workingDirectory, ct); + return await this.CreateKernelAsync(workingDirectory, false, ct); } throw; @@ -176,50 +219,6 @@ private void WriteLine(string data) this.Output?.Invoke(this, data); } - private bool RestoreDotnetInteractive() - { - this.WriteLine("Restore dotnet interactive tool"); - // write RestoreInteractive.config from embedded resource to this.workingDirectory - var assembly = Assembly.GetAssembly(typeof(InteractiveService))!; - var resourceName = "AutoGen.DotnetInteractive.RestoreInteractive.config"; - using (var stream = assembly.GetManifestResourceStream(resourceName)!) - using (var fileStream = File.Create(Path.Combine(this.installingDirectory, "RestoreInteractive.config"))) - { - stream.CopyTo(fileStream); - } - - // write dotnet-tool.json from embedded resource to this.workingDirectory - - resourceName = "AutoGen.DotnetInteractive.dotnet-tools.json"; - using (var stream2 = assembly.GetManifestResourceStream(resourceName)!) - using (var fileStream2 = File.Create(Path.Combine(this.installingDirectory, "dotnet-tools.json"))) - { - stream2.CopyTo(fileStream2); - } - - var psi = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = $"tool restore --configfile RestoreInteractive.config", - WorkingDirectory = this.installingDirectory, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true, - }; - - using var process = new Process { StartInfo = psi }; - process.OutputDataReceived += this.PrintProcessOutput; - process.ErrorDataReceived += this.PrintProcessOutput; - process.Start(); - process.BeginErrorReadLine(); - process.BeginOutputReadLine(); - process.WaitForExit(); - - return process.ExitCode == 0; - } - private void PrintProcessOutput(object sender, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) diff --git a/dotnet/src/AutoGen.DotnetInteractive/dotnet-tools.json b/dotnet/src/AutoGen.DotnetInteractive/dotnet-tools.json index b2677b61678..12b09e61cae 100644 --- a/dotnet/src/AutoGen.DotnetInteractive/dotnet-tools.json +++ b/dotnet/src/AutoGen.DotnetInteractive/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "Microsoft.dotnet-interactive": { - "version": "1.0.431302", + "version": "1.0.522904", "commands": [ "dotnet-interactive" ] diff --git a/dotnet/src/AutoGen.Mistral/DTOs/ChatMessage.cs b/dotnet/src/AutoGen.Mistral/DTOs/ChatMessage.cs index c5dae2aa34d..b0fa1757c12 100644 --- a/dotnet/src/AutoGen.Mistral/DTOs/ChatMessage.cs +++ b/dotnet/src/AutoGen.Mistral/DTOs/ChatMessage.cs @@ -13,7 +13,7 @@ public class ChatMessage /// /// role. /// content. - public ChatMessage(RoleEnum? role = default(RoleEnum?), string? content = null) + public ChatMessage(RoleEnum? role = default, string? content = null) { this.Role = role; this.Content = content; @@ -67,18 +67,25 @@ public enum RoleEnum [JsonPropertyName("tool_calls")] public List? ToolCalls { get; set; } + + [JsonPropertyName("tool_call_id")] + public string? ToolCallId { get; set; } } public class FunctionContent { - public FunctionContent(FunctionCall function) + public FunctionContent(string id, FunctionCall function) { this.Function = function; + this.Id = id; } [JsonPropertyName("function")] public FunctionCall Function { get; set; } + [JsonPropertyName("id")] + public string Id { get; set; } + public class FunctionCall { public FunctionCall(string name, string arguments) diff --git a/dotnet/src/AutoGen.Mistral/Middleware/MistralChatMessageConnector.cs b/dotnet/src/AutoGen.Mistral/Middleware/MistralChatMessageConnector.cs index 3ba910aa700..95592e97fcc 100644 --- a/dotnet/src/AutoGen.Mistral/Middleware/MistralChatMessageConnector.cs +++ b/dotnet/src/AutoGen.Mistral/Middleware/MistralChatMessageConnector.cs @@ -158,7 +158,7 @@ private IMessage PostProcessMessage(ChatCompletionResponse response, IAgent from else if (finishReason == Choice.FinishReasonEnum.ToolCalls) { var functionContents = choice.Message?.ToolCalls ?? throw new ArgumentNullException("choice.Message.ToolCalls"); - var toolCalls = functionContents.Select(f => new ToolCall(f.Function.Name, f.Function.Arguments)).ToList(); + var toolCalls = functionContents.Select(f => new ToolCall(f.Function.Name, f.Function.Arguments) { ToolCallId = f.Id }).ToList(); return new ToolCallMessage(toolCalls, from: from.Name); } else @@ -257,6 +257,7 @@ private IEnumerable> ProcessToolCallResultMessage(ToolCall var message = new ChatMessage(ChatMessage.RoleEnum.Tool, content: toolCall.Result) { Name = toolCall.FunctionName, + ToolCallId = toolCall.ToolCallId, }; messages.Add(message); @@ -305,10 +306,12 @@ private IEnumerable> ProcessToolCallMessage(ToolCallMessag // convert tool call message to chat message var chatMessage = new ChatMessage(ChatMessage.RoleEnum.Assistant); chatMessage.ToolCalls = new List(); - foreach (var toolCall in toolCallMessage.ToolCalls) + for (var i = 0; i < toolCallMessage.ToolCalls.Count; i++) { + var toolCall = toolCallMessage.ToolCalls[i]; + var toolCallId = toolCall.ToolCallId ?? $"{toolCall.FunctionName}_{i}"; var functionCall = new FunctionContent.FunctionCall(toolCall.FunctionName, toolCall.FunctionArguments); - var functionContent = new FunctionContent(functionCall); + var functionContent = new FunctionContent(toolCallId, functionCall); chatMessage.ToolCalls.Add(functionContent); } diff --git a/dotnet/src/AutoGen.Ollama/Agent/OllamaAgent.cs b/dotnet/src/AutoGen.Ollama/Agent/OllamaAgent.cs new file mode 100644 index 00000000000..9ef68388d60 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/Agent/OllamaAgent.cs @@ -0,0 +1,185 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// OllamaAgent.cs + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using AutoGen.Core; + +namespace AutoGen.Ollama; + +/// +/// An agent that can interact with ollama models. +/// +public class OllamaAgent : IStreamingAgent +{ + private readonly HttpClient _httpClient; + private readonly string _modelName; + private readonly string _systemMessage; + private readonly OllamaReplyOptions? _replyOptions; + + public OllamaAgent(HttpClient httpClient, string name, string modelName, + string systemMessage = "You are a helpful AI assistant", + OllamaReplyOptions? replyOptions = null) + { + Name = name; + _httpClient = httpClient; + _modelName = modelName; + _systemMessage = systemMessage; + _replyOptions = replyOptions; + } + + public async Task GenerateReplyAsync( + IEnumerable messages, GenerateReplyOptions? options = null, CancellationToken cancellation = default) + { + ChatRequest request = await BuildChatRequest(messages, options); + request.Stream = false; + var httpRequest = BuildRequest(request); + using (HttpResponseMessage? response = await _httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseContentRead, cancellation)) + { + response.EnsureSuccessStatusCode(); + Stream? streamResponse = await response.Content.ReadAsStreamAsync(); + ChatResponse chatResponse = await JsonSerializer.DeserializeAsync(streamResponse, cancellationToken: cancellation) + ?? throw new Exception("Failed to deserialize response"); + var output = new MessageEnvelope(chatResponse, from: Name); + return output; + } + } + + public async IAsyncEnumerable GenerateStreamingReplyAsync( + IEnumerable messages, + GenerateReplyOptions? options = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + ChatRequest request = await BuildChatRequest(messages, options); + request.Stream = true; + HttpRequestMessage message = BuildRequest(request); + using (HttpResponseMessage? response = await _httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, cancellationToken)) + { + response.EnsureSuccessStatusCode(); + using Stream? stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var reader = new StreamReader(stream); + + while (!reader.EndOfStream && !cancellationToken.IsCancellationRequested) + { + string? line = await reader.ReadLineAsync(); + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + ChatResponseUpdate? update = JsonSerializer.Deserialize(line); + if (update is { Done: false }) + { + yield return new MessageEnvelope(update, from: Name); + } + else + { + var finalUpdate = JsonSerializer.Deserialize(line) ?? throw new Exception("Failed to deserialize response"); + + yield return new MessageEnvelope(finalUpdate, from: Name); + } + } + } + } + + public string Name { get; } + + private async Task BuildChatRequest(IEnumerable messages, GenerateReplyOptions? options) + { + var request = new ChatRequest + { + Model = _modelName, + Messages = await BuildChatHistory(messages) + }; + + if (options is OllamaReplyOptions replyOptions) + { + BuildChatRequestOptions(replyOptions, request); + return request; + } + + if (_replyOptions != null) + { + BuildChatRequestOptions(_replyOptions, request); + return request; + } + return request; + } + private void BuildChatRequestOptions(OllamaReplyOptions replyOptions, ChatRequest request) + { + request.Format = replyOptions.Format == FormatType.Json ? OllamaConsts.JsonFormatType : null; + request.Template = replyOptions.Template; + request.KeepAlive = replyOptions.KeepAlive; + + if (replyOptions.Temperature != null + || replyOptions.MaxToken != null + || replyOptions.StopSequence != null + || replyOptions.Seed != null + || replyOptions.MiroStat != null + || replyOptions.MiroStatEta != null + || replyOptions.MiroStatTau != null + || replyOptions.NumCtx != null + || replyOptions.NumGqa != null + || replyOptions.NumGpu != null + || replyOptions.NumThread != null + || replyOptions.RepeatLastN != null + || replyOptions.RepeatPenalty != null + || replyOptions.TopK != null + || replyOptions.TopP != null + || replyOptions.TfsZ != null) + { + request.Options = new ModelReplyOptions + { + Temperature = replyOptions.Temperature, + NumPredict = replyOptions.MaxToken, + Stop = replyOptions.StopSequence?[0], + Seed = replyOptions.Seed, + MiroStat = replyOptions.MiroStat, + MiroStatEta = replyOptions.MiroStatEta, + MiroStatTau = replyOptions.MiroStatTau, + NumCtx = replyOptions.NumCtx, + NumGqa = replyOptions.NumGqa, + NumGpu = replyOptions.NumGpu, + NumThread = replyOptions.NumThread, + RepeatLastN = replyOptions.RepeatLastN, + RepeatPenalty = replyOptions.RepeatPenalty, + TopK = replyOptions.TopK, + TopP = replyOptions.TopP, + TfsZ = replyOptions.TfsZ + }; + } + } + private async Task> BuildChatHistory(IEnumerable messages) + { + var history = messages.Select(m => m switch + { + IMessage chatMessage => chatMessage.Content, + _ => throw new ArgumentException("Invalid message type") + }); + + // if there's no system message in the history, add one to the beginning + if (!history.Any(m => m.Role == "system")) + { + history = new[] { new Message() { Role = "system", Value = _systemMessage } }.Concat(history); + } + + return history.ToList(); + } + + private static HttpRequestMessage BuildRequest(ChatRequest request) + { + string serialized = JsonSerializer.Serialize(request); + return new HttpRequestMessage(HttpMethod.Post, OllamaConsts.ChatCompletionEndpoint) + { + Content = new StringContent(serialized, Encoding.UTF8, OllamaConsts.JsonMediaType) + }; + } +} diff --git a/dotnet/src/AutoGen.Ollama/AutoGen.Ollama.csproj b/dotnet/src/AutoGen.Ollama/AutoGen.Ollama.csproj new file mode 100644 index 00000000000..20924a476b7 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/AutoGen.Ollama.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + AutoGen.Ollama + True + + + + + + + diff --git a/dotnet/src/AutoGen.Ollama/DTOs/ChatRequest.cs b/dotnet/src/AutoGen.Ollama/DTOs/ChatRequest.cs new file mode 100644 index 00000000000..3b0cf04a1a0 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/DTOs/ChatRequest.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ChatRequest.cs + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace AutoGen.Ollama; + +public class ChatRequest +{ + /// + /// (required) the model name + /// + [JsonPropertyName("model")] + public string Model { get; set; } = string.Empty; + + /// + /// the messages of the chat, this can be used to keep a chat memory + /// + [JsonPropertyName("messages")] + public IList Messages { get; set; } = []; + + /// + /// the format to return a response in. Currently, the only accepted value is json + /// + [JsonPropertyName("format")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Format { get; set; } + + /// + /// additional model parameters listed in the documentation for the Modelfile such as temperature + /// + [JsonPropertyName("options")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ModelReplyOptions? Options { get; set; } + /// + /// the prompt template to use (overrides what is defined in the Modelfile) + /// + [JsonPropertyName("template")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Template { get; set; } + /// + /// if false the response will be returned as a single response object, rather than a stream of objects + /// + [JsonPropertyName("stream")] + public bool Stream { get; set; } + /// + /// controls how long the model will stay loaded into memory following the request (default: 5m) + /// + [JsonPropertyName("keep_alive")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? KeepAlive { get; set; } +} diff --git a/dotnet/src/AutoGen.Ollama/DTOs/ChatResponse.cs b/dotnet/src/AutoGen.Ollama/DTOs/ChatResponse.cs new file mode 100644 index 00000000000..7d8142de785 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/DTOs/ChatResponse.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ChatResponse.cs + +using System.Text.Json.Serialization; + +namespace AutoGen.Ollama; + +public class ChatResponse : ChatResponseUpdate +{ + /// + /// time spent generating the response + /// + [JsonPropertyName("total_duration")] + public long TotalDuration { get; set; } + + /// + /// time spent in nanoseconds loading the model + /// + [JsonPropertyName("load_duration")] + public long LoadDuration { get; set; } + + /// + /// number of tokens in the prompt + /// + [JsonPropertyName("prompt_eval_count")] + public int PromptEvalCount { get; set; } + + /// + /// time spent in nanoseconds evaluating the prompt + /// + [JsonPropertyName("prompt_eval_duration")] + public long PromptEvalDuration { get; set; } + + /// + /// number of tokens the response + /// + [JsonPropertyName("eval_count")] + public int EvalCount { get; set; } + + /// + /// time in nanoseconds spent generating the response + /// + [JsonPropertyName("eval_duration")] + public long EvalDuration { get; set; } +} diff --git a/dotnet/src/AutoGen.Ollama/DTOs/ChatResponseUpdate.cs b/dotnet/src/AutoGen.Ollama/DTOs/ChatResponseUpdate.cs new file mode 100644 index 00000000000..8b4dac194f4 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/DTOs/ChatResponseUpdate.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ChatResponseUpdate.cs + +using System.Text.Json.Serialization; + +namespace AutoGen.Ollama; + +public class ChatResponseUpdate +{ + [JsonPropertyName("model")] + public string Model { get; set; } = string.Empty; + + [JsonPropertyName("created_at")] + public string CreatedAt { get; set; } = string.Empty; + + [JsonPropertyName("message")] + public Message? Message { get; set; } + + [JsonPropertyName("done")] + public bool Done { get; set; } +} diff --git a/dotnet/src/AutoGen.Ollama/DTOs/Message.cs b/dotnet/src/AutoGen.Ollama/DTOs/Message.cs new file mode 100644 index 00000000000..2e0d891cc61 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/DTOs/Message.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ChatResponseUpdate.cs + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace AutoGen.Ollama; + +public class Message +{ + public Message() + { + } + + public Message(string role, string value) + { + Role = role; + Value = value; + } + + /// + /// the role of the message, either system, user or assistant + /// + [JsonPropertyName("role")] + public string Role { get; set; } = string.Empty; + /// + /// the content of the message + /// + [JsonPropertyName("content")] + public string Value { get; set; } = string.Empty; + + /// + /// (optional): a list of images to include in the message (for multimodal models such as llava) + /// + [JsonPropertyName("images")] + public IList? Images { get; set; } +} diff --git a/dotnet/src/AutoGen.Ollama/DTOs/ModelReplyOptions.cs b/dotnet/src/AutoGen.Ollama/DTOs/ModelReplyOptions.cs new file mode 100644 index 00000000000..9d54a1bb83b --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/DTOs/ModelReplyOptions.cs @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ModelReplyOptions.cs + +using System.Text.Json.Serialization; + +namespace AutoGen.Ollama; + +//https://github.com/ollama/ollama/blob/main/docs/modelfile.md#valid-parameters-and-values +public class ModelReplyOptions +{ + /// + /// Enable Mirostat sampling for controlling perplexity. (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0) + /// + [JsonPropertyName("mirostat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? MiroStat { get; set; } + + /// + /// Influences how quickly the algorithm responds to feedback from the generated text. + /// A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1) + /// + [JsonPropertyName("mirostat_eta")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public float? MiroStatEta { get; set; } + + /// + /// Controls the balance between coherence and diversity of the output. + /// A lower value will result in more focused and coherent text. (Default: 5.0) + /// + [JsonPropertyName("mirostat_tau")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public float? MiroStatTau { get; set; } + + /// + /// Sets the size of the context window used to generate the next token. (Default: 2048) + /// + [JsonPropertyName("num_ctx")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? NumCtx { get; set; } + + /// + /// The number of GQA groups in the transformer layer. Required for some models, for example it is 8 for llama2:70b + /// + [JsonPropertyName("num_gqa")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? NumGqa { get; set; } + + /// + /// The number of layers to send to the GPU(s). On macOS it defaults to 1 to enable metal support, 0 to disable. + /// + [JsonPropertyName("num_gpu")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? NumGpu { get; set; } + + /// + /// Sets the number of threads to use during computation. By default, Ollama will detect this for optimal performance. + /// It is recommended to set this value to the number of physical CPU cores your system has (as opposed to the logical number of cores). + /// + [JsonPropertyName("num_thread")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? NumThread { get; set; } + + /// + /// Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx) + /// + [JsonPropertyName("repeat_last_n")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? RepeatLastN { get; set; } + + /// + /// Sets how strongly to penalize repetitions. + /// A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1) + /// + [JsonPropertyName("repeat_penalty")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public float? RepeatPenalty { get; set; } + + /// + /// The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8) + /// + [JsonPropertyName("temperature")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public float? Temperature { get; set; } + + /// + /// Sets the random number seed to use for generation. + /// Setting this to a specific number will make the model generate the same text for the same prompt. (Default: 0) + /// + [JsonPropertyName("seed")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Seed { get; set; } + + /// + /// Sets the stop sequences to use. When this pattern is encountered the LLM will stop generating text and return. + /// Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile. + /// + [JsonPropertyName("stop")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Stop { get; set; } + + /// + /// Tail free sampling is used to reduce the impact of less probable tokens from the output. + /// A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1) + /// + [JsonPropertyName("tfs_z")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public float? TfsZ { get; set; } + + /// + /// Maximum number of tokens to predict when generating text. (Default: 128, -1 = infinite generation, -2 = fill context) + /// + [JsonPropertyName("num_predict")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? NumPredict { get; set; } + + /// + /// Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40) + /// + [JsonPropertyName("top_k")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? TopK { get; set; } + + /// + /// Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9) + /// + [JsonPropertyName("top_p")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? TopP { get; set; } +} diff --git a/dotnet/src/AutoGen.Ollama/DTOs/OllamaReplyOptions.cs b/dotnet/src/AutoGen.Ollama/DTOs/OllamaReplyOptions.cs new file mode 100644 index 00000000000..c7c77d1db25 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/DTOs/OllamaReplyOptions.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// OllamaReplyOptions.cs + +using AutoGen.Core; + +namespace AutoGen.Ollama; + +public enum FormatType +{ + None, + Json, +} + +public class OllamaReplyOptions : GenerateReplyOptions +{ + /// + /// the format to return a response in. Currently, the only accepted value is json + /// + public FormatType Format { get; set; } = FormatType.None; + + /// + /// the prompt template to use (overrides what is defined in the Modelfile) + /// + public string? Template { get; set; } + + /// + /// The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8) + /// + public new float? Temperature { get; set; } + + /// + /// controls how long the model will stay loaded into memory following the request (default: 5m) + /// + public string? KeepAlive { get; set; } + + /// + /// Enable Mirostat sampling for controlling perplexity. (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0) + /// + public int? MiroStat { get; set; } + + /// + /// Influences how quickly the algorithm responds to feedback from the generated text. + /// A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1) + /// + public float? MiroStatEta { get; set; } + + /// + /// Controls the balance between coherence and diversity of the output. + /// A lower value will result in more focused and coherent text. (Default: 5.0) + /// + public float? MiroStatTau { get; set; } + + /// + /// Sets the size of the context window used to generate the next token. (Default: 2048) + /// + public int? NumCtx { get; set; } + + /// + /// The number of GQA groups in the transformer layer. Required for some models, for example it is 8 for llama2:70b + /// + public int? NumGqa { get; set; } + + /// + /// The number of layers to send to the GPU(s). On macOS it defaults to 1 to enable metal support, 0 to disable. + /// + public int? NumGpu { get; set; } + + /// + /// Sets the number of threads to use during computation. By default, Ollama will detect this for optimal performance. + /// It is recommended to set this value to the number of physical CPU cores your system has (as opposed to the logical number of cores). + /// + public int? NumThread { get; set; } + + /// + /// Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx) + /// + public int? RepeatLastN { get; set; } + + /// + /// Sets how strongly to penalize repetitions. + /// A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1) + /// + public float? RepeatPenalty { get; set; } + + /// + /// Sets the random number seed to use for generation. + /// Setting this to a specific number will make the model generate the same text for the same prompt. (Default: 0) + /// + public int? Seed { get; set; } + + /// + /// Tail free sampling is used to reduce the impact of less probable tokens from the output. + /// A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1) + /// + public float? TfsZ { get; set; } + + /// + /// Maximum number of tokens to predict when generating text. (Default: 128, -1 = infinite generation, -2 = fill context) + /// + public new int? MaxToken { get; set; } + + /// + /// Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40) + /// + public int? TopK { get; set; } + + /// + /// Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9) + /// + public int? TopP { get; set; } +} diff --git a/dotnet/src/AutoGen.Ollama/Embeddings/ITextEmbeddingService.cs b/dotnet/src/AutoGen.Ollama/Embeddings/ITextEmbeddingService.cs new file mode 100644 index 00000000000..5ce0dc8cc40 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/Embeddings/ITextEmbeddingService.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ITextEmbeddingService.cs + +using System.Threading; +using System.Threading.Tasks; + +namespace AutoGen.Ollama; + +public interface ITextEmbeddingService +{ + public Task GenerateAsync(TextEmbeddingsRequest request, CancellationToken cancellationToken); +} diff --git a/dotnet/src/AutoGen.Ollama/Embeddings/OllamaTextEmbeddingService.cs b/dotnet/src/AutoGen.Ollama/Embeddings/OllamaTextEmbeddingService.cs new file mode 100644 index 00000000000..2e431e7bcb8 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/Embeddings/OllamaTextEmbeddingService.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// OllamaTextEmbeddingService.cs + +using System; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace AutoGen.Ollama; + +public class OllamaTextEmbeddingService : ITextEmbeddingService +{ + private readonly HttpClient _client; + + public OllamaTextEmbeddingService(HttpClient client) + { + _client = client; + } + public async Task GenerateAsync(TextEmbeddingsRequest request, CancellationToken cancellationToken = default) + { + using (HttpResponseMessage? response = await _client + .SendAsync(BuildPostRequest(request), HttpCompletionOption.ResponseContentRead, cancellationToken)) + { + response.EnsureSuccessStatusCode(); + + Stream? streamResponse = await response.Content.ReadAsStreamAsync(); + TextEmbeddingsResponse output = await JsonSerializer + .DeserializeAsync(streamResponse, cancellationToken: cancellationToken) + ?? throw new Exception("Failed to deserialize response"); + return output; + } + } + private static HttpRequestMessage BuildPostRequest(TextEmbeddingsRequest request) + { + string serialized = JsonSerializer.Serialize(request); + return new HttpRequestMessage(HttpMethod.Post, OllamaConsts.EmbeddingsEndpoint) + { + Content = new StringContent(serialized, Encoding.UTF8, OllamaConsts.JsonMediaType) + }; + } +} diff --git a/dotnet/src/AutoGen.Ollama/Embeddings/TextEmbeddingsRequest.cs b/dotnet/src/AutoGen.Ollama/Embeddings/TextEmbeddingsRequest.cs new file mode 100644 index 00000000000..7f2531c522a --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/Embeddings/TextEmbeddingsRequest.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// TextEmbeddingsRequest.cs + +using System.Text.Json.Serialization; + +namespace AutoGen.Ollama; + +public class TextEmbeddingsRequest +{ + /// + /// name of model to generate embeddings from + /// + [JsonPropertyName("model")] + public string Model { get; set; } = string.Empty; + /// + /// text to generate embeddings for + /// + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + /// + /// additional model parameters listed in the documentation for the Modelfile such as temperature + /// + [JsonPropertyName("options")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ModelReplyOptions? Options { get; set; } + /// + /// controls how long the model will stay loaded into memory following the request (default: 5m) + /// + [JsonPropertyName("keep_alive")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? KeepAlive { get; set; } +} diff --git a/dotnet/src/AutoGen.Ollama/Embeddings/TextEmbeddingsResponse.cs b/dotnet/src/AutoGen.Ollama/Embeddings/TextEmbeddingsResponse.cs new file mode 100644 index 00000000000..580059c033b --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/Embeddings/TextEmbeddingsResponse.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// TextEmbeddingsResponse.cs + +using System.Text.Json.Serialization; + +namespace AutoGen.Ollama; + +public class TextEmbeddingsResponse +{ + [JsonPropertyName("embedding")] + public double[]? Embedding { get; set; } +} diff --git a/dotnet/src/AutoGen.Ollama/Extension/OllamaAgentExtension.cs b/dotnet/src/AutoGen.Ollama/Extension/OllamaAgentExtension.cs new file mode 100644 index 00000000000..4c0df513ef8 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/Extension/OllamaAgentExtension.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// OllamaAgentExtension.cs + +using AutoGen.Core; + +namespace AutoGen.Ollama.Extension; + +public static class OllamaAgentExtension +{ + /// + /// Register an to the + /// + /// the connector to use. If null, a new instance of will be created. + public static MiddlewareStreamingAgent RegisterMessageConnector( + this OllamaAgent agent, OllamaMessageConnector? connector = null) + { + if (connector == null) + { + connector = new OllamaMessageConnector(); + } + + return agent.RegisterStreamingMiddleware(connector); + } + + /// + /// Register an to the where T is + /// + /// the connector to use. If null, a new instance of will be created. + public static MiddlewareStreamingAgent RegisterMessageConnector( + this MiddlewareStreamingAgent agent, OllamaMessageConnector? connector = null) + { + if (connector == null) + { + connector = new OllamaMessageConnector(); + } + + return agent.RegisterStreamingMiddleware(connector); + } +} diff --git a/dotnet/src/AutoGen.Ollama/Middlewares/OllamaMessageConnector.cs b/dotnet/src/AutoGen.Ollama/Middlewares/OllamaMessageConnector.cs new file mode 100644 index 00000000000..a21ec3a1c99 --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/Middlewares/OllamaMessageConnector.cs @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// OllamaMessageConnector.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using AutoGen.Core; + +namespace AutoGen.Ollama; + +public class OllamaMessageConnector : IStreamingMiddleware +{ + public string Name => nameof(OllamaMessageConnector); + + public async Task InvokeAsync(MiddlewareContext context, IAgent agent, + CancellationToken cancellationToken = default) + { + var messages = ProcessMessage(context.Messages, agent); + IMessage reply = await agent.GenerateReplyAsync(messages, context.Options, cancellationToken); + + return reply switch + { + IMessage messageEnvelope when messageEnvelope.Content.Message?.Value is string content => new TextMessage(Role.Assistant, content, messageEnvelope.From), + IMessage messageEnvelope when messageEnvelope.Content.Message?.Value is null => throw new InvalidOperationException("Message content is null"), + _ => reply + }; + } + + public async IAsyncEnumerable InvokeAsync(MiddlewareContext context, IStreamingAgent agent, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var messages = ProcessMessage(context.Messages, agent); + var chunks = new List(); + await foreach (var update in agent.GenerateStreamingReplyAsync(messages, context.Options, cancellationToken)) + { + if (update is IStreamingMessage chatResponseUpdate) + { + var response = chatResponseUpdate.Content switch + { + _ when chatResponseUpdate.Content.Message?.Value is string content => new TextMessageUpdate(Role.Assistant, content, chatResponseUpdate.From), + _ => null, + }; + + if (response != null) + { + chunks.Add(chatResponseUpdate.Content); + yield return response; + } + } + else + { + yield return update; + } + } + + if (chunks.Count == 0) + { + yield break; + } + + // if the chunks are not empty, aggregate them into a single message + var messageContent = string.Join(string.Empty, chunks.Select(c => c.Message?.Value)); + var message = new TextMessage(Role.Assistant, messageContent, agent.Name); + + yield return message; + } + + private IEnumerable ProcessMessage(IEnumerable messages, IAgent agent) + { + return messages.SelectMany(m => + { + if (m is IMessage messageEnvelope) + { + return [m]; + } + else + { + return m switch + { + TextMessage textMessage => ProcessTextMessage(textMessage, agent), + ImageMessage imageMessage => ProcessImageMessage(imageMessage, agent), + MultiModalMessage multiModalMessage => ProcessMultiModalMessage(multiModalMessage, agent), + _ => [m], + }; + } + }); + } + + private IEnumerable ProcessMultiModalMessage(MultiModalMessage multiModalMessage, IAgent agent) + { + var textMessages = multiModalMessage.Content.Where(m => m is TextMessage textMessage && textMessage.GetContent() is not null); + var imageMessages = multiModalMessage.Content.Where(m => m is ImageMessage); + + // aggregate the text messages into one message + // by concatenating the content using newline + var textContent = string.Join("\n", textMessages.Select(m => ((TextMessage)m).Content)); + + // collect all the images + var images = imageMessages.SelectMany(m => ProcessImageMessage((ImageMessage)m, agent) + .SelectMany(m => (m as IMessage)?.Content.Images)); + + var message = new Message() + { + Role = "user", + Value = textContent, + Images = images.ToList(), + }; + + return [MessageEnvelope.Create(message, agent.Name)]; + } + + private IEnumerable ProcessImageMessage(ImageMessage imageMessage, IAgent agent) + { + byte[]? data = imageMessage.Data?.ToArray(); + if (data is null) + { + if (imageMessage.Url is null) + { + throw new InvalidOperationException("Invalid ImageMessage, the data or url must be provided"); + } + + var uri = new Uri(imageMessage.Url); + // download the image from the URL + using var client = new HttpClient(); + var response = client.GetAsync(uri).Result; + if (!response.IsSuccessStatusCode) + { + throw new HttpRequestException($"Failed to download the image from {uri}"); + } + + data = response.Content.ReadAsByteArrayAsync().Result; + } + + var base64Image = Convert.ToBase64String(data); + var message = imageMessage.From switch + { + null when imageMessage.Role == Role.User => new Message { Role = "user", Images = [base64Image] }, + null => throw new InvalidOperationException("Invalid Role, the role must be user"), + _ when imageMessage.From != agent.Name => new Message { Role = "user", Images = [base64Image] }, + _ => throw new InvalidOperationException("The from field must be null or the agent name"), + }; + + return [MessageEnvelope.Create(message, agent.Name)]; + } + + private IEnumerable ProcessTextMessage(TextMessage textMessage, IAgent agent) + { + if (textMessage.Role == Role.System) + { + var message = new Message + { + Role = "system", + Value = textMessage.Content + }; + + return [MessageEnvelope.Create(message, agent.Name)]; + } + else if (textMessage.From == agent.Name) + { + var message = new Message + { + Role = "assistant", + Value = textMessage.Content + }; + + return [MessageEnvelope.Create(message, agent.Name)]; + } + else + { + var message = textMessage.From switch + { + null when textMessage.Role == Role.User => new Message { Role = "user", Value = textMessage.Content }, + null when textMessage.Role == Role.Assistant => new Message { Role = "assistant", Value = textMessage.Content }, + null => throw new InvalidOperationException("Invalid Role"), + _ when textMessage.From != agent.Name => new Message { Role = "user", Value = textMessage.Content }, + _ => throw new InvalidOperationException("The from field must be null or the agent name"), + }; + + return [MessageEnvelope.Create(message, agent.Name)]; + } + } +} diff --git a/dotnet/src/AutoGen.Ollama/OllamaConsts.cs b/dotnet/src/AutoGen.Ollama/OllamaConsts.cs new file mode 100644 index 00000000000..f305446a9aa --- /dev/null +++ b/dotnet/src/AutoGen.Ollama/OllamaConsts.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// OllamaConsts.cs + +namespace AutoGen.Ollama; + +public class OllamaConsts +{ + public const string JsonFormatType = "json"; + public const string JsonMediaType = "application/json"; + public const string ChatCompletionEndpoint = "/api/chat"; + public const string EmbeddingsEndpoint = "/api/embeddings"; +} diff --git a/dotnet/src/AutoGen.OpenAI/Agent/GPTAgent.cs b/dotnet/src/AutoGen.OpenAI/Agent/GPTAgent.cs index 52070788e34..cdc6cc464d1 100644 --- a/dotnet/src/AutoGen.OpenAI/Agent/GPTAgent.cs +++ b/dotnet/src/AutoGen.OpenAI/Agent/GPTAgent.cs @@ -29,10 +29,8 @@ namespace AutoGen.OpenAI; /// public class GPTAgent : IStreamingAgent { - private readonly IDictionary>>? functionMap; private readonly OpenAIClient openAIClient; - private readonly string? modelName; - private readonly OpenAIChatAgent _innerAgent; + private readonly IStreamingAgent _innerAgent; public GPTAgent( string name, @@ -52,16 +50,23 @@ public GPTAgent( _ => throw new ArgumentException($"Unsupported config type {config.GetType()}"), }; - modelName = config switch + var modelName = config switch { AzureOpenAIConfig azureConfig => azureConfig.DeploymentName, OpenAIConfig openAIConfig => openAIConfig.ModelId, _ => throw new ArgumentException($"Unsupported config type {config.GetType()}"), }; - _innerAgent = new OpenAIChatAgent(openAIClient, name, modelName, systemMessage, temperature, maxTokens, seed, responseFormat, functions); + _innerAgent = new OpenAIChatAgent(openAIClient, name, modelName, systemMessage, temperature, maxTokens, seed, responseFormat, functions) + .RegisterMessageConnector(); + + if (functionMap is not null) + { + var functionMapMiddleware = new FunctionCallMiddleware(functionMap: functionMap); + _innerAgent = _innerAgent.RegisterStreamingMiddleware(functionMapMiddleware); + } + Name = name; - this.functionMap = functionMap; } public GPTAgent( @@ -77,10 +82,16 @@ public GPTAgent( IDictionary>>? functionMap = null) { this.openAIClient = openAIClient; - this.modelName = modelName; Name = name; - this.functionMap = functionMap; - _innerAgent = new OpenAIChatAgent(openAIClient, name, modelName, systemMessage, temperature, maxTokens, seed, responseFormat, functions); + + _innerAgent = new OpenAIChatAgent(openAIClient, name, modelName, systemMessage, temperature, maxTokens, seed, responseFormat, functions) + .RegisterMessageConnector(); + + if (functionMap is not null) + { + var functionMapMiddleware = new FunctionCallMiddleware(functionMap: functionMap); + _innerAgent = _innerAgent.RegisterStreamingMiddleware(functionMapMiddleware); + } } public string Name { get; } @@ -90,14 +101,7 @@ public async Task GenerateReplyAsync( GenerateReplyOptions? options = null, CancellationToken cancellationToken = default) { - var agent = this._innerAgent.RegisterMessageConnector(); - if (this.functionMap is not null) - { - var functionMapMiddleware = new FunctionCallMiddleware(functionMap: this.functionMap); - agent = agent.RegisterStreamingMiddleware(functionMapMiddleware); - } - - return await agent.GenerateReplyAsync(messages, options, cancellationToken); + return await _innerAgent.GenerateReplyAsync(messages, options, cancellationToken); } public IAsyncEnumerable GenerateStreamingReplyAsync( @@ -105,13 +109,6 @@ public IAsyncEnumerable GenerateStreamingReplyAsync( GenerateReplyOptions? options = null, CancellationToken cancellationToken = default) { - var agent = this._innerAgent.RegisterMessageConnector(); - if (this.functionMap is not null) - { - var functionMapMiddleware = new FunctionCallMiddleware(functionMap: this.functionMap); - agent = agent.RegisterStreamingMiddleware(functionMapMiddleware); - } - - return agent.GenerateStreamingReplyAsync(messages, options, cancellationToken); + return _innerAgent.GenerateStreamingReplyAsync(messages, options, cancellationToken); } } diff --git a/dotnet/src/AutoGen.OpenAI/Extension/MessageExtension.cs b/dotnet/src/AutoGen.OpenAI/Extension/MessageExtension.cs index b3dfb1e8668..ed795e5e8ed 100644 --- a/dotnet/src/AutoGen.OpenAI/Extension/MessageExtension.cs +++ b/dotnet/src/AutoGen.OpenAI/Extension/MessageExtension.cs @@ -12,6 +12,8 @@ public static class MessageExtension { public static string TEXT_CONTENT_TYPE = "text"; public static string IMAGE_CONTENT_TYPE = "image"; + + [Obsolete("This method is deprecated, please replace Message with one of the built-in message types.")] public static ChatRequestUserMessage ToChatRequestUserMessage(this Message message) { if (message.Value is ChatRequestUserMessage message1) @@ -50,6 +52,7 @@ public static ChatRequestUserMessage ToChatRequestUserMessage(this Message messa throw new ArgumentException("Content is null and metadata is null"); } + [Obsolete("This method is deprecated")] public static IEnumerable ToOpenAIChatRequestMessage(this IAgent agent, IMessage message) { if (message is IMessage oaiMessage) diff --git a/dotnet/src/AutoGen.OpenAI/Middleware/OpenAIChatRequestMessageConnector.cs b/dotnet/src/AutoGen.OpenAI/Middleware/OpenAIChatRequestMessageConnector.cs index 2bd9470ffa7..8f1825e2fa0 100644 --- a/dotnet/src/AutoGen.OpenAI/Middleware/OpenAIChatRequestMessageConnector.cs +++ b/dotnet/src/AutoGen.OpenAI/Middleware/OpenAIChatRequestMessageConnector.cs @@ -19,7 +19,6 @@ namespace AutoGen.OpenAI; /// - /// - /// - -/// - /// - where T is /// - where TMessage1 is and TMessage2 is /// @@ -27,6 +26,11 @@ public class OpenAIChatRequestMessageConnector : IMiddleware, IStreamingMiddlewa { private bool strictMode = false; + /// + /// Create a new instance of . + /// + /// If true, will throw an + /// When the message type is not supported. If false, it will ignore the unsupported message type. public OpenAIChatRequestMessageConnector(bool strictMode = false) { this.strictMode = strictMode; @@ -36,8 +40,7 @@ public OpenAIChatRequestMessageConnector(bool strictMode = false) public async Task InvokeAsync(MiddlewareContext context, IAgent agent, CancellationToken cancellationToken = default) { - var chatMessages = ProcessIncomingMessages(agent, context.Messages) - .Select(m => new MessageEnvelope(m)); + var chatMessages = ProcessIncomingMessages(agent, context.Messages); var reply = await agent.GenerateReplyAsync(chatMessages, context.Options, cancellationToken); @@ -49,8 +52,7 @@ public async IAsyncEnumerable InvokeAsync( IStreamingAgent agent, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - var chatMessages = ProcessIncomingMessages(agent, context.Messages) - .Select(m => new MessageEnvelope(m)); + var chatMessages = ProcessIncomingMessages(agent, context.Messages); var streamingReply = agent.GenerateStreamingReplyAsync(chatMessages, context.Options, cancellationToken); string? currentToolName = null; await foreach (var reply in streamingReply) @@ -73,7 +75,14 @@ public async IAsyncEnumerable InvokeAsync( } else { - yield return reply; + if (this.strictMode) + { + throw new InvalidOperationException($"Invalid streaming message type {reply.GetType().Name}"); + } + else + { + yield return reply; + } } } } @@ -82,16 +91,10 @@ public IMessage PostProcessMessage(IMessage message) { return message switch { - TextMessage => message, - ImageMessage => message, - MultiModalMessage => message, - ToolCallMessage => message, - ToolCallResultMessage => message, - Message => message, - AggregateMessage => message, - IMessage m => PostProcessMessage(m), - IMessage m => PostProcessMessage(m), - _ => throw new InvalidOperationException("The type of message is not supported. Must be one of TextMessage, ImageMessage, MultiModalMessage, ToolCallMessage, ToolCallResultMessage, Message, IMessage, AggregateMessage"), + IMessage m => PostProcessChatResponseMessage(m.Content, m.From), + IMessage m => PostProcessChatCompletions(m), + _ when strictMode is false => message, + _ => throw new InvalidOperationException($"Invalid return message type {message.GetType().Name}"), }; } @@ -120,12 +123,7 @@ public IMessage PostProcessMessage(IMessage message) } } - private IMessage PostProcessMessage(IMessage message) - { - return PostProcessMessage(message.Content, message.From); - } - - private IMessage PostProcessMessage(IMessage message) + private IMessage PostProcessChatCompletions(IMessage message) { // throw exception if prompt filter results is not null if (message.Content.Choices[0].FinishReason == CompletionsFinishReason.ContentFiltered) @@ -133,12 +131,12 @@ private IMessage PostProcessMessage(IMessage message) throw new InvalidOperationException("The content is filtered because its potential risk. Please try another input."); } - return PostProcessMessage(message.Content.Choices[0].Message, message.From); + return PostProcessChatResponseMessage(message.Content.Choices[0].Message, message.From); } - private IMessage PostProcessMessage(ChatResponseMessage chatResponseMessage, string? from) + private IMessage PostProcessChatResponseMessage(ChatResponseMessage chatResponseMessage, string? from) { - if (chatResponseMessage.Content is string content) + if (chatResponseMessage.Content is string content && !string.IsNullOrEmpty(content)) { return new TextMessage(Role.Assistant, content, from); } @@ -154,7 +152,7 @@ private IMessage PostProcessMessage(ChatResponseMessage chatResponseMessage, str .Where(tc => tc is ChatCompletionsFunctionToolCall) .Select(tc => (ChatCompletionsFunctionToolCall)tc); - var toolCalls = functionToolCalls.Select(tc => new ToolCall(tc.Name, tc.Arguments)); + var toolCalls = functionToolCalls.Select(tc => new ToolCall(tc.Name, tc.Arguments) { ToolCallId = tc.Id }); return new ToolCallMessage(toolCalls, from); } @@ -162,112 +160,44 @@ private IMessage PostProcessMessage(ChatResponseMessage chatResponseMessage, str throw new InvalidOperationException("Invalid ChatResponseMessage"); } - public IEnumerable ProcessIncomingMessages(IAgent agent, IEnumerable messages) + public IEnumerable ProcessIncomingMessages(IAgent agent, IEnumerable messages) { - return messages.SelectMany(m => + return messages.SelectMany(m => { - if (m.From == null) + if (m is IMessage crm) { - return ProcessIncomingMessagesWithEmptyFrom(m); - } - else if (m.From == agent.Name) - { - return ProcessIncomingMessagesForSelf(m); + return [crm]; } else { - return ProcessIncomingMessagesForOther(m); + var chatRequestMessages = m switch + { + TextMessage textMessage => ProcessTextMessage(agent, textMessage), + ImageMessage imageMessage when (imageMessage.From is null || imageMessage.From != agent.Name) => ProcessImageMessage(agent, imageMessage), + MultiModalMessage multiModalMessage when (multiModalMessage.From is null || multiModalMessage.From != agent.Name) => ProcessMultiModalMessage(agent, multiModalMessage), + ToolCallMessage toolCallMessage when (toolCallMessage.From is null || toolCallMessage.From == agent.Name) => ProcessToolCallMessage(agent, toolCallMessage), + ToolCallResultMessage toolCallResultMessage => ProcessToolCallResultMessage(toolCallResultMessage), + AggregateMessage aggregateMessage => ProcessFunctionCallMiddlewareMessage(agent, aggregateMessage), +#pragma warning disable CS0618 // deprecated + Message msg => ProcessMessage(agent, msg), +#pragma warning restore CS0618 // deprecated + _ when strictMode is false => [], + _ => throw new InvalidOperationException($"Invalid message type: {m.GetType().Name}"), + }; + + if (chatRequestMessages.Any()) + { + return chatRequestMessages.Select(cm => MessageEnvelope.Create(cm, m.From)); + } + else + { + return [m]; + } } }); } - private IEnumerable ProcessIncomingMessagesForSelf(IMessage message) - { - return message switch - { - TextMessage textMessage => ProcessIncomingMessagesForSelf(textMessage), - ImageMessage imageMessage => ProcessIncomingMessagesForSelf(imageMessage), - MultiModalMessage multiModalMessage => ProcessIncomingMessagesForSelf(multiModalMessage), - ToolCallMessage toolCallMessage => ProcessIncomingMessagesForSelf(toolCallMessage), - ToolCallResultMessage toolCallResultMessage => ProcessIncomingMessagesForSelf(toolCallResultMessage), - Message msg => ProcessIncomingMessagesForSelf(msg), - IMessage crm => ProcessIncomingMessagesForSelf(crm), - AggregateMessage aggregateMessage => ProcessIncomingMessagesForSelf(aggregateMessage), - _ => throw new NotImplementedException(), - }; - } - - private IEnumerable ProcessIncomingMessagesWithEmptyFrom(IMessage message) - { - return message switch - { - TextMessage textMessage => ProcessIncomingMessagesWithEmptyFrom(textMessage), - ImageMessage imageMessage => ProcessIncomingMessagesWithEmptyFrom(imageMessage), - MultiModalMessage multiModalMessage => ProcessIncomingMessagesWithEmptyFrom(multiModalMessage), - ToolCallMessage toolCallMessage => ProcessIncomingMessagesWithEmptyFrom(toolCallMessage), - ToolCallResultMessage toolCallResultMessage => ProcessIncomingMessagesWithEmptyFrom(toolCallResultMessage), - Message msg => ProcessIncomingMessagesWithEmptyFrom(msg), - IMessage crm => ProcessIncomingMessagesWithEmptyFrom(crm), - AggregateMessage aggregateMessage => ProcessIncomingMessagesWithEmptyFrom(aggregateMessage), - _ => throw new NotImplementedException(), - }; - } - - private IEnumerable ProcessIncomingMessagesForOther(IMessage message) - { - return message switch - { - TextMessage textMessage => ProcessIncomingMessagesForOther(textMessage), - ImageMessage imageMessage => ProcessIncomingMessagesForOther(imageMessage), - MultiModalMessage multiModalMessage => ProcessIncomingMessagesForOther(multiModalMessage), - ToolCallMessage toolCallMessage => ProcessIncomingMessagesForOther(toolCallMessage), - ToolCallResultMessage toolCallResultMessage => ProcessIncomingMessagesForOther(toolCallResultMessage), - Message msg => ProcessIncomingMessagesForOther(msg), - IMessage crm => ProcessIncomingMessagesForOther(crm), - AggregateMessage aggregateMessage => ProcessIncomingMessagesForOther(aggregateMessage), - _ => throw new NotImplementedException(), - }; - } - - private IEnumerable ProcessIncomingMessagesForSelf(TextMessage message) - { - if (message.Role == Role.System) - { - return new[] { new ChatRequestSystemMessage(message.Content) }; - } - else - { - return new[] { new ChatRequestAssistantMessage(message.Content) }; - } - } - - private IEnumerable ProcessIncomingMessagesForSelf(ImageMessage _) - { - return [new ChatRequestAssistantMessage("// Image Message is not supported")]; - } - - private IEnumerable ProcessIncomingMessagesForSelf(MultiModalMessage _) - { - return [new ChatRequestAssistantMessage("// MultiModal Message is not supported")]; - } - - private IEnumerable ProcessIncomingMessagesForSelf(ToolCallMessage message) - { - var toolCall = message.ToolCalls.Select(tc => new ChatCompletionsFunctionToolCall(tc.FunctionName, tc.FunctionName, tc.FunctionArguments)); - var chatRequestMessage = new ChatRequestAssistantMessage(string.Empty); - foreach (var tc in toolCall) - { - chatRequestMessage.ToolCalls.Add(tc); - } - - return new[] { chatRequestMessage }; - } - - private IEnumerable ProcessIncomingMessagesForSelf(ToolCallResultMessage message) - { - return message.ToolCalls.Select(tc => new ChatRequestToolMessage(tc.Result, tc.FunctionName)); - } - + [Obsolete("This method is deprecated, please use ProcessIncomingMessages(IAgent agent, IEnumerable messages) instead.")] private IEnumerable ProcessIncomingMessagesForSelf(Message message) { if (message.Role == Role.System) @@ -303,151 +233,147 @@ private IEnumerable ProcessIncomingMessagesForSelf(Message m } } - private IEnumerable ProcessIncomingMessagesForSelf(IMessage message) - { - return new[] { message.Content }; - } - - private IEnumerable ProcessIncomingMessagesForSelf(AggregateMessage aggregateMessage) + [Obsolete("This method is deprecated, please use ProcessIncomingMessages(IAgent agent, IEnumerable messages) instead.")] + private IEnumerable ProcessIncomingMessagesForOther(Message message) { - var toolCallMessage1 = aggregateMessage.Message1; - var toolCallResultMessage = aggregateMessage.Message2; - - var assistantMessage = new ChatRequestAssistantMessage(string.Empty); - var toolCalls = toolCallMessage1.ToolCalls.Select(tc => new ChatCompletionsFunctionToolCall(tc.FunctionName, tc.FunctionName, tc.FunctionArguments)); - foreach (var tc in toolCalls) + if (message.Role == Role.System) { - assistantMessage.ToolCalls.Add(tc); + return [new ChatRequestSystemMessage(message.Content) { Name = message.From }]; } + else if (message.Content is string content && content is { Length: > 0 }) + { + if (message.FunctionName is not null) + { + return new[] { new ChatRequestToolMessage(content, message.FunctionName) }; + } - var toolCallResults = toolCallResultMessage.ToolCalls.Select(tc => new ChatRequestToolMessage(tc.Result, tc.FunctionName)); - - // return assistantMessage and tool call result messages - var messages = new List { assistantMessage }; - messages.AddRange(toolCallResults); - - return messages; + return [new ChatRequestUserMessage(message.Content) { Name = message.From }]; + } + else if (message.FunctionName is string _) + { + return [new ChatRequestUserMessage("// Message type is not supported") { Name = message.From }]; + } + else + { + throw new InvalidOperationException("Invalid Message as message from other."); + } } - private IEnumerable ProcessIncomingMessagesForOther(TextMessage message) + private IEnumerable ProcessTextMessage(IAgent agent, TextMessage message) { if (message.Role == Role.System) { - return new[] { new ChatRequestSystemMessage(message.Content) }; + return [new ChatRequestSystemMessage(message.Content) { Name = message.From }]; + } + + if (agent.Name == message.From) + { + return [new ChatRequestAssistantMessage(message.Content) { Name = agent.Name }]; } else { - return new[] { new ChatRequestUserMessage(message.Content) }; + return message.From switch + { + null when message.Role == Role.User => [new ChatRequestUserMessage(message.Content)], + null when message.Role == Role.Assistant => [new ChatRequestAssistantMessage(message.Content)], + null => throw new InvalidOperationException("Invalid Role"), + _ => [new ChatRequestUserMessage(message.Content) { Name = message.From }] + }; } } - private IEnumerable ProcessIncomingMessagesForOther(ImageMessage message) + private IEnumerable ProcessImageMessage(IAgent agent, ImageMessage message) { - return new[] { new ChatRequestUserMessage([ - new ChatMessageImageContentItem(new Uri(message.Url ?? message.BuildDataUri())), - ])}; + if (agent.Name == message.From) + { + // image message from assistant is not supported + throw new ArgumentException("ImageMessage is not supported when message.From is the same with agent"); + } + + var imageContentItem = this.CreateChatMessageImageContentItemFromImageMessage(message); + return [new ChatRequestUserMessage([imageContentItem]) { Name = message.From }]; } - private IEnumerable ProcessIncomingMessagesForOther(MultiModalMessage message) + private IEnumerable ProcessMultiModalMessage(IAgent agent, MultiModalMessage message) { + if (agent.Name == message.From) + { + // image message from assistant is not supported + throw new ArgumentException("MultiModalMessage is not supported when message.From is the same with agent"); + } + IEnumerable items = message.Content.Select(ci => ci switch { TextMessage text => new ChatMessageTextContentItem(text.Content), - ImageMessage image => new ChatMessageImageContentItem(new Uri(image.Url ?? image.BuildDataUri())), + ImageMessage image => this.CreateChatMessageImageContentItemFromImageMessage(image), _ => throw new NotImplementedException(), }); - return new[] { new ChatRequestUserMessage(items) }; + return [new ChatRequestUserMessage(items) { Name = message.From }]; } - private IEnumerable ProcessIncomingMessagesForOther(ToolCallMessage msg) + private ChatMessageImageContentItem CreateChatMessageImageContentItemFromImageMessage(ImageMessage message) { - throw new ArgumentException("ToolCallMessage is not supported when message.From is not the same with agent"); + return message.Data is null + ? new ChatMessageImageContentItem(new Uri(message.Url)) + : new ChatMessageImageContentItem(message.Data, message.Data.MediaType); } - private IEnumerable ProcessIncomingMessagesForOther(ToolCallResultMessage message) + private IEnumerable ProcessToolCallMessage(IAgent agent, ToolCallMessage message) { - return message.ToolCalls.Select(tc => new ChatRequestToolMessage(tc.Result, tc.FunctionName)); - } - - private IEnumerable ProcessIncomingMessagesForOther(Message message) - { - if (message.Role == Role.System) + if (message.From is not null && message.From != agent.Name) { - return new[] { new ChatRequestSystemMessage(message.Content) }; + throw new ArgumentException("ToolCallMessage is not supported when message.From is not the same with agent"); } - else if (message.Content is string content && content is { Length: > 0 }) - { - if (message.FunctionName is not null) - { - return new[] { new ChatRequestToolMessage(content, message.FunctionName) }; - } - return new[] { new ChatRequestUserMessage(message.Content) }; - } - else if (message.FunctionName is string _) - { - return new[] - { - new ChatRequestUserMessage("// Message type is not supported"), - }; - } - else + var toolCall = message.ToolCalls.Select((tc, i) => new ChatCompletionsFunctionToolCall(tc.ToolCallId ?? $"{tc.FunctionName}_{i}", tc.FunctionName, tc.FunctionArguments)); + var chatRequestMessage = new ChatRequestAssistantMessage(string.Empty) { Name = message.From }; + foreach (var tc in toolCall) { - throw new InvalidOperationException("Invalid Message as message from other."); + chatRequestMessage.ToolCalls.Add(tc); } - } - - private IEnumerable ProcessIncomingMessagesForOther(IMessage message) - { - return new[] { message.Content }; - } - - private IEnumerable ProcessIncomingMessagesForOther(AggregateMessage aggregateMessage) - { - // convert as user message - var resultMessage = aggregateMessage.Message2; - - return resultMessage.ToolCalls.Select(tc => new ChatRequestUserMessage(tc.Result)); - } - - private IEnumerable ProcessIncomingMessagesWithEmptyFrom(TextMessage message) - { - return ProcessIncomingMessagesForOther(message); - } - private IEnumerable ProcessIncomingMessagesWithEmptyFrom(ImageMessage message) - { - return ProcessIncomingMessagesForOther(message); + return [chatRequestMessage]; } - private IEnumerable ProcessIncomingMessagesWithEmptyFrom(MultiModalMessage message) + private IEnumerable ProcessToolCallResultMessage(ToolCallResultMessage message) { - return ProcessIncomingMessagesForOther(message); + return message.ToolCalls + .Where(tc => tc.Result is not null) + .Select((tc, i) => new ChatRequestToolMessage(tc.Result, tc.ToolCallId ?? $"{tc.FunctionName}_{i}")); } - private IEnumerable ProcessIncomingMessagesWithEmptyFrom(ToolCallMessage message) + [Obsolete("This method is deprecated, please use ProcessIncomingMessages(IAgent agent, IEnumerable messages) instead.")] + private IEnumerable ProcessMessage(IAgent agent, Message message) { - return ProcessIncomingMessagesForSelf(message); + if (message.From is not null && message.From != agent.Name) + { + return ProcessIncomingMessagesForOther(message); + } + else + { + return ProcessIncomingMessagesForSelf(message); + } } - private IEnumerable ProcessIncomingMessagesWithEmptyFrom(ToolCallResultMessage message) + private IEnumerable ProcessFunctionCallMiddlewareMessage(IAgent agent, AggregateMessage aggregateMessage) { - return ProcessIncomingMessagesForOther(message); - } + if (aggregateMessage.From is not null && aggregateMessage.From != agent.Name) + { + // convert as user message + var resultMessage = aggregateMessage.Message2; - private IEnumerable ProcessIncomingMessagesWithEmptyFrom(Message message) - { - return ProcessIncomingMessagesForOther(message); - } + return resultMessage.ToolCalls.Select(tc => new ChatRequestUserMessage(tc.Result) { Name = aggregateMessage.From }); + } + else + { + var toolCallMessage1 = aggregateMessage.Message1; + var toolCallResultMessage = aggregateMessage.Message2; - private IEnumerable ProcessIncomingMessagesWithEmptyFrom(IMessage message) - { - return new[] { message.Content }; - } + var assistantMessage = this.ProcessToolCallMessage(agent, toolCallMessage1); + var toolCallResults = this.ProcessToolCallResultMessage(toolCallResultMessage); - private IEnumerable ProcessIncomingMessagesWithEmptyFrom(AggregateMessage aggregateMessage) - { - return ProcessIncomingMessagesForOther(aggregateMessage); + return assistantMessage.Concat(toolCallResults); + } } } diff --git a/dotnet/src/AutoGen.SemanticKernel/Middleware/SemanticKernelChatMessageContentConnector.cs b/dotnet/src/AutoGen.SemanticKernel/Middleware/SemanticKernelChatMessageContentConnector.cs index 6a8395ef22e..6ce242eb1ab 100644 --- a/dotnet/src/AutoGen.SemanticKernel/Middleware/SemanticKernelChatMessageContentConnector.cs +++ b/dotnet/src/AutoGen.SemanticKernel/Middleware/SemanticKernelChatMessageContentConnector.cs @@ -133,7 +133,9 @@ private IEnumerable ProcessMessageForSelf(IMessage message) { TextMessage textMessage => ProcessMessageForSelf(textMessage), MultiModalMessage multiModalMessage => ProcessMessageForSelf(multiModalMessage), +#pragma warning disable CS0618 // deprecated Message m => ProcessMessageForSelf(m), +#pragma warning restore CS0618 // deprecated _ => throw new System.NotImplementedException(), }; } @@ -145,7 +147,9 @@ private IEnumerable ProcessMessageForOthers(IMessage message TextMessage textMessage => ProcessMessageForOthers(textMessage), MultiModalMessage multiModalMessage => ProcessMessageForOthers(multiModalMessage), ImageMessage imageMessage => ProcessMessageForOthers(imageMessage), +#pragma warning disable CS0618 // deprecated Message m => ProcessMessageForOthers(m), +#pragma warning restore CS0618 // deprecated _ => throw new InvalidOperationException("unsupported message type, only support TextMessage, ImageMessage, MultiModalMessage and Message."), }; } @@ -208,7 +212,7 @@ private IEnumerable ProcessMessageForOthers(MultiModalMessag return [new ChatMessageContent(AuthorRole.User, collections)]; } - + [Obsolete("This method is deprecated, please use the specific method instead.")] private IEnumerable ProcessMessageForSelf(Message message) { if (message.Role == Role.System) @@ -229,6 +233,7 @@ private IEnumerable ProcessMessageForSelf(Message message) } } + [Obsolete("This method is deprecated, please use the specific method instead.")] private IEnumerable ProcessMessageForOthers(Message message) { if (message.Role == Role.System) diff --git a/dotnet/src/AutoGen.SourceGenerator/AutoGen.SourceGenerator.csproj b/dotnet/src/AutoGen.SourceGenerator/AutoGen.SourceGenerator.csproj index 4558160722d..37f344ed11e 100644 --- a/dotnet/src/AutoGen.SourceGenerator/AutoGen.SourceGenerator.csproj +++ b/dotnet/src/AutoGen.SourceGenerator/AutoGen.SourceGenerator.csproj @@ -14,7 +14,7 @@ - + AutoGen.SourceGenerator @@ -50,6 +50,10 @@ + + + + True diff --git a/dotnet/src/AutoGen.SourceGenerator/FunctionCallGenerator.cs b/dotnet/src/AutoGen.SourceGenerator/FunctionCallGenerator.cs index 50bdc03f0af..cd01416182b 100644 --- a/dotnet/src/AutoGen.SourceGenerator/FunctionCallGenerator.cs +++ b/dotnet/src/AutoGen.SourceGenerator/FunctionCallGenerator.cs @@ -144,7 +144,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) private class PartialClassOutput { - public PartialClassOutput(string fullClassName, ClassDeclarationSyntax classDeclarationSyntax, IEnumerable functionContracts) + public PartialClassOutput(string fullClassName, ClassDeclarationSyntax classDeclarationSyntax, IEnumerable functionContracts) { FullClassName = fullClassName; ClassDeclarationSyntax = classDeclarationSyntax; @@ -155,10 +155,10 @@ public PartialClassOutput(string fullClassName, ClassDeclarationSyntax classDecl public ClassDeclarationSyntax ClassDeclarationSyntax { get; } - public IEnumerable FunctionContracts { get; } + public IEnumerable FunctionContracts { get; } } - private FunctionContract CreateFunctionContract(MethodDeclarationSyntax method, string? className, string? namespaceName) + private SourceGeneratorFunctionContract CreateFunctionContract(MethodDeclarationSyntax method, string? className, string? namespaceName) { // get function_call attribute var functionCallAttribute = method.AttributeLists.SelectMany(attributeList => attributeList.Attributes) @@ -208,7 +208,7 @@ private FunctionContract CreateFunctionContract(MethodDeclarationSyntax method, description = System.Text.RegularExpressions.Regex.Replace(description, @"[^\S\r\n]+\/[\/]+\s*", string.Empty); } var jsonItemType = parameter.Type!.ToString().EndsWith("[]") ? parameter.Type!.ToString().Substring(0, parameter.Type!.ToString().Length - 2) : null; - return new ParameterContract + return new SourceGeneratorParameterContract { Name = parameter.Identifier.ToString(), JsonType = parameter.Type!.ToString() switch @@ -234,7 +234,7 @@ private FunctionContract CreateFunctionContract(MethodDeclarationSyntax method, }; }); - return new FunctionContract + return new SourceGeneratorFunctionContract { ClassName = className, Namespace = namespaceName, diff --git a/dotnet/src/AutoGen.SourceGenerator/FunctionExtension.cs b/dotnet/src/AutoGen.SourceGenerator/FunctionExtension.cs index a56e4cb54f4..cfb77d26a2b 100644 --- a/dotnet/src/AutoGen.SourceGenerator/FunctionExtension.cs +++ b/dotnet/src/AutoGen.SourceGenerator/FunctionExtension.cs @@ -5,27 +5,27 @@ internal static class FunctionExtension { - public static string GetFunctionName(this FunctionContract function) + public static string GetFunctionName(this SourceGeneratorFunctionContract function) { return function.Name ?? string.Empty; } - public static string GetFunctionSchemaClassName(this FunctionContract function) + public static string GetFunctionSchemaClassName(this SourceGeneratorFunctionContract function) { return $"{function.GetFunctionName()}Schema"; } - public static string GetFunctionDefinitionName(this FunctionContract function) + public static string GetFunctionDefinitionName(this SourceGeneratorFunctionContract function) { return $"{function.GetFunctionName()}Function"; } - public static string GetFunctionWrapperName(this FunctionContract function) + public static string GetFunctionWrapperName(this SourceGeneratorFunctionContract function) { return $"{function.GetFunctionName()}Wrapper"; } - public static string GetFunctionContractName(this FunctionContract function) + public static string GetFunctionContractName(this SourceGeneratorFunctionContract function) { return $"{function.GetFunctionName()}FunctionContract"; } diff --git a/dotnet/src/AutoGen.SourceGenerator/FunctionContract.cs b/dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs similarity index 81% rename from dotnet/src/AutoGen.SourceGenerator/FunctionContract.cs rename to dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs index 2f26352173d..24e42affa3b 100644 --- a/dotnet/src/AutoGen.SourceGenerator/FunctionContract.cs +++ b/dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs @@ -3,7 +3,7 @@ namespace AutoGen.SourceGenerator { - internal class FunctionContract + internal class SourceGeneratorFunctionContract { public string? Namespace { get; set; } @@ -15,12 +15,12 @@ internal class FunctionContract public string? ReturnDescription { get; set; } - public ParameterContract[]? Parameters { get; set; } + public SourceGeneratorParameterContract[]? Parameters { get; set; } public string? ReturnType { get; set; } } - internal class ParameterContract + internal class SourceGeneratorParameterContract { public string? Name { get; set; } diff --git a/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.cs b/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.cs index 1d455bd3041..e56db112eb7 100644 --- a/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.cs +++ b/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.cs @@ -31,7 +31,6 @@ public virtual string TransformText() // This code was generated by a tool. // //---------------------- -using Azure.AI.OpenAI; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -152,7 +151,8 @@ public virtual string TransformText() } this.Write(" },\r\n"); } - this.Write(" };\r\n }\r\n\r\n public Azure.AI.OpenAI.FunctionDefinition "); + this.Write(" };\r\n }\r\n\r\n public global::Azure.AI.OpenAI.FunctionDefin" + + "ition "); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.GetFunctionDefinitionName())); this.Write("\r\n {\r\n get => this."); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.GetFunctionContractName())); @@ -168,7 +168,7 @@ public virtual string TransformText() public string NameSpace {get; set;} public string ClassName {get; set;} -public IEnumerable FunctionContracts {get; set;} +public IEnumerable FunctionContracts {get; set;} public bool IsStatic {get; set;} = false; } diff --git a/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.tt b/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.tt index baa2a680fe2..526dfe400ce 100644 --- a/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.tt +++ b/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.tt @@ -8,7 +8,6 @@ // This code was generated by a tool. // //---------------------- -using Azure.AI.OpenAI; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -98,7 +97,7 @@ namespace <#=NameSpace#> }; } - public Azure.AI.OpenAI.FunctionDefinition <#=functionContract.GetFunctionDefinitionName()#> + public global::Azure.AI.OpenAI.FunctionDefinition <#=functionContract.GetFunctionDefinitionName()#> { get => this.<#=functionContract.GetFunctionContractName()#>.ToOpenAIFunctionDefinition(); } diff --git a/dotnet/test/AutoGen.DotnetInteractive.Tests/AutoGen.DotnetInteractive.Tests.csproj b/dotnet/test/AutoGen.DotnetInteractive.Tests/AutoGen.DotnetInteractive.Tests.csproj new file mode 100644 index 00000000000..cf2c24eaf78 --- /dev/null +++ b/dotnet/test/AutoGen.DotnetInteractive.Tests/AutoGen.DotnetInteractive.Tests.csproj @@ -0,0 +1,24 @@ + + + + $(TestTargetFramework) + enable + false + True + + + + + + + + + + + + + + + + + diff --git a/dotnet/test/AutoGen.DotnetInteractive.Tests/DotnetInteractiveServiceTest.cs b/dotnet/test/AutoGen.DotnetInteractive.Tests/DotnetInteractiveServiceTest.cs new file mode 100644 index 00000000000..0e36053c45e --- /dev/null +++ b/dotnet/test/AutoGen.DotnetInteractive.Tests/DotnetInteractiveServiceTest.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// DotnetInteractiveServiceTest.cs + +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; + +namespace AutoGen.DotnetInteractive.Tests; + +public class DotnetInteractiveServiceTest : IDisposable +{ + private ITestOutputHelper _output; + private InteractiveService _interactiveService; + private string _workingDir; + + public DotnetInteractiveServiceTest(ITestOutputHelper output) + { + _output = output; + _workingDir = Path.Combine(Path.GetTempPath(), "test", Path.GetRandomFileName()); + if (!Directory.Exists(_workingDir)) + { + Directory.CreateDirectory(_workingDir); + } + + _interactiveService = new InteractiveService(_workingDir); + _interactiveService.StartAsync(_workingDir, default).Wait(); + } + + public void Dispose() + { + _interactiveService.Dispose(); + } + + [Fact] + public async Task ItRunCSharpCodeSnippetTestsAsync() + { + var cts = new CancellationTokenSource(); + var isRunning = await _interactiveService.StartAsync(_workingDir, cts.Token); + + isRunning.Should().BeTrue(); + + _interactiveService.IsRunning().Should().BeTrue(); + + // test code snippet + var hello_world = @" +Console.WriteLine(""hello world""); +"; + + await this.TestCSharpCodeSnippet(_interactiveService, hello_world, "hello world"); + await this.TestCSharpCodeSnippet( + _interactiveService, + code: @" +Console.WriteLine(""hello world"" +", + expectedOutput: "Error: (2,32): error CS1026: ) expected"); + + await this.TestCSharpCodeSnippet( + service: _interactiveService, + code: "throw new Exception();", + expectedOutput: "Error: System.Exception: Exception of type 'System.Exception' was thrown"); + } + + [Fact] + public async Task ItRunPowershellScriptTestsAsync() + { + // test power shell + var ps = @"Write-Output ""hello world"""; + await this.TestPowershellCodeSnippet(_interactiveService, ps, "hello world"); + } + + private async Task TestPowershellCodeSnippet(InteractiveService service, string code, string expectedOutput) + { + var result = await service.SubmitPowershellCodeAsync(code, CancellationToken.None); + result.Should().StartWith(expectedOutput); + } + + private async Task TestCSharpCodeSnippet(InteractiveService service, string code, string expectedOutput) + { + var result = await service.SubmitCSharpCodeAsync(code, CancellationToken.None); + result.Should().StartWith(expectedOutput); + } +} diff --git a/dotnet/test/AutoGen.Mistral.Tests/MistralClientAgentTests.cs b/dotnet/test/AutoGen.Mistral.Tests/MistralClientAgentTests.cs index 2b6839dd0ef..3aa61a7a71d 100644 --- a/dotnet/test/AutoGen.Mistral.Tests/MistralClientAgentTests.cs +++ b/dotnet/test/AutoGen.Mistral.Tests/MistralClientAgentTests.cs @@ -87,11 +87,15 @@ public async Task MistralAgentFunctionCallMessageTest() } """; var functionCallResult = await this.GetWeatherWrapper(weatherFunctionArgumets); - + var toolCall = new ToolCall(this.GetWeatherFunctionContract.Name!, weatherFunctionArgumets) + { + ToolCallId = "012345678", // Mistral AI requires the tool call id to be a length of 9 + Result = functionCallResult, + }; IMessage[] chatHistory = [ new TextMessage(Role.User, "what's the weather in Seattle?"), - new ToolCallMessage(this.GetWeatherFunctionContract.Name!, weatherFunctionArgumets, from: agent.Name), - new ToolCallResultMessage(functionCallResult, this.GetWeatherFunctionContract.Name!, weatherFunctionArgumets), + new ToolCallMessage([toolCall], from: agent.Name), + new ToolCallResultMessage([toolCall], weatherFunctionArgumets), ]; var reply = await agent.SendAsync(chatHistory: chatHistory); @@ -152,7 +156,7 @@ public async Task MistralAgentFunctionCallMiddlewareMessageTest() var question = new TextMessage(Role.User, "what's the weather in Seattle?"); var reply = await functionCallAgent.SendAsync(question); - reply.Should().BeOfType>(); + reply.Should().BeOfType(); // resend the reply to the same agent so it can generate the final response // because the reply's from is the agent's name diff --git a/dotnet/test/AutoGen.Ollama.Tests/AutoGen.Ollama.Tests.csproj b/dotnet/test/AutoGen.Ollama.Tests/AutoGen.Ollama.Tests.csproj new file mode 100644 index 00000000000..27f80716f1c --- /dev/null +++ b/dotnet/test/AutoGen.Ollama.Tests/AutoGen.Ollama.Tests.csproj @@ -0,0 +1,33 @@ + + + + $(TestTargetFramework) + enable + false + True + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/dotnet/test/AutoGen.Ollama.Tests/OllamaAgentTests.cs b/dotnet/test/AutoGen.Ollama.Tests/OllamaAgentTests.cs new file mode 100644 index 00000000000..c1fb466f0b0 --- /dev/null +++ b/dotnet/test/AutoGen.Ollama.Tests/OllamaAgentTests.cs @@ -0,0 +1,224 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// OllamaAgentTests.cs + +using System.Text.Json; +using AutoGen.Core; +using AutoGen.Ollama.Extension; +using AutoGen.Tests; +using FluentAssertions; + +namespace AutoGen.Ollama.Tests; + +public class OllamaAgentTests +{ + [ApiKeyFact("OLLAMA_HOST", "OLLAMA_MODEL_NAME")] + public async Task GenerateReplyAsync_ReturnsValidMessage_WhenCalled() + { + string host = Environment.GetEnvironmentVariable("OLLAMA_HOST") + ?? throw new InvalidOperationException("OLLAMA_HOST is not set."); + string modelName = Environment.GetEnvironmentVariable("OLLAMA_MODEL_NAME") + ?? throw new InvalidOperationException("OLLAMA_MODEL_NAME is not set."); + OllamaAgent ollamaAgent = BuildOllamaAgent(host, modelName); + + var message = new Message("user", "hey how are you"); + var messages = new IMessage[] { MessageEnvelope.Create(message, from: modelName) }; + IMessage result = await ollamaAgent.GenerateReplyAsync(messages); + + result.Should().NotBeNull(); + result.Should().BeOfType>(); + result.From.Should().Be(ollamaAgent.Name); + } + + [ApiKeyFact("OLLAMA_HOST", "OLLAMA_MODEL_NAME")] + public async Task GenerateReplyAsync_ReturnsValidJsonMessageContent_WhenCalled() + { + string host = Environment.GetEnvironmentVariable("OLLAMA_HOST") + ?? throw new InvalidOperationException("OLLAMA_HOST is not set."); + string modelName = Environment.GetEnvironmentVariable("OLLAMA_MODEL_NAME") + ?? throw new InvalidOperationException("OLLAMA_MODEL_NAME is not set."); + OllamaAgent ollamaAgent = BuildOllamaAgent(host, modelName); + + var message = new Message("user", "What color is the sky at different times of the day? Respond using JSON"); + var messages = new IMessage[] { MessageEnvelope.Create(message, from: modelName) }; + IMessage result = await ollamaAgent.GenerateReplyAsync(messages, new OllamaReplyOptions + { + Format = FormatType.Json + }); + + result.Should().NotBeNull(); + result.Should().BeOfType>(); + result.From.Should().Be(ollamaAgent.Name); + + string jsonContent = ((MessageEnvelope)result).Content.Message!.Value; + bool isValidJson = IsValidJsonMessage(jsonContent); + isValidJson.Should().BeTrue(); + } + + [ApiKeyFact("OLLAMA_HOST", "OLLAMA_MODEL_NAME")] + public async Task GenerateStreamingReplyAsync_ReturnsValidMessages_WhenCalled() + { + string host = Environment.GetEnvironmentVariable("OLLAMA_HOST") + ?? throw new InvalidOperationException("OLLAMA_HOST is not set."); + string modelName = Environment.GetEnvironmentVariable("OLLAMA_MODEL_NAME") + ?? throw new InvalidOperationException("OLLAMA_MODEL_NAME is not set."); + OllamaAgent ollamaAgent = BuildOllamaAgent(host, modelName); + + var msg = new Message("user", "hey how are you"); + var messages = new IMessage[] { MessageEnvelope.Create(msg, from: modelName) }; + IStreamingMessage? finalReply = default; + await foreach (IStreamingMessage message in ollamaAgent.GenerateStreamingReplyAsync(messages)) + { + message.Should().NotBeNull(); + message.From.Should().Be(ollamaAgent.Name); + var streamingMessage = (IMessage)message; + if (streamingMessage.Content.Done) + { + finalReply = message; + break; + } + else + { + streamingMessage.Content.Message.Should().NotBeNull(); + streamingMessage.Content.Done.Should().BeFalse(); + } + } + + finalReply.Should().BeOfType>(); + var update = ((MessageEnvelope)finalReply!).Content; + update.Done.Should().BeTrue(); + update.TotalDuration.Should().BeGreaterThan(0); + } + + [ApiKeyFact("OLLAMA_HOST")] + public async Task ItReturnValidMessageUsingLLavaAsync() + { + var host = Environment.GetEnvironmentVariable("OLLAMA_HOST") + ?? throw new InvalidOperationException("OLLAMA_HOST is not set."); + var modelName = "llava:latest"; + var ollamaAgent = BuildOllamaAgent(host, modelName); + var imagePath = Path.Combine("images", "image.png"); + var base64Image = Convert.ToBase64String(File.ReadAllBytes(imagePath)); + var message = new Message() + { + Role = "user", + Value = "What's the color of the background in this image", + Images = [base64Image], + }; + + var messages = new IMessage[] { MessageEnvelope.Create(message, from: modelName) }; + var reply = await ollamaAgent.GenerateReplyAsync(messages); + + reply.Should().BeOfType>(); + var chatResponse = ((MessageEnvelope)reply).Content; + chatResponse.Message.Should().NotBeNull(); + } + + [ApiKeyFact("OLLAMA_HOST")] + public async Task ItCanProcessMultiModalMessageUsingLLavaAsync() + { + var host = Environment.GetEnvironmentVariable("OLLAMA_HOST") + ?? throw new InvalidOperationException("OLLAMA_HOST is not set."); + var modelName = "llava:latest"; + var ollamaAgent = BuildOllamaAgent(host, modelName) + .RegisterMessageConnector(); + var image = Path.Combine("images", "image.png"); + var binaryData = BinaryData.FromBytes(File.ReadAllBytes(image), "image/png"); + var imageMessage = new ImageMessage(Role.User, binaryData); + var textMessage = new TextMessage(Role.User, "What's in this image?"); + var multiModalMessage = new MultiModalMessage(Role.User, [textMessage, imageMessage]); + + var reply = await ollamaAgent.SendAsync(multiModalMessage); + reply.Should().BeOfType(); + reply.GetRole().Should().Be(Role.Assistant); + reply.GetContent().Should().NotBeNullOrEmpty(); + reply.From.Should().Be(ollamaAgent.Name); + } + + [ApiKeyFact("OLLAMA_HOST")] + public async Task ItCanProcessImageMessageUsingLLavaAsync() + { + var host = Environment.GetEnvironmentVariable("OLLAMA_HOST") + ?? throw new InvalidOperationException("OLLAMA_HOST is not set."); + var modelName = "llava:latest"; + var ollamaAgent = BuildOllamaAgent(host, modelName) + .RegisterMessageConnector(); + var image = Path.Combine("images", "image.png"); + var binaryData = BinaryData.FromBytes(File.ReadAllBytes(image), "image/png"); + var imageMessage = new ImageMessage(Role.User, binaryData); + + var reply = await ollamaAgent.SendAsync(imageMessage); + reply.Should().BeOfType(); + reply.GetRole().Should().Be(Role.Assistant); + reply.GetContent().Should().NotBeNullOrEmpty(); + reply.From.Should().Be(ollamaAgent.Name); + } + + [ApiKeyFact("OLLAMA_HOST")] + public async Task ItReturnValidStreamingMessageUsingLLavaAsync() + { + var host = Environment.GetEnvironmentVariable("OLLAMA_HOST") + ?? throw new InvalidOperationException("OLLAMA_HOST is not set."); + var modelName = "llava:latest"; + var ollamaAgent = BuildOllamaAgent(host, modelName); + var squareImagePath = Path.Combine("images", "square.png"); + var base64Image = Convert.ToBase64String(File.ReadAllBytes(squareImagePath)); + var imageMessage = new Message() + { + Role = "user", + Value = "What's in this image?", + Images = [base64Image], + }; + + var messages = new IMessage[] { MessageEnvelope.Create(imageMessage, from: modelName) }; + + IStreamingMessage? finalReply = default; + await foreach (IStreamingMessage message in ollamaAgent.GenerateStreamingReplyAsync(messages)) + { + message.Should().NotBeNull(); + message.From.Should().Be(ollamaAgent.Name); + var streamingMessage = (IMessage)message; + if (streamingMessage.Content.Done) + { + finalReply = message; + break; + } + else + { + streamingMessage.Content.Message.Should().NotBeNull(); + streamingMessage.Content.Done.Should().BeFalse(); + } + } + + finalReply.Should().BeOfType>(); + var update = ((MessageEnvelope)finalReply!).Content; + update.Done.Should().BeTrue(); + update.TotalDuration.Should().BeGreaterThan(0); + } + + private static bool IsValidJsonMessage(string input) + { + try + { + JsonDocument.Parse(input); + return true; + } + catch (JsonException) + { + return false; + } + catch (Exception ex) + { + Console.WriteLine("An unexpected exception occurred: " + ex.Message); + return false; + } + } + + private static OllamaAgent BuildOllamaAgent(string host, string modelName) + { + var httpClient = new HttpClient + { + BaseAddress = new Uri(host) + }; + return new OllamaAgent(httpClient, "TestAgent", modelName); + } +} diff --git a/dotnet/test/AutoGen.Ollama.Tests/OllamaMessageTests.cs b/dotnet/test/AutoGen.Ollama.Tests/OllamaMessageTests.cs new file mode 100644 index 00000000000..b19291e9767 --- /dev/null +++ b/dotnet/test/AutoGen.Ollama.Tests/OllamaMessageTests.cs @@ -0,0 +1,176 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// OllamaMessageTests.cs + +using AutoGen.Core; +using AutoGen.Tests; +using FluentAssertions; +using Xunit; +namespace AutoGen.Ollama.Tests; + +public class OllamaMessageTests +{ + [Fact] + public async Task ItProcessUserTextMessageAsync() + { + var messageConnector = new OllamaMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, ct) => + { + msgs.Count().Should().Be(1); + var innerMessage = msgs.First(); + innerMessage.Should().BeOfType>(); + var message = (IMessage)innerMessage; + message.Content.Value.Should().Be("Hello"); + message.Content.Images.Should().BeNullOrEmpty(); + message.Content.Role.Should().Be("user"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(messageConnector); + + // when from is null and role is user + await agent.SendAsync("Hello"); + + // when from is user and role is user + var userMessage = new TextMessage(Role.User, "Hello", from: "user"); + await agent.SendAsync(userMessage); + + // when from is user but role is assistant + userMessage = new TextMessage(Role.Assistant, "Hello", from: "user"); + await agent.SendAsync(userMessage); + } + + [Fact] + public async Task ItProcessStreamingTextMessageAsync() + { + var messageConnector = new OllamaMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterStreamingMiddleware(messageConnector); + + var messageChunks = Enumerable.Range(0, 10) + .Select(i => new ChatResponseUpdate() + { + Message = new Message() + { + Value = i.ToString(), + Role = "assistant", + } + }) + .Select(m => MessageEnvelope.Create(m)); + + IStreamingMessage? finalReply = null; + await foreach (var reply in agent.GenerateStreamingReplyAsync(messageChunks)) + { + reply.Should().BeAssignableTo(); + finalReply = reply; + } + + finalReply.Should().BeOfType(); + var textMessage = (TextMessage)finalReply!; + textMessage.GetContent().Should().Be("0123456789"); + } + + [Fact] + public async Task ItProcessAssistantTextMessageAsync() + { + var messageConnector = new OllamaMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, ct) => + { + msgs.Count().Should().Be(1); + var innerMessage = msgs.First(); + innerMessage.Should().BeOfType>(); + var message = (IMessage)innerMessage; + message.Content.Value.Should().Be("Hello"); + message.Content.Images.Should().BeNullOrEmpty(); + message.Content.Role.Should().Be("assistant"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(messageConnector); + + // when from is null and role is assistant + var assistantMessage = new TextMessage(Role.Assistant, "Hello"); + await agent.SendAsync(assistantMessage); + + // when from is assistant and role is assistant + assistantMessage = new TextMessage(Role.Assistant, "Hello", from: "assistant"); + await agent.SendAsync(assistantMessage); + + // when from is assistant but role is user + assistantMessage = new TextMessage(Role.User, "Hello", from: "assistant"); + await agent.SendAsync(assistantMessage); + } + + [Fact] + public async Task ItProcessSystemTextMessageAsync() + { + var messageConnector = new OllamaMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, ct) => + { + msgs.Count().Should().Be(1); + var innerMessage = msgs.First(); + innerMessage.Should().BeOfType>(); + var message = (IMessage)innerMessage; + message.Content.Value.Should().Be("Hello"); + message.Content.Images.Should().BeNullOrEmpty(); + message.Content.Role.Should().Be("system"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(messageConnector); + + // when role is system + var systemMessage = new TextMessage(Role.System, "Hello"); + await agent.SendAsync(systemMessage); + } + + [Fact] + public async Task ItProcessImageMessageAsync() + { + var messageConnector = new OllamaMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, ct) => + { + msgs.Count().Should().Be(1); + var innerMessage = msgs.First(); + innerMessage.Should().BeOfType>(); + var message = (IMessage)innerMessage; + message.Content.Images!.Count.Should().Be(1); + message.Content.Role.Should().Be("user"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(messageConnector); + + var square = Path.Combine("images", "square.png"); + BinaryData imageBinaryData = BinaryData.FromBytes(File.ReadAllBytes(square), "image/png"); + var imageMessage = new ImageMessage(Role.User, imageBinaryData); + await agent.SendAsync(imageMessage); + } + + [Fact] + public async Task ItProcessMultiModalMessageAsync() + { + var messageConnector = new OllamaMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, ct) => + { + msgs.Count().Should().Be(1); + var message = msgs.First(); + message.Should().BeOfType>(); + + var multiModalMessage = (IMessage)message; + multiModalMessage.Content.Images!.Count.Should().Be(1); + multiModalMessage.Content.Value.Should().Be("Hello"); + + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(messageConnector); + + var square = Path.Combine("images", "square.png"); + BinaryData imageBinaryData = BinaryData.FromBytes(File.ReadAllBytes(square), "image/png"); + var imageMessage = new ImageMessage(Role.User, imageBinaryData); + var textMessage = new TextMessage(Role.User, "Hello"); + var multiModalMessage = new MultiModalMessage(Role.User, [textMessage, imageMessage]); + + await agent.SendAsync(multiModalMessage); + } +} diff --git a/dotnet/test/AutoGen.Ollama.Tests/OllamaTextEmbeddingServiceTests.cs b/dotnet/test/AutoGen.Ollama.Tests/OllamaTextEmbeddingServiceTests.cs new file mode 100644 index 00000000000..06522bdd823 --- /dev/null +++ b/dotnet/test/AutoGen.Ollama.Tests/OllamaTextEmbeddingServiceTests.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// OllamaTextEmbeddingServiceTests.cs + +using AutoGen.Tests; +using FluentAssertions; + +namespace AutoGen.Ollama.Tests; + +public class OllamaTextEmbeddingServiceTests +{ + [ApiKeyFact("OLLAMA_HOST", "OLLAMA_EMBEDDING_MODEL_NAME")] + public async Task GenerateAsync_ReturnsEmbeddings_WhenApiResponseIsSuccessful() + { + string host = Environment.GetEnvironmentVariable("OLLAMA_HOST") + ?? throw new InvalidOperationException("OLLAMA_HOST is not set."); + string embeddingModelName = Environment.GetEnvironmentVariable("OLLAMA_EMBEDDING_MODEL_NAME") + ?? throw new InvalidOperationException("OLLAMA_EMBEDDING_MODEL_NAME is not set."); + var httpClient = new HttpClient + { + BaseAddress = new Uri(host) + }; + var request = new TextEmbeddingsRequest { Model = embeddingModelName, Prompt = "Llamas are members of the camelid family", }; + var service = new OllamaTextEmbeddingService(httpClient); + TextEmbeddingsResponse response = await service.GenerateAsync(request); + response.Should().NotBeNull(); + } +} diff --git a/dotnet/test/AutoGen.Ollama.Tests/images/image.png b/dotnet/test/AutoGen.Ollama.Tests/images/image.png new file mode 100644 index 00000000000..ca276f81f5b --- /dev/null +++ b/dotnet/test/AutoGen.Ollama.Tests/images/image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:300b7c9d6ba0c23a3e52fbd2e268141ddcca0434a9fb9dcf7e58e7e903d36dcf +size 2126185 diff --git a/dotnet/test/AutoGen.Ollama.Tests/images/square.png b/dotnet/test/AutoGen.Ollama.Tests/images/square.png new file mode 100644 index 00000000000..afb4f4cd4df --- /dev/null +++ b/dotnet/test/AutoGen.Ollama.Tests/images/square.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8323d0b8eceb752e14c29543b2e28bb2fc648ed9719095c31b7708867a4dc918 +size 491 diff --git a/dotnet/test/AutoGen.Tests/ApprovalTests/OpenAIMessageTests.BasicMessageTest.approved.txt b/dotnet/test/AutoGen.OpenAI.Tests/ApprovalTests/OpenAIMessageTests.BasicMessageTest.approved.txt similarity index 73% rename from dotnet/test/AutoGen.Tests/ApprovalTests/OpenAIMessageTests.BasicMessageTest.approved.txt rename to dotnet/test/AutoGen.OpenAI.Tests/ApprovalTests/OpenAIMessageTests.BasicMessageTest.approved.txt index 2cb58f4d88c..e8e9af84dbd 100644 --- a/dotnet/test/AutoGen.Tests/ApprovalTests/OpenAIMessageTests.BasicMessageTest.approved.txt +++ b/dotnet/test/AutoGen.OpenAI.Tests/ApprovalTests/OpenAIMessageTests.BasicMessageTest.approved.txt @@ -3,6 +3,7 @@ "OriginalMessage": "TextMessage(system, You are a helpful AI assistant, )", "ConvertedMessages": [ { + "Name": null, "Role": "system", "Content": "You are a helpful AI assistant" } @@ -14,6 +15,7 @@ { "Role": "user", "Content": "Hello", + "Name": "user", "MultiModaItem": null } ] @@ -24,71 +26,20 @@ { "Role": "assistant", "Content": "How can I help you?", + "Name": "assistant", "TooCall": [], "FunctionCallName": null, "FunctionCallArguments": null } ] }, - { - "OriginalMessage": "Message(system, You are a helpful AI assistant, , , )", - "ConvertedMessages": [ - { - "Role": "system", - "Content": "You are a helpful AI assistant" - } - ] - }, - { - "OriginalMessage": "Message(user, Hello, user, , )", - "ConvertedMessages": [ - { - "Role": "user", - "Content": "Hello", - "MultiModaItem": null - } - ] - }, - { - "OriginalMessage": "Message(assistant, How can I help you?, assistant, , )", - "ConvertedMessages": [ - { - "Role": "assistant", - "Content": "How can I help you?", - "TooCall": [], - "FunctionCallName": null, - "FunctionCallArguments": null - } - ] - }, - { - "OriginalMessage": "Message(function, result, user, , )", - "ConvertedMessages": [ - { - "Role": "user", - "Content": "result", - "MultiModaItem": null - } - ] - }, - { - "OriginalMessage": "Message(assistant, , assistant, functionName, functionArguments)", - "ConvertedMessages": [ - { - "Role": "assistant", - "Content": null, - "TooCall": [], - "FunctionCallName": "functionName", - "FunctionCallArguments": "functionArguments" - } - ] - }, { "OriginalMessage": "ImageMessage(user, https://example.com/image.png, user)", "ConvertedMessages": [ { "Role": "user", "Content": null, + "Name": "user", "MultiModaItem": [ { "Type": "Image", @@ -107,6 +58,7 @@ { "Role": "user", "Content": null, + "Name": "user", "MultiModaItem": [ { "Type": "Text", @@ -129,6 +81,7 @@ { "Role": "assistant", "Content": "", + "Name": "assistant", "TooCall": [ { "Type": "Function", @@ -158,12 +111,12 @@ { "Role": "tool", "Content": "test", - "ToolCallId": "result" + "ToolCallId": "result_0" }, { "Role": "tool", "Content": "test", - "ToolCallId": "result" + "ToolCallId": "result_1" } ] }, @@ -173,18 +126,19 @@ { "Role": "assistant", "Content": "", + "Name": "assistant", "TooCall": [ { "Type": "Function", "Name": "test", "Arguments": "test", - "Id": "test" + "Id": "test_0" }, { "Type": "Function", "Name": "test", "Arguments": "test", - "Id": "test" + "Id": "test_1" } ], "FunctionCallName": null, @@ -198,6 +152,7 @@ { "Role": "assistant", "Content": "", + "Name": "assistant", "TooCall": [ { "Type": "Function", diff --git a/dotnet/test/AutoGen.OpenAI.Tests/AutoGen.OpenAI.Tests.csproj b/dotnet/test/AutoGen.OpenAI.Tests/AutoGen.OpenAI.Tests.csproj new file mode 100644 index 00000000000..044975354b8 --- /dev/null +++ b/dotnet/test/AutoGen.OpenAI.Tests/AutoGen.OpenAI.Tests.csproj @@ -0,0 +1,32 @@ + + + + $(TestTargetFramework) + false + True + + + + + + + + + + + + + + + + + + + + $([System.String]::Copy('%(FileName)').Split('.')[0]) + $(ProjectExt.Replace('proj', '')) + %(ParentFile)%(ParentExtension) + + + + diff --git a/dotnet/test/AutoGen.OpenAI.Tests/GlobalUsing.cs b/dotnet/test/AutoGen.OpenAI.Tests/GlobalUsing.cs new file mode 100644 index 00000000000..d66bf001ed5 --- /dev/null +++ b/dotnet/test/AutoGen.OpenAI.Tests/GlobalUsing.cs @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// GlobalUsing.cs + +global using AutoGen.Core; diff --git a/dotnet/test/AutoGen.OpenAI.Tests/MathClassTest.cs b/dotnet/test/AutoGen.OpenAI.Tests/MathClassTest.cs new file mode 100644 index 00000000000..87fc0767020 --- /dev/null +++ b/dotnet/test/AutoGen.OpenAI.Tests/MathClassTest.cs @@ -0,0 +1,223 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// MathClassTest.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using AutoGen.OpenAI.Extension; +using AutoGen.Tests; +using Azure.AI.OpenAI; +using FluentAssertions; +using Xunit.Abstractions; + +namespace AutoGen.OpenAI.Tests +{ + public partial class MathClassTest + { + private readonly ITestOutputHelper _output; + + // as of 2024-05-20, aoai return 500 error when round > 1 + // I'm pretty sure that round > 5 was supported before + // So this is probably some wield regression on aoai side + // I'll keep this test case here for now, plus setting round to 1 + // so the test can still pass. + // In the future, we should rewind this test case to round > 1 (previously was 5) + private int round = 1; + public MathClassTest(ITestOutputHelper output) + { + _output = output; + } + + private Task Print(IEnumerable messages, GenerateReplyOptions? option, IAgent agent, CancellationToken ct) + { + try + { + var reply = agent.GenerateReplyAsync(messages, option, ct).Result; + + _output.WriteLine(reply.FormatMessage()); + return Task.FromResult(reply); + } + catch (Exception) + { + _output.WriteLine("Request failed"); + _output.WriteLine($"agent name: {agent.Name}"); + foreach (var message in messages) + { + _output.WriteLine(message.FormatMessage()); + } + + throw; + } + + } + + [FunctionAttribute] + public async Task CreateMathQuestion(string question, int question_index) + { + return $@"[MATH_QUESTION] +Question {question_index}: +{question} + +Student, please answer"; + } + + [FunctionAttribute] + public async Task AnswerQuestion(string answer) + { + return $@"[MATH_ANSWER] +The answer is {answer} +teacher please check answer"; + } + + [FunctionAttribute] + public async Task AnswerIsCorrect(string message) + { + return $@"[ANSWER_IS_CORRECT] +{message} +please update progress"; + } + + [FunctionAttribute] + public async Task UpdateProgress(int correctAnswerCount) + { + if (correctAnswerCount >= this.round) + { + return $@"[UPDATE_PROGRESS] +{GroupChatExtension.TERMINATE}"; + } + else + { + return $@"[UPDATE_PROGRESS] +the number of resolved question is {correctAnswerCount} +teacher, please create the next math question"; + } + } + + + [ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT")] + public async Task OpenAIAgentMathChatTestAsync() + { + var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new ArgumentException("AZURE_OPENAI_API_KEY is not set"); + var endPoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new ArgumentException("AZURE_OPENAI_ENDPOINT is not set"); + + var openaiClient = new OpenAIClient(new Uri(endPoint), new Azure.AzureKeyCredential(key)); + var model = "gpt-35-turbo-16k"; + var teacher = await CreateTeacherAgentAsync(openaiClient, model); + var student = await CreateStudentAssistantAgentAsync(openaiClient, model); + + var adminFunctionMiddleware = new FunctionCallMiddleware( + functions: [this.UpdateProgressFunctionContract], + functionMap: new Dictionary>> + { + { this.UpdateProgressFunction.Name!, this.UpdateProgressWrapper }, + }); + var admin = new OpenAIChatAgent( + openAIClient: openaiClient, + modelName: model, + name: "Admin", + systemMessage: $@"You are admin. You update progress after each question is answered.") + .RegisterMessageConnector() + .RegisterStreamingMiddleware(adminFunctionMiddleware) + .RegisterMiddleware(Print); + + var groupAdmin = new OpenAIChatAgent( + openAIClient: openaiClient, + modelName: model, + name: "GroupAdmin", + systemMessage: "You are group admin. You manage the group chat.") + .RegisterMessageConnector() + .RegisterMiddleware(Print); + await RunMathChatAsync(teacher, student, admin, groupAdmin); + } + + private async Task CreateTeacherAgentAsync(OpenAIClient client, string model) + { + var functionCallMiddleware = new FunctionCallMiddleware( + functions: [this.CreateMathQuestionFunctionContract, this.AnswerIsCorrectFunctionContract], + functionMap: new Dictionary>> + { + { this.CreateMathQuestionFunctionContract.Name!, this.CreateMathQuestionWrapper }, + { this.AnswerIsCorrectFunctionContract.Name!, this.AnswerIsCorrectWrapper }, + }); + + var teacher = new OpenAIChatAgent( + openAIClient: client, + name: "Teacher", + systemMessage: @"You are a preschool math teacher. +You create math question and ask student to answer it. +Then you check if the answer is correct. +If the answer is wrong, you ask student to fix it", + modelName: model) + .RegisterMessageConnector() + .RegisterStreamingMiddleware(functionCallMiddleware) + .RegisterMiddleware(Print); + + return teacher; + } + + private async Task CreateStudentAssistantAgentAsync(OpenAIClient client, string model) + { + var functionCallMiddleware = new FunctionCallMiddleware( + functions: [this.AnswerQuestionFunctionContract], + functionMap: new Dictionary>> + { + { this.AnswerQuestionFunctionContract.Name!, this.AnswerQuestionWrapper }, + }); + var student = new OpenAIChatAgent( + openAIClient: client, + name: "Student", + modelName: model, + systemMessage: @"You are a student. You answer math question from teacher.") + .RegisterMessageConnector() + .RegisterStreamingMiddleware(functionCallMiddleware) + .RegisterMiddleware(Print); + + return student; + } + + private async Task RunMathChatAsync(IAgent teacher, IAgent student, IAgent admin, IAgent groupAdmin) + { + var teacher2Student = Transition.Create(teacher, student); + var student2Teacher = Transition.Create(student, teacher); + var teacher2Admin = Transition.Create(teacher, admin); + var admin2Teacher = Transition.Create(admin, teacher); + var workflow = new Graph( + [ + teacher2Student, + student2Teacher, + teacher2Admin, + admin2Teacher, + ]); + var group = new GroupChat( + workflow: workflow, + members: [ + admin, + teacher, + student, + ], + admin: groupAdmin); + + var groupChatManager = new GroupChatManager(group); + var chatHistory = await admin.InitiateChatAsync(groupChatManager, "teacher, create question", maxRound: 50); + + chatHistory.Where(msg => msg.From == teacher.Name && msg.GetContent()?.Contains("[MATH_QUESTION]") is true) + .Count() + .Should().BeGreaterThanOrEqualTo(this.round); + + chatHistory.Where(msg => msg.From == student.Name && msg.GetContent()?.Contains("[MATH_ANSWER]") is true) + .Count() + .Should().BeGreaterThanOrEqualTo(this.round); + + chatHistory.Where(msg => msg.From == teacher.Name && msg.GetContent()?.Contains("[ANSWER_IS_CORRECT]") is true) + .Count() + .Should().BeGreaterThanOrEqualTo(this.round); + + // check if there's terminate chat message from admin + chatHistory.Where(msg => msg.From == admin.Name && msg.IsGroupChatTerminateMessage()) + .Count() + .Should().Be(1); + } + } +} diff --git a/dotnet/test/AutoGen.Tests/OpenAIChatAgentTest.cs b/dotnet/test/AutoGen.OpenAI.Tests/OpenAIChatAgentTest.cs similarity index 93% rename from dotnet/test/AutoGen.Tests/OpenAIChatAgentTest.cs rename to dotnet/test/AutoGen.OpenAI.Tests/OpenAIChatAgentTest.cs index c504eb06a18..284cd3856bb 100644 --- a/dotnet/test/AutoGen.Tests/OpenAIChatAgentTest.cs +++ b/dotnet/test/AutoGen.OpenAI.Tests/OpenAIChatAgentTest.cs @@ -5,12 +5,12 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using AutoGen.OpenAI; using AutoGen.OpenAI.Extension; +using AutoGen.Tests; using Azure.AI.OpenAI; using FluentAssertions; -namespace AutoGen.Tests; +namespace AutoGen.OpenAI.Tests; public partial class OpenAIChatAgentTest { @@ -79,7 +79,6 @@ public async Task OpenAIChatMessageContentConnectorTestAsync() new TextMessage(Role.Assistant, "Hello", from: "user"), ], from: "user"), - new Message(Role.Assistant, "Hello", from: "user"), // Message type is going to be deprecated, please use TextMessage instead }; foreach (var message in messages) @@ -133,7 +132,6 @@ public async Task OpenAIChatAgentToolCallTestAsync() new TextMessage(Role.Assistant, question, from: "user"), ], from: "user"), - new Message(Role.Assistant, question, from: "user"), // Message type is going to be deprecated, please use TextMessage instead }; foreach (var message in messages) @@ -202,14 +200,13 @@ public async Task OpenAIChatAgentToolCallInvokingTestAsync() new TextMessage(Role.Assistant, question, from: "user"), ], from: "user"), - new Message(Role.Assistant, question, from: "user"), // Message type is going to be deprecated, please use TextMessage instead }; foreach (var message in messages) { var reply = await functionCallAgent.SendAsync(message); - reply.Should().BeOfType>(); + reply.Should().BeOfType(); reply.From.Should().Be("assistant"); reply.GetToolCalls()!.Count().Should().Be(1); reply.GetToolCalls()!.First().FunctionName.Should().Be(this.GetWeatherAsyncFunctionContract.Name); @@ -229,7 +226,7 @@ public async Task OpenAIChatAgentToolCallInvokingTestAsync() } else { - streamingMessage.Should().BeOfType>(); + streamingMessage.Should().BeOfType(); streamingMessage.As().GetContent()!.ToLower().Should().Contain("seattle"); } } diff --git a/dotnet/test/AutoGen.OpenAI.Tests/OpenAIMessageTests.cs b/dotnet/test/AutoGen.OpenAI.Tests/OpenAIMessageTests.cs new file mode 100644 index 00000000000..81581d068ee --- /dev/null +++ b/dotnet/test/AutoGen.OpenAI.Tests/OpenAIMessageTests.cs @@ -0,0 +1,720 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// OpenAIMessageTests.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using System.Threading.Tasks; +using ApprovalTests; +using ApprovalTests.Namers; +using ApprovalTests.Reporters; +using AutoGen.Tests; +using Azure.AI.OpenAI; +using FluentAssertions; +using Xunit; + +namespace AutoGen.OpenAI.Tests; + +public class OpenAIMessageTests +{ + private readonly JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + WriteIndented = true, + IgnoreReadOnlyProperties = false, + }; + + [Fact] + [UseReporter(typeof(DiffReporter))] + [UseApprovalSubdirectory("ApprovalTests")] + public void BasicMessageTest() + { + IMessage[] messages = [ + new TextMessage(Role.System, "You are a helpful AI assistant"), + new TextMessage(Role.User, "Hello", "user"), + new TextMessage(Role.Assistant, "How can I help you?", from: "assistant"), + new ImageMessage(Role.User, "https://example.com/image.png", "user"), + new MultiModalMessage(Role.Assistant, + [ + new TextMessage(Role.User, "Hello", "user"), + new ImageMessage(Role.User, "https://example.com/image.png", "user"), + ], "user"), + new ToolCallMessage("test", "test", "assistant"), + new ToolCallResultMessage("result", "test", "test", "user"), + new ToolCallResultMessage( + [ + new ToolCall("result", "test", "test"), + new ToolCall("result", "test", "test"), + ], "user"), + new ToolCallMessage( + [ + new ToolCall("test", "test"), + new ToolCall("test", "test"), + ], "assistant"), + new AggregateMessage( + message1: new ToolCallMessage("test", "test", "assistant"), + message2: new ToolCallResultMessage("result", "test", "test", "assistant"), "assistant"), + ]; + var openaiMessageConnectorMiddleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant"); + + var oaiMessages = messages.Select(m => (m, openaiMessageConnectorMiddleware.ProcessIncomingMessages(agent, [m]))); + VerifyOAIMessages(oaiMessages); + } + + [Fact] + public async Task ItProcessUserTextMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestUserMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().Be("Hello"); + chatRequestMessage.Name.Should().Be("user"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + IMessage message = new TextMessage(Role.User, "Hello", "user"); + await agent.GenerateReplyAsync([message]); + } + + [Fact] + public async Task ItShortcutChatRequestMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + + var chatRequestMessage = (ChatRequestUserMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().Be("hello"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + var userMessage = new ChatRequestUserMessage("hello"); + var chatRequestMessage = MessageEnvelope.Create(userMessage); + await agent.GenerateReplyAsync([chatRequestMessage]); + } + + [Fact] + public async Task ItShortcutMessageWhenStrictModelIsFalseAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + + var chatRequestMessage = ((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Should().Be("hello"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + var userMessage = "hello"; + var chatRequestMessage = MessageEnvelope.Create(userMessage); + await agent.GenerateReplyAsync([chatRequestMessage]); + } + + [Fact] + public async Task ItThrowExceptionWhenStrictModeIsTrueAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(true); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(middleware); + + // user message + var userMessage = "hello"; + var chatRequestMessage = MessageEnvelope.Create(userMessage); + Func action = async () => await agent.GenerateReplyAsync([chatRequestMessage]); + + await action.Should().ThrowAsync().WithMessage("Invalid message type: MessageEnvelope`1"); + } + + [Fact] + public async Task ItProcessAssistantTextMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestAssistantMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().Be("How can I help you?"); + chatRequestMessage.Name.Should().Be("assistant"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // assistant message + IMessage message = new TextMessage(Role.Assistant, "How can I help you?", "assistant"); + await agent.GenerateReplyAsync([message]); + } + + [Fact] + public async Task ItProcessSystemTextMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestSystemMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().Be("You are a helpful AI assistant"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // system message + IMessage message = new TextMessage(Role.System, "You are a helpful AI assistant"); + await agent.GenerateReplyAsync([message]); + } + + [Fact] + public async Task ItProcessImageMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestUserMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().BeNullOrEmpty(); + chatRequestMessage.Name.Should().Be("user"); + chatRequestMessage.MultimodalContentItems.Count().Should().Be(1); + chatRequestMessage.MultimodalContentItems.First().Should().BeOfType(); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + IMessage message = new ImageMessage(Role.User, "https://example.com/image.png", "user"); + await agent.GenerateReplyAsync([message]); + } + + [Fact] + public async Task ItThrowExceptionWhenProcessingImageMessageFromSelfAndStrictModeIsTrueAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(true); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(middleware); + + var imageMessage = new ImageMessage(Role.Assistant, "https://example.com/image.png", "assistant"); + Func action = async () => await agent.GenerateReplyAsync([imageMessage]); + + await action.Should().ThrowAsync().WithMessage("Invalid message type: ImageMessage"); + } + + [Fact] + public async Task ItProcessMultiModalMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestUserMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().BeNullOrEmpty(); + chatRequestMessage.Name.Should().Be("user"); + chatRequestMessage.MultimodalContentItems.Count().Should().Be(2); + chatRequestMessage.MultimodalContentItems.First().Should().BeOfType(); + chatRequestMessage.MultimodalContentItems.Last().Should().BeOfType(); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + IMessage message = new MultiModalMessage( + Role.User, + [ + new TextMessage(Role.User, "Hello", "user"), + new ImageMessage(Role.User, "https://example.com/image.png", "user"), + ], "user"); + await agent.GenerateReplyAsync([message]); + } + + [Fact] + public async Task ItThrowExceptionWhenProcessingMultiModalMessageFromSelfAndStrictModeIsTrueAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(true); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(middleware); + + var multiModalMessage = new MultiModalMessage( + Role.Assistant, + [ + new TextMessage(Role.User, "Hello", "assistant"), + new ImageMessage(Role.User, "https://example.com/image.png", "assistant"), + ], "assistant"); + + Func action = async () => await agent.GenerateReplyAsync([multiModalMessage]); + + await action.Should().ThrowAsync().WithMessage("Invalid message type: MultiModalMessage"); + } + + [Fact] + public async Task ItProcessToolCallMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestAssistantMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().BeNullOrEmpty(); + chatRequestMessage.Name.Should().Be("assistant"); + chatRequestMessage.ToolCalls.Count().Should().Be(1); + chatRequestMessage.ToolCalls.First().Should().BeOfType(); + var functionToolCall = (ChatCompletionsFunctionToolCall)chatRequestMessage.ToolCalls.First(); + functionToolCall.Name.Should().Be("test"); + functionToolCall.Id.Should().Be("test"); + functionToolCall.Arguments.Should().Be("test"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + IMessage message = new ToolCallMessage("test", "test", "assistant"); + await agent.GenerateReplyAsync([message]); + } + + [Fact] + public async Task ItProcessParallelToolCallMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestAssistantMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().BeNullOrEmpty(); + chatRequestMessage.Name.Should().Be("assistant"); + chatRequestMessage.ToolCalls.Count().Should().Be(2); + for (int i = 0; i < chatRequestMessage.ToolCalls.Count(); i++) + { + chatRequestMessage.ToolCalls.ElementAt(i).Should().BeOfType(); + var functionToolCall = (ChatCompletionsFunctionToolCall)chatRequestMessage.ToolCalls.ElementAt(i); + functionToolCall.Name.Should().Be("test"); + functionToolCall.Id.Should().Be($"test_{i}"); + functionToolCall.Arguments.Should().Be("test"); + } + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + var toolCalls = new[] + { + new ToolCall("test", "test"), + new ToolCall("test", "test"), + }; + IMessage message = new ToolCallMessage(toolCalls, "assistant"); + await agent.GenerateReplyAsync([message]); + } + + [Fact] + public async Task ItThrowExceptionWhenProcessingToolCallMessageFromUserAndStrictModeIsTrueAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(strictMode: true); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(middleware); + + var toolCallMessage = new ToolCallMessage("test", "test", "user"); + Func action = async () => await agent.GenerateReplyAsync([toolCallMessage]); + await action.Should().ThrowAsync().WithMessage("Invalid message type: ToolCallMessage"); + } + + [Fact] + public async Task ItProcessToolCallResultMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestToolMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().Be("result"); + chatRequestMessage.ToolCallId.Should().Be("test"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + IMessage message = new ToolCallResultMessage("result", "test", "test", "user"); + await agent.GenerateReplyAsync([message]); + } + + [Fact] + public async Task ItProcessParallelToolCallResultMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + msgs.Count().Should().Be(2); + + for (int i = 0; i < msgs.Count(); i++) + { + var innerMessage = msgs.ElementAt(i); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestToolMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().Be("result"); + chatRequestMessage.ToolCallId.Should().Be($"test_{i}"); + } + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + var toolCalls = new[] + { + new ToolCall("test", "test", "result"), + new ToolCall("test", "test", "result"), + }; + IMessage message = new ToolCallResultMessage(toolCalls, "user"); + await agent.GenerateReplyAsync([message]); + } + + [Fact] + public async Task ItProcessFunctionCallMiddlewareMessageFromUserAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + msgs.Count().Should().Be(1); + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestUserMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().Be("result"); + chatRequestMessage.Name.Should().Be("user"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + var toolCallMessage = new ToolCallMessage("test", "test", "user"); + var toolCallResultMessage = new ToolCallResultMessage("result", "test", "test", "user"); + var aggregateMessage = new AggregateMessage(toolCallMessage, toolCallResultMessage, "user"); + await agent.GenerateReplyAsync([aggregateMessage]); + } + + [Fact] + public async Task ItProcessFunctionCallMiddlewareMessageFromAssistantAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + msgs.Count().Should().Be(2); + var innerMessage = msgs.Last(); + innerMessage!.Should().BeOfType>(); + var chatRequestMessage = (ChatRequestToolMessage)((MessageEnvelope)innerMessage!).Content; + chatRequestMessage.Content.Should().Be("result"); + chatRequestMessage.ToolCallId.Should().Be("test"); + + var toolCallMessage = msgs.First(); + toolCallMessage!.Should().BeOfType>(); + var toolCallRequestMessage = (ChatRequestAssistantMessage)((MessageEnvelope)toolCallMessage!).Content; + toolCallRequestMessage.Content.Should().BeNullOrEmpty(); + toolCallRequestMessage.ToolCalls.Count().Should().Be(1); + toolCallRequestMessage.ToolCalls.First().Should().BeOfType(); + var functionToolCall = (ChatCompletionsFunctionToolCall)toolCallRequestMessage.ToolCalls.First(); + functionToolCall.Name.Should().Be("test"); + functionToolCall.Id.Should().Be("test"); + functionToolCall.Arguments.Should().Be("test"); + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + var toolCallMessage = new ToolCallMessage("test", "test", "assistant"); + var toolCallResultMessage = new ToolCallResultMessage("result", "test", "test", "assistant"); + var aggregateMessage = new ToolCallAggregateMessage(toolCallMessage, toolCallResultMessage, "assistant"); + await agent.GenerateReplyAsync([aggregateMessage]); + } + + [Fact] + public async Task ItProcessParallelFunctionCallMiddlewareMessageFromAssistantAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(async (msgs, _, innerAgent, _) => + { + msgs.Count().Should().Be(3); + var toolCallMessage = msgs.First(); + toolCallMessage!.Should().BeOfType>(); + var toolCallRequestMessage = (ChatRequestAssistantMessage)((MessageEnvelope)toolCallMessage!).Content; + toolCallRequestMessage.Content.Should().BeNullOrEmpty(); + toolCallRequestMessage.ToolCalls.Count().Should().Be(2); + + for (int i = 0; i < toolCallRequestMessage.ToolCalls.Count(); i++) + { + toolCallRequestMessage.ToolCalls.ElementAt(i).Should().BeOfType(); + var functionToolCall = (ChatCompletionsFunctionToolCall)toolCallRequestMessage.ToolCalls.ElementAt(i); + functionToolCall.Name.Should().Be("test"); + functionToolCall.Id.Should().Be($"test_{i}"); + functionToolCall.Arguments.Should().Be("test"); + } + + for (int i = 1; i < msgs.Count(); i++) + { + var toolCallResultMessage = msgs.ElementAt(i); + toolCallResultMessage!.Should().BeOfType>(); + var toolCallResultRequestMessage = (ChatRequestToolMessage)((MessageEnvelope)toolCallResultMessage!).Content; + toolCallResultRequestMessage.Content.Should().Be("result"); + toolCallResultRequestMessage.ToolCallId.Should().Be($"test_{i - 1}"); + } + + return await innerAgent.GenerateReplyAsync(msgs); + }) + .RegisterMiddleware(middleware); + + // user message + var toolCalls = new[] + { + new ToolCall("test", "test", "result"), + new ToolCall("test", "test", "result"), + }; + var toolCallMessage = new ToolCallMessage(toolCalls, "assistant"); + var toolCallResultMessage = new ToolCallResultMessage(toolCalls, "assistant"); + var aggregateMessage = new AggregateMessage(toolCallMessage, toolCallResultMessage, "assistant"); + await agent.GenerateReplyAsync([aggregateMessage]); + } + + [Fact] + public async Task ItConvertChatResponseMessageToTextMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(middleware); + + // text message + var textMessage = CreateInstance(ChatRole.Assistant, "hello"); + var chatRequestMessage = MessageEnvelope.Create(textMessage); + + var message = await agent.GenerateReplyAsync([chatRequestMessage]); + message.Should().BeOfType(); + message.GetContent().Should().Be("hello"); + message.GetRole().Should().Be(Role.Assistant); + } + + [Fact] + public async Task ItConvertChatResponseMessageToToolCallMessageAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(middleware); + + // tool call message + var toolCallMessage = CreateInstance(ChatRole.Assistant, "", new[] { new ChatCompletionsFunctionToolCall("test", "test", "test") }, new FunctionCall("test", "test"), CreateInstance(), new Dictionary()); + var chatRequestMessage = MessageEnvelope.Create(toolCallMessage); + var message = await agent.GenerateReplyAsync([chatRequestMessage]); + message.Should().BeOfType(); + message.GetToolCalls()!.Count().Should().Be(1); + message.GetToolCalls()!.First().FunctionName.Should().Be("test"); + message.GetToolCalls()!.First().FunctionArguments.Should().Be("test"); + } + + [Fact] + public async Task ItReturnOriginalMessageWhenStrictModeIsFalseAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(middleware); + + // text message + var textMessage = "hello"; + var messageToSend = MessageEnvelope.Create(textMessage); + + var message = await agent.GenerateReplyAsync([messageToSend]); + message.Should().BeOfType>(); + } + + [Fact] + public async Task ItThrowInvalidOperationExceptionWhenStrictModeIsTrueAsync() + { + var middleware = new OpenAIChatRequestMessageConnector(true); + var agent = new EchoAgent("assistant") + .RegisterMiddleware(middleware); + + // text message + var textMessage = new ChatRequestUserMessage("hello"); + var messageToSend = MessageEnvelope.Create(textMessage); + Func action = async () => await agent.GenerateReplyAsync([messageToSend]); + + await action.Should().ThrowAsync().WithMessage("Invalid return message type MessageEnvelope`1"); + } + + [Fact] + public void ToOpenAIChatRequestMessageShortCircuitTest() + { + var agent = new EchoAgent("assistant"); + var middleware = new OpenAIChatRequestMessageConnector(); + ChatRequestMessage[] messages = + [ + new ChatRequestUserMessage("Hello"), + new ChatRequestAssistantMessage("How can I help you?"), + new ChatRequestSystemMessage("You are a helpful AI assistant"), + new ChatRequestFunctionMessage("result", "functionName"), + new ChatRequestToolMessage("test", "test"), + ]; + + foreach (var oaiMessage in messages) + { + IMessage message = new MessageEnvelope(oaiMessage); + var oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); + oaiMessages.Count().Should().Be(1); + //oaiMessages.First().Should().BeOfType>(); + if (oaiMessages.First() is IMessage chatRequestMessage) + { + chatRequestMessage.Content.Should().Be(oaiMessage); + } + else + { + // fail the test + Assert.True(false); + } + } + } + private void VerifyOAIMessages(IEnumerable<(IMessage, IEnumerable)> messages) + { + var jsonObjects = messages.Select(pair => + { + var (originalMessage, ms) = pair; + var objs = new List(); + foreach (var m in ms) + { + object? obj = null; + var chatRequestMessage = (m as IMessage)?.Content; + if (chatRequestMessage is ChatRequestUserMessage userMessage) + { + obj = new + { + Role = userMessage.Role.ToString(), + Content = userMessage.Content, + Name = userMessage.Name, + MultiModaItem = userMessage.MultimodalContentItems?.Select(item => + { + return item switch + { + ChatMessageImageContentItem imageContentItem => new + { + Type = "Image", + ImageUrl = GetImageUrlFromContent(imageContentItem), + } as object, + ChatMessageTextContentItem textContentItem => new + { + Type = "Text", + Text = textContentItem.Text, + } as object, + _ => throw new System.NotImplementedException(), + }; + }), + }; + } + + if (chatRequestMessage is ChatRequestAssistantMessage assistantMessage) + { + obj = new + { + Role = assistantMessage.Role.ToString(), + Content = assistantMessage.Content, + Name = assistantMessage.Name, + TooCall = assistantMessage.ToolCalls.Select(tc => + { + return tc switch + { + ChatCompletionsFunctionToolCall functionToolCall => new + { + Type = "Function", + Name = functionToolCall.Name, + Arguments = functionToolCall.Arguments, + Id = functionToolCall.Id, + } as object, + _ => throw new System.NotImplementedException(), + }; + }), + FunctionCallName = assistantMessage.FunctionCall?.Name, + FunctionCallArguments = assistantMessage.FunctionCall?.Arguments, + }; + } + + if (chatRequestMessage is ChatRequestSystemMessage systemMessage) + { + obj = new + { + Name = systemMessage.Name, + Role = systemMessage.Role.ToString(), + Content = systemMessage.Content, + }; + } + + if (chatRequestMessage is ChatRequestFunctionMessage functionMessage) + { + obj = new + { + Role = functionMessage.Role.ToString(), + Content = functionMessage.Content, + Name = functionMessage.Name, + }; + } + + if (chatRequestMessage is ChatRequestToolMessage toolCallMessage) + { + obj = new + { + Role = toolCallMessage.Role.ToString(), + Content = toolCallMessage.Content, + ToolCallId = toolCallMessage.ToolCallId, + }; + } + + objs.Add(obj ?? throw new System.NotImplementedException()); + } + + return new + { + OriginalMessage = originalMessage.ToString(), + ConvertedMessages = objs, + }; + }); + + var json = JsonSerializer.Serialize(jsonObjects, this.jsonSerializerOptions); + Approvals.Verify(json); + } + + private object? GetImageUrlFromContent(ChatMessageImageContentItem content) + { + return content.GetType().GetProperty("ImageUrl", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.GetValue(content); + } + + private static T CreateInstance(params object[] args) + { + var type = typeof(T); + var instance = type.Assembly.CreateInstance( + type.FullName!, false, + BindingFlags.Instance | BindingFlags.NonPublic, + null, args, null, null); + return (T)instance!; + } +} diff --git a/dotnet/test/AutoGen.SemanticKernel.Tests/KernelFunctionMiddlewareTests.cs b/dotnet/test/AutoGen.SemanticKernel.Tests/KernelFunctionMiddlewareTests.cs index f560419e8c8..658cc899e10 100644 --- a/dotnet/test/AutoGen.SemanticKernel.Tests/KernelFunctionMiddlewareTests.cs +++ b/dotnet/test/AutoGen.SemanticKernel.Tests/KernelFunctionMiddlewareTests.cs @@ -30,8 +30,8 @@ public async Task ItRegisterKernelFunctionMiddlewareFromTestPluginTests() var reply = await agent.SendAsync("what's the status of the light?"); reply.GetContent().Should().Be("off"); - reply.Should().BeOfType>(); - if (reply is AggregateMessage aggregateMessage) + reply.Should().BeOfType(); + if (reply is ToolCallAggregateMessage aggregateMessage) { var toolCallMessage = aggregateMessage.Message1; toolCallMessage.ToolCalls.Should().HaveCount(1); @@ -44,8 +44,8 @@ public async Task ItRegisterKernelFunctionMiddlewareFromTestPluginTests() reply = await agent.SendAsync("change the status of the light to on"); reply.GetContent().Should().Be("The status of the light is now on"); - reply.Should().BeOfType>(); - if (reply is AggregateMessage aggregateMessage1) + reply.Should().BeOfType(); + if (reply is ToolCallAggregateMessage aggregateMessage1) { var toolCallMessage = aggregateMessage1.Message1; toolCallMessage.ToolCalls.Should().HaveCount(1); @@ -75,8 +75,8 @@ public async Task ItRegisterKernelFunctionMiddlewareFromMethodTests() var reply = await agent.SendAsync("what's the weather in Seattle?"); reply.GetContent().Should().Be("The weather in Seattle is sunny."); - reply.Should().BeOfType>(); - if (reply is AggregateMessage getWeatherMessage) + reply.Should().BeOfType(); + if (reply is ToolCallAggregateMessage getWeatherMessage) { var toolCallMessage = getWeatherMessage.Message1; toolCallMessage.ToolCalls.Should().HaveCount(1); @@ -88,8 +88,8 @@ public async Task ItRegisterKernelFunctionMiddlewareFromMethodTests() reply = await agent.SendAsync("Create a person object with name: John, email: 12345@gmail.com, age: 30"); reply.GetContent().Should().Be("Name: John, Email: 12345@gmail.com, Age: 30"); - reply.Should().BeOfType>(); - if (reply is AggregateMessage createPersonObjectMessage) + reply.Should().BeOfType(); + if (reply is ToolCallAggregateMessage createPersonObjectMessage) { var toolCallMessage = createPersonObjectMessage.Message1; toolCallMessage.ToolCalls.Should().HaveCount(1); diff --git a/dotnet/test/AutoGen.SourceGenerator.Tests/ApprovalTests/FunctionCallTemplateTests.TestFunctionCallTemplate.approved.txt b/dotnet/test/AutoGen.SourceGenerator.Tests/ApprovalTests/FunctionCallTemplateTests.TestFunctionCallTemplate.approved.txt new file mode 100644 index 00000000000..feab4ebd607 --- /dev/null +++ b/dotnet/test/AutoGen.SourceGenerator.Tests/ApprovalTests/FunctionCallTemplateTests.TestFunctionCallTemplate.approved.txt @@ -0,0 +1,71 @@ +//---------------------- +// +// This code was generated by a tool. +// +//---------------------- +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using System; +using AutoGen.Core; +using AutoGen.OpenAI.Extension; + +namespace AutoGen.SourceGenerator.Tests +{ + public partial class FunctionExamples + { + + private class AddAsyncSchema + { + [JsonPropertyName(@"a")] + public System.Int32 a {get; set;} + [JsonPropertyName(@"b")] + public System.Int32 b {get; set;} + } + + public System.Threading.Tasks.Task`1[System.String] AddAsyncWrapper(string arguments) + { + var schema = JsonSerializer.Deserialize( + arguments, + new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }); + + return AddAsync(schema.a, schema.b); + } + + public FunctionContract AddAsyncFunctionContract + { + get => new FunctionContract + { + Name = @"AddAsync", + Description = @"Add two numbers.", + ReturnType = typeof(System.Threading.Tasks.Task`1[System.String]), + Parameters = new [] + { + new FunctionParameterContract + { + Name = @"a", + Description = @"The first number.", + ParameterType = typeof(System.Int32), + IsRequired = true, + }, + new FunctionParameterContract + { + Name = @"b", + Description = @"The second number.", + ParameterType = typeof(System.Int32), + IsRequired = true, + }, + }, + }; + } + + public global::Azure.AI.OpenAI.FunctionDefinition AddAsyncFunction + { + get => this.AddAsyncFunctionContract.ToOpenAIFunctionDefinition(); + } + } +} + diff --git a/dotnet/test/AutoGen.SourceGenerator.Tests/AutoGen.SourceGenerator.Tests.csproj b/dotnet/test/AutoGen.SourceGenerator.Tests/AutoGen.SourceGenerator.Tests.csproj index c4b2a8aa8ce..0d0d91e0522 100644 --- a/dotnet/test/AutoGen.SourceGenerator.Tests/AutoGen.SourceGenerator.Tests.csproj +++ b/dotnet/test/AutoGen.SourceGenerator.Tests/AutoGen.SourceGenerator.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/dotnet/test/AutoGen.SourceGenerator.Tests/FunctionCallTemplateTests.cs b/dotnet/test/AutoGen.SourceGenerator.Tests/FunctionCallTemplateTests.cs new file mode 100644 index 00000000000..3c1e6c8ede3 --- /dev/null +++ b/dotnet/test/AutoGen.SourceGenerator.Tests/FunctionCallTemplateTests.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// FunctionCallTemplateTests.cs + +using ApprovalTests; +using ApprovalTests.Namers; +using ApprovalTests.Reporters; +using AutoGen.SourceGenerator.Template; +using Xunit; + +namespace AutoGen.SourceGenerator.Tests; + +public class FunctionCallTemplateTests +{ + [Fact] + [UseReporter(typeof(DiffReporter))] + [UseApprovalSubdirectory("ApprovalTests")] + public void TestFunctionCallTemplate() + { + var functionExample = new FunctionExamples(); + var function = functionExample.AddAsyncFunctionContract; + var functionCallTemplate = new FunctionCallTemplate() + { + ClassName = function.ClassName, + NameSpace = function.Namespace, + FunctionContracts = [new SourceGeneratorFunctionContract() + { + Name = function.Name, + Description = function.Description, + ReturnType = function.ReturnType!.ToString(), + ReturnDescription = function.ReturnDescription, + Parameters = function.Parameters!.Select(p => new SourceGeneratorParameterContract() + { + Name = p.Name, + Description = p.Description, + Type = p.ParameterType!.ToString(), + IsOptional = !p.IsRequired, + JsonType = p.ParameterType!.ToString(), + }).ToArray() + }] + }; + + var actual = functionCallTemplate.TransformText(); + + Approvals.Verify(actual); + } +} diff --git a/dotnet/test/AutoGen.Tests/EchoAgent.cs b/dotnet/test/AutoGen.Tests/EchoAgent.cs index 28a7b91bad5..9cead5ad251 100644 --- a/dotnet/test/AutoGen.Tests/EchoAgent.cs +++ b/dotnet/test/AutoGen.Tests/EchoAgent.cs @@ -3,12 +3,13 @@ using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; namespace AutoGen.Tests { - internal class EchoAgent : IAgent + public class EchoAgent : IStreamingAgent { public EchoAgent(string name) { @@ -27,5 +28,14 @@ public Task GenerateReplyAsync( return Task.FromResult(lastMessage); } + + public async IAsyncEnumerable GenerateStreamingReplyAsync(IEnumerable messages, GenerateReplyOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + foreach (var message in messages) + { + message.From = this.Name; + yield return message; + } + } } } diff --git a/dotnet/test/AutoGen.Tests/MathClassTest.cs b/dotnet/test/AutoGen.Tests/MathClassTest.cs deleted file mode 100644 index 3f1eac76246..00000000000 --- a/dotnet/test/AutoGen.Tests/MathClassTest.cs +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// MathClassTest.cs - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AutoGen.OpenAI; -using FluentAssertions; -using Xunit.Abstractions; - -namespace AutoGen.Tests -{ - public partial class MathClassTest - { - private readonly ITestOutputHelper _output; - public MathClassTest(ITestOutputHelper output) - { - _output = output; - } - - [FunctionAttribute] - public async Task CreateMathQuestion(string question, int question_index) - { - return $@"// ignore this line [MATH_QUESTION] -Question #{question_index}: -{question}"; - } - - [FunctionAttribute] - public async Task AnswerQuestion(string answer) - { - return $@"// ignore this line [MATH_ANSWER] -The answer is {answer}, teacher please check answer"; - } - - [FunctionAttribute] - public async Task AnswerIsCorrect(string message) - { - return $@"// ignore this line [ANSWER_IS_CORRECT] -{message}"; - } - - [FunctionAttribute] - public async Task UpdateProgress(int correctAnswerCount) - { - if (correctAnswerCount >= 5) - { - return $@"// ignore this line [UPDATE_PROGRESS] -{GroupChatExtension.TERMINATE}"; - } - else - { - return $@"// ignore this line [UPDATE_PROGRESS] -the number of resolved question is {correctAnswerCount} -teacher, please create the next math question"; - } - } - - - [ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT")] - public async Task AssistantAgentMathChatTestAsync() - { - var teacher = await CreateTeacherAssistantAgentAsync(); - var student = await CreateStudentAssistantAgentAsync(); - var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new ArgumentException("AZURE_OPENAI_API_KEY is not set"); - var endPoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new ArgumentException("AZURE_OPENAI_ENDPOINT is not set"); - var model = "gpt-35-turbo-16k"; - var admin = new GPTAgent( - name: "Admin", - systemMessage: $@"You are admin. You ask teacher to create 5 math questions. You update progress after each question is answered.", - config: new AzureOpenAIConfig(endPoint, model, key), - functions: new[] - { - this.UpdateProgressFunction, - }, - functionMap: new Dictionary>> - { - { this.UpdateProgressFunction.Name, this.UpdateProgressWrapper }, - }) - .RegisterMiddleware(async (messages, options, agent, ct) => - { - // check admin reply to make sure it calls UpdateProgress function - var maxAttempt = 5; - var reply = await agent.GenerateReplyAsync(messages, options, ct); - while (maxAttempt-- > 0) - { - if (options?.Functions is { Length: 0 }) - { - return reply; - } - - var formattedMessage = reply.FormatMessage(); - this._output.WriteLine(formattedMessage); - if (reply.GetContent()?.Contains("[UPDATE_PROGRESS]") is true) - { - return reply; - } - else - { - await Task.Delay(1000); - var review = "Admin, please update progress based on conversation"; - reply = await agent.SendAsync(review, messages, ct); - } - } - - throw new Exception("Admin does not call UpdateProgress function"); - }); - - await RunMathChatAsync(teacher, student, admin); - } - - private async Task CreateTeacherAssistantAgentAsync() - { - var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new ArgumentException("AZURE_OPENAI_API_KEY is not set"); - var endPoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new ArgumentException("AZURE_OPENAI_ENDPOINT is not set"); - var model = "gpt-35-turbo-16k"; - var config = new AzureOpenAIConfig(endPoint, model, key); - var llmConfig = new ConversableAgentConfig - { - ConfigList = new[] - { - config, - }, - FunctionContracts = new[] - { - this.CreateMathQuestionFunctionContract, - this.AnswerIsCorrectFunctionContract, - }, - }; - - var teacher = new AssistantAgent( - name: "Teacher", - systemMessage: $@"You are a preschool math teacher. -You create math question and ask student to answer it. -Then you check if the answer is correct. -If the answer is wrong, you ask student to fix it. -If the answer is correct, you create another math question. -", - llmConfig: llmConfig, - functionMap: new Dictionary>> - { - { this.CreateMathQuestionFunction.Name, this.CreateMathQuestionWrapper }, - { this.AnswerIsCorrectFunction.Name, this.AnswerIsCorrectWrapper }, - }); - - return teacher; - } - - private async Task CreateStudentAssistantAgentAsync() - { - var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new ArgumentException("AZURE_OPENAI_API_KEY is not set"); - var endPoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new ArgumentException("AZURE_OPENAI_ENDPOINT is not set"); - var model = "gpt-35-turbo-16k"; - var config = new AzureOpenAIConfig(endPoint, model, key); - var llmConfig = new ConversableAgentConfig - { - FunctionContracts = new[] - { - this.AnswerQuestionFunctionContract, - }, - ConfigList = new[] - { - config, - }, - }; - var student = new AssistantAgent( - name: "Student", - systemMessage: $@"You are a student. Here's your workflow in pseudo code: --workflow- -answer_question -if answer is wrong - fix_answer --end- - -Here are a few examples of answer_question: --example 1- -2 - -Here are a few examples of fix_answer: --example 1- -sorry, the answer should be 2, not 3 -", - llmConfig: llmConfig, - functionMap: new Dictionary>> - { - { this.AnswerQuestionFunction.Name, this.AnswerQuestionWrapper } - }); - - return student; - } - - private async Task RunMathChatAsync(IAgent teacher, IAgent student, IAgent admin) - { - var group = new GroupChat( - [ - admin, - teacher, - student, - ], - admin); - - admin.SendIntroduction($@"Welcome to the group chat! I'm admin", group); - teacher.SendIntroduction($@"Hey I'm Teacher", group); - student.SendIntroduction($@"Hey I'm Student", group); - admin.SendIntroduction(@$"Teacher, please create pre-school math question for student and check answer. -Student, for each question, please answer it and ask teacher to check if the answer is correct. -I'll update the progress after each question is answered. -The conversation will end after 5 correct answers. -", group); - - var groupChatManager = new GroupChatManager(group); - var chatHistory = await admin.InitiateChatAsync(groupChatManager, maxRound: 50); - - // print chat history - foreach (var message in chatHistory) - { - _output.WriteLine(message.FormatMessage()); - } - - // check if there's five questions from teacher - chatHistory.Where(msg => msg.From == teacher.Name && msg.GetContent()?.Contains("[MATH_QUESTION]") is true) - .Count() - .Should().BeGreaterThanOrEqualTo(5); - - // check if there's more than five answers from student (answer might be wrong) - chatHistory.Where(msg => msg.From == student.Name && msg.GetContent()?.Contains("[MATH_ANSWER]") is true) - .Count() - .Should().BeGreaterThanOrEqualTo(5); - - // check if there's five answer_is_correct from teacher - chatHistory.Where(msg => msg.From == teacher.Name && msg.GetContent()?.Contains("[ANSWER_IS_CORRECT]") is true) - .Count() - .Should().BeGreaterThanOrEqualTo(5); - - // check if there's terminate chat message from admin - chatHistory.Where(msg => msg.From == admin.Name && msg.IsGroupChatTerminateMessage()) - .Count() - .Should().Be(1); - } - } -} diff --git a/dotnet/test/AutoGen.Tests/OpenAIMessageTests.cs b/dotnet/test/AutoGen.Tests/OpenAIMessageTests.cs deleted file mode 100644 index 6e9cd28c4cb..00000000000 --- a/dotnet/test/AutoGen.Tests/OpenAIMessageTests.cs +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// OpenAIMessageTests.cs - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; -using ApprovalTests; -using ApprovalTests.Namers; -using ApprovalTests.Reporters; -using AutoGen.OpenAI; -using Azure.AI.OpenAI; -using FluentAssertions; -using Xunit; - -namespace AutoGen.Tests; - -public class OpenAIMessageTests -{ - private readonly JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions - { - WriteIndented = true, - IgnoreReadOnlyProperties = false, - }; - - [Fact] - [UseReporter(typeof(DiffReporter))] - [UseApprovalSubdirectory("ApprovalTests")] - public void BasicMessageTest() - { - IMessage[] messages = [ - new TextMessage(Role.System, "You are a helpful AI assistant"), - new TextMessage(Role.User, "Hello", "user"), - new TextMessage(Role.Assistant, "How can I help you?", from: "assistant"), - new Message(Role.System, "You are a helpful AI assistant"), - new Message(Role.User, "Hello", "user"), - new Message(Role.Assistant, "How can I help you?", from: "assistant"), - new Message(Role.Function, "result", "user"), - new Message(Role.Assistant, null, "assistant") - { - FunctionName = "functionName", - FunctionArguments = "functionArguments", - }, - new ImageMessage(Role.User, "https://example.com/image.png", "user"), - new MultiModalMessage(Role.Assistant, - [ - new TextMessage(Role.User, "Hello", "user"), - new ImageMessage(Role.User, "https://example.com/image.png", "user"), - ], "user"), - new ToolCallMessage("test", "test", "assistant"), - new ToolCallResultMessage("result", "test", "test", "user"), - new ToolCallResultMessage( - [ - new ToolCall("result", "test", "test"), - new ToolCall("result", "test", "test"), - ], "user"), - new ToolCallMessage( - [ - new ToolCall("test", "test"), - new ToolCall("test", "test"), - ], "assistant"), - new AggregateMessage( - message1: new ToolCallMessage("test", "test", "assistant"), - message2: new ToolCallResultMessage("result", "test", "test", "assistant"), "assistant"), - ]; - var openaiMessageConnectorMiddleware = new OpenAIChatRequestMessageConnector(); - var agent = new EchoAgent("assistant"); - - var oaiMessages = messages.Select(m => (m, openaiMessageConnectorMiddleware.ProcessIncomingMessages(agent, [m]))); - VerifyOAIMessages(oaiMessages); - } - - [Fact] - public void ToOpenAIChatRequestMessageTest() - { - var agent = new EchoAgent("assistant"); - var middleware = new OpenAIChatRequestMessageConnector(); - - // user message - IMessage message = new TextMessage(Role.User, "Hello", "user"); - var oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().BeOfType(); - var userMessage = (ChatRequestUserMessage)oaiMessages.First(); - userMessage.Content.Should().Be("Hello"); - - // user message test 2 - // even if Role is assistant, it should be converted to user message because it is from the user - message = new TextMessage(Role.Assistant, "Hello", "user"); - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().BeOfType(); - userMessage = (ChatRequestUserMessage)oaiMessages.First(); - userMessage.Content.Should().Be("Hello"); - - // user message with multimodal content - // image - message = new ImageMessage(Role.User, "https://example.com/image.png", "user"); - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().BeOfType(); - userMessage = (ChatRequestUserMessage)oaiMessages.First(); - userMessage.Content.Should().BeNullOrEmpty(); - userMessage.MultimodalContentItems.Count().Should().Be(1); - userMessage.MultimodalContentItems.First().Should().BeOfType(); - - // text and image - message = new MultiModalMessage( - Role.User, - [ - new TextMessage(Role.User, "Hello", "user"), - new ImageMessage(Role.User, "https://example.com/image.png", "user"), - ], "user"); - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().BeOfType(); - userMessage = (ChatRequestUserMessage)oaiMessages.First(); - userMessage.Content.Should().BeNullOrEmpty(); - userMessage.MultimodalContentItems.Count().Should().Be(2); - userMessage.MultimodalContentItems.First().Should().BeOfType(); - - // assistant text message - message = new TextMessage(Role.Assistant, "How can I help you?", "assistant"); - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().BeOfType(); - var assistantMessage = (ChatRequestAssistantMessage)oaiMessages.First(); - assistantMessage.Content.Should().Be("How can I help you?"); - - // assistant text message with single tool call - message = new ToolCallMessage("test", "test", "assistant"); - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().BeOfType(); - assistantMessage = (ChatRequestAssistantMessage)oaiMessages.First(); - assistantMessage.Content.Should().BeNullOrEmpty(); - assistantMessage.ToolCalls.Count().Should().Be(1); - assistantMessage.ToolCalls.First().Should().BeOfType(); - - // user should not suppose to send tool call message - message = new ToolCallMessage("test", "test", "user"); - Func action = () => middleware.ProcessIncomingMessages(agent, [message]).First(); - action.Should().Throw().WithMessage("ToolCallMessage is not supported when message.From is not the same with agent"); - - // assistant text message with multiple tool calls - message = new ToolCallMessage( - toolCalls: - [ - new ToolCall("test", "test"), - new ToolCall("test", "test"), - ], "assistant"); - - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().BeOfType(); - assistantMessage = (ChatRequestAssistantMessage)oaiMessages.First(); - assistantMessage.Content.Should().BeNullOrEmpty(); - assistantMessage.ToolCalls.Count().Should().Be(2); - - // tool call result message - message = new ToolCallResultMessage("result", "test", "test", "user"); - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().BeOfType(); - var toolCallMessage = (ChatRequestToolMessage)oaiMessages.First(); - toolCallMessage.Content.Should().Be("result"); - - // tool call result message with multiple tool calls - message = new ToolCallResultMessage( - toolCalls: - [ - new ToolCall("result", "test", "test"), - new ToolCall("result", "test", "test"), - ], "user"); - - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(2); - oaiMessages.First().Should().BeOfType(); - toolCallMessage = (ChatRequestToolMessage)oaiMessages.First(); - toolCallMessage.Content.Should().Be("test"); - oaiMessages.Last().Should().BeOfType(); - toolCallMessage = (ChatRequestToolMessage)oaiMessages.Last(); - toolCallMessage.Content.Should().Be("test"); - - // aggregate message test - // aggregate message with tool call and tool call result will be returned by GPT agent if the tool call is automatically invoked inside agent - message = new AggregateMessage( - message1: new ToolCallMessage("test", "test", "assistant"), - message2: new ToolCallResultMessage("result", "test", "test", "assistant"), "assistant"); - - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(2); - oaiMessages.First().Should().BeOfType(); - assistantMessage = (ChatRequestAssistantMessage)oaiMessages.First(); - assistantMessage.Content.Should().BeNullOrEmpty(); - assistantMessage.ToolCalls.Count().Should().Be(1); - - oaiMessages.Last().Should().BeOfType(); - toolCallMessage = (ChatRequestToolMessage)oaiMessages.Last(); - toolCallMessage.Content.Should().Be("result"); - - // aggregate message test 2 - // if the aggregate message is from user, it should be converted to user message - message = new AggregateMessage( - message1: new ToolCallMessage("test", "test", "user"), - message2: new ToolCallResultMessage("result", "test", "test", "user"), "user"); - - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().BeOfType(); - userMessage = (ChatRequestUserMessage)oaiMessages.First(); - userMessage.Content.Should().Be("result"); - - // aggregate message test 3 - // if the aggregate message is from user and contains multiple tool call results, it should be converted to user message - message = new AggregateMessage( - message1: new ToolCallMessage( - toolCalls: - [ - new ToolCall("test", "test"), - new ToolCall("test", "test"), - ], from: "user"), - message2: new ToolCallResultMessage( - toolCalls: - [ - new ToolCall("result", "test", "test"), - new ToolCall("result", "test", "test"), - ], from: "user"), "user"); - - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - oaiMessages.Count().Should().Be(2); - oaiMessages.First().Should().BeOfType(); - oaiMessages.Last().Should().BeOfType(); - - // system message - message = new TextMessage(Role.System, "You are a helpful AI assistant"); - oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().BeOfType(); - } - - [Fact] - public void ToOpenAIChatRequestMessageShortCircuitTest() - { - var agent = new EchoAgent("assistant"); - var middleware = new OpenAIChatRequestMessageConnector(); - ChatRequestMessage[] messages = - [ - new ChatRequestUserMessage("Hello"), - new ChatRequestAssistantMessage("How can I help you?"), - new ChatRequestSystemMessage("You are a helpful AI assistant"), - new ChatRequestFunctionMessage("result", "functionName"), - new ChatRequestToolMessage("test", "test"), - ]; - - foreach (var oaiMessage in messages) - { - IMessage message = new MessageEnvelope(oaiMessage); - var oaiMessages = middleware.ProcessIncomingMessages(agent, [message]); - oaiMessages.Count().Should().Be(1); - oaiMessages.First().Should().Be(oaiMessage); - } - } - private void VerifyOAIMessages(IEnumerable<(IMessage, IEnumerable)> messages) - { - var jsonObjects = messages.Select(pair => - { - var (originalMessage, ms) = pair; - var objs = new List(); - foreach (var m in ms) - { - object? obj = null; - if (m is ChatRequestUserMessage userMessage) - { - obj = new - { - Role = userMessage.Role.ToString(), - Content = userMessage.Content, - MultiModaItem = userMessage.MultimodalContentItems?.Select(item => - { - return item switch - { - ChatMessageImageContentItem imageContentItem => new - { - Type = "Image", - ImageUrl = GetImageUrlFromContent(imageContentItem), - } as object, - ChatMessageTextContentItem textContentItem => new - { - Type = "Text", - Text = textContentItem.Text, - } as object, - _ => throw new System.NotImplementedException(), - }; - }), - }; - } - - if (m is ChatRequestAssistantMessage assistantMessage) - { - obj = new - { - Role = assistantMessage.Role.ToString(), - Content = assistantMessage.Content, - TooCall = assistantMessage.ToolCalls.Select(tc => - { - return tc switch - { - ChatCompletionsFunctionToolCall functionToolCall => new - { - Type = "Function", - Name = functionToolCall.Name, - Arguments = functionToolCall.Arguments, - Id = functionToolCall.Id, - } as object, - _ => throw new System.NotImplementedException(), - }; - }), - FunctionCallName = assistantMessage.FunctionCall?.Name, - FunctionCallArguments = assistantMessage.FunctionCall?.Arguments, - }; - } - - if (m is ChatRequestSystemMessage systemMessage) - { - obj = new - { - Role = systemMessage.Role.ToString(), - Content = systemMessage.Content, - }; - } - - if (m is ChatRequestFunctionMessage functionMessage) - { - obj = new - { - Role = functionMessage.Role.ToString(), - Content = functionMessage.Content, - Name = functionMessage.Name, - }; - } - - if (m is ChatRequestToolMessage toolCallMessage) - { - obj = new - { - Role = toolCallMessage.Role.ToString(), - Content = toolCallMessage.Content, - ToolCallId = toolCallMessage.ToolCallId, - }; - } - - objs.Add(obj ?? throw new System.NotImplementedException()); - } - - return new - { - OriginalMessage = originalMessage.ToString(), - ConvertedMessages = objs, - }; - }); - - var json = JsonSerializer.Serialize(jsonObjects, this.jsonSerializerOptions); - Approvals.Verify(json); - } - - private object? GetImageUrlFromContent(ChatMessageImageContentItem content) - { - return content.GetType().GetProperty("ImageUrl", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.GetValue(content); - } -} diff --git a/dotnet/test/AutoGen.Tests/SingleAgentTest.cs b/dotnet/test/AutoGen.Tests/SingleAgentTest.cs index 79d2b9c2f3f..b784ff8da03 100644 --- a/dotnet/test/AutoGen.Tests/SingleAgentTest.cs +++ b/dotnet/test/AutoGen.Tests/SingleAgentTest.cs @@ -118,7 +118,6 @@ public async Task GPTFunctionCallAgentTestAsync() var agentWithFunction = new GPTAgent("gpt", "You are a helpful AI assistant", config, 0, functions: new[] { this.EchoAsyncFunction }); await EchoFunctionCallTestAsync(agentWithFunction); - await UpperCaseTestAsync(agentWithFunction); } [ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT")] @@ -144,7 +143,6 @@ public async Task AssistantAgentFunctionCallTestAsync() llmConfig: llmConfig); await EchoFunctionCallTestAsync(assistantAgent); - await UpperCaseTestAsync(assistantAgent); } [Fact] @@ -286,7 +284,7 @@ public async Task EchoFunctionCallExecutionTestAsync(IAgent agent) reply.GetContent().Should().Be("[ECHO] Hello world"); reply.From.Should().Be(agent.Name); - reply.Should().BeOfType>(); + reply.Should().BeOfType(); } public async Task EchoFunctionCallExecutionStreamingTestAsync(IStreamingAgent agent) @@ -306,7 +304,7 @@ public async Task EchoFunctionCallExecutionStreamingTestAsync(IStreamingAgent ag finalReply = reply; } - if (finalReply is AggregateMessage aggregateMessage) + if (finalReply is ToolCallAggregateMessage aggregateMessage) { var toolCallResultMessage = aggregateMessage.Message2; toolCallResultMessage.ToolCalls.First().Result.Should().Be(answer); diff --git a/dotnet/test/AutoGen.Tests/TwoAgentTest.cs b/dotnet/test/AutoGen.Tests/TwoAgentTest.cs index 91437eaa618..5c9a350e01b 100644 --- a/dotnet/test/AutoGen.Tests/TwoAgentTest.cs +++ b/dotnet/test/AutoGen.Tests/TwoAgentTest.cs @@ -68,7 +68,7 @@ public async Task TwoAgentWeatherChatTestAsync() else { // terminate message - return new Message(Role.Assistant, GroupChatExtension.TERMINATE); + return new TextMessage(Role.Assistant, GroupChatExtension.TERMINATE); } }) .RegisterMiddleware(async (msgs, option, agent, ct) => diff --git a/dotnet/test/AutoGen.Tests/WorkflowTest.cs b/dotnet/test/AutoGen.Tests/WorkflowTest.cs index d57cf2126c4..d1d12010e39 100644 --- a/dotnet/test/AutoGen.Tests/WorkflowTest.cs +++ b/dotnet/test/AutoGen.Tests/WorkflowTest.cs @@ -30,12 +30,12 @@ public async Task TransitionTestAsync() var canTransit = await aliceToBob.CanTransitionAsync([]); canTransit.Should().BeFalse(); - canTransit = await aliceToBob.CanTransitionAsync(new[] { new Message(Role.Assistant, "Hello") }); + canTransit = await aliceToBob.CanTransitionAsync(new[] { new TextMessage(Role.Assistant, "Hello") }); canTransit.Should().BeTrue(); // if no function is provided, it should always return true var aliceToBobNoFunction = Transition.Create(alice, bob); - canTransit = await aliceToBobNoFunction.CanTransitionAsync(new[] { new Message(Role.Assistant, "Hello") }); + canTransit = await aliceToBobNoFunction.CanTransitionAsync(new[] { new TextMessage(Role.Assistant, "Hello") }); canTransit.Should().BeTrue(); } diff --git a/dotnet/website/articles/Built-in-messages.md b/dotnet/website/articles/Built-in-messages.md index 2767091bd76..3a3754a3058 100644 --- a/dotnet/website/articles/Built-in-messages.md +++ b/dotnet/website/articles/Built-in-messages.md @@ -7,12 +7,15 @@ Besides, AutoGen also provides a set of built-in message types that implement th > [!NOTE] > The minimal requirement for an agent to be used as admin in @AutoGen.Core.GroupChat is to support @AutoGen.Core.TextMessage. +> [!NOTE] +> @AutoGen.Core.Message will be deprecated in 0.0.14. Please replace it with a more specific message type like @AutoGen.Core.TextMessage, @AutoGen.Core.ImageMessage, etc. + - @AutoGen.Core.TextMessage: A message that contains a piece of text. - @AutoGen.Core.ImageMessage: A message that contains an image. - @AutoGen.Core.MultiModalMessage: A message that contains multiple modalities like text, image, etc. - @AutoGen.Core.ToolCallMessage: A message that represents a function call request. - @AutoGen.Core.ToolCallResultMessage: A message that represents a function call result. -- @AutoGen.Core.AggregateMessage`2: A message that represents an aggregate message that contains multiple sub-messages. This type of message is used by @AutoGen.Core.FunctionCallMiddleware to aggregate both @AutoGen.Core.ToolCallMessage and @AutoGen.Core.ToolCallResultMessage into a single message. +- @AutoGen.Core.ToolCallAggregateMessage: A message that contains both @AutoGen.Core.ToolCallMessage and @AutoGen.Core.ToolCallResultMessage. This type of message is used by @AutoGen.Core.FunctionCallMiddleware to aggregate both @AutoGen.Core.ToolCallMessage and @AutoGen.Core.ToolCallResultMessage into a single message. - @AutoGen.Core.MessageEnvelope`1: A message that represents an envelope that contains a message of any type. - @AutoGen.Core.Message: The original message type before 0.0.9. This message type is reserved for backward compatibility. It is recommended to replace it with a more specific message type like @AutoGen.Core.TextMessage, @AutoGen.Core.ImageMessage, etc. diff --git a/dotnet/website/articles/MistralChatAgent-count-token-usage.md b/dotnet/website/articles/MistralChatAgent-count-token-usage.md index b7f025aa11d..261845cf615 100644 --- a/dotnet/website/articles/MistralChatAgent-count-token-usage.md +++ b/dotnet/website/articles/MistralChatAgent-count-token-usage.md @@ -4,7 +4,7 @@ The following example shows how to create a `MistralAITokenCounterMiddleware` @A To collect the token usage for the entire chat session, one easy solution is simply collect all the responses from agent and sum up the token usage for each response. To collect all the agent responses, we can create a middleware which simply saves all responses to a list and register it with the agent. To get the token usage information for each response, because in the example we are using @AutoGen.Mistral.MistralClientAgent, we can simply get the token usage from the response object. > [!NOTE] -> You can find the complete example in the [Example13_OpenAIAgent_JsonMode](https://github.com/microsoft/autogen/tree/dotnet/dotnet/sample/AutoGen.BasicSamples/Example14_MistralClientAgent_TokenCount.cs). +> You can find the complete example in the [Example13_OpenAIAgent_JsonMode](https://github.com/microsoft/autogen/tree/main/dotnet/sample/AutoGen.BasicSamples/Example14_MistralClientAgent_TokenCount.cs). - Step 1: Adding using statement [!code-csharp[](../../sample/AutoGen.BasicSamples/Example14_MistralClientAgent_TokenCount.cs?name=using_statements)] diff --git a/dotnet/website/articles/OpenAIChatAgent-connect-to-third-party-api.md b/dotnet/website/articles/OpenAIChatAgent-connect-to-third-party-api.md index 2072c0f99a8..8321fc87a5c 100644 --- a/dotnet/website/articles/OpenAIChatAgent-connect-to-third-party-api.md +++ b/dotnet/website/articles/OpenAIChatAgent-connect-to-third-party-api.md @@ -1,7 +1,7 @@ The following example shows how to connect to third-party OpenAI API using @AutoGen.OpenAI.OpenAIChatAgent. > [!NOTE] -> You can find the complete code of this example in [Example16_OpenAIChatAgent_ConnectToThirdPartyBackend](https://github.com/microsoft/autogen/tree/dotnet/dotnet/sample/AutoGen.BasicSamples/Example16_OpenAIChatAgent_ConnectToThirdPartyBackend.cs). +> You can find the complete code of this example in [Example16_OpenAIChatAgent_ConnectToThirdPartyBackend](https://github.com/microsoft/autogen/tree/main/dotnet/sample/AutoGen.BasicSamples/Example16_OpenAIChatAgent_ConnectToThirdPartyBackend.cs). ## Overview A lot of LLM applications/platforms support spinning up a chat server that is compatible with OpenAI API, such as LM Studio, Ollama, Mistral etc. This means that you can connect to these servers using the @AutoGen.OpenAI.OpenAIChatAgent. diff --git a/dotnet/website/articles/OpenAIChatAgent-use-json-mode.md b/dotnet/website/articles/OpenAIChatAgent-use-json-mode.md index 4d69340f585..a822cb04633 100644 --- a/dotnet/website/articles/OpenAIChatAgent-use-json-mode.md +++ b/dotnet/website/articles/OpenAIChatAgent-use-json-mode.md @@ -9,7 +9,7 @@ JSON mode is a new feature in OpenAI which allows you to instruct model to alway ## How to enable JSON mode in OpenAIChatAgent. > [!NOTE] -> You can find the complete example in the [Example13_OpenAIAgent_JsonMode](https://github.com/microsoft/autogen/tree/dotnet/dotnet/sample/AutoGen.BasicSamples/Example13_OpenAIAgent_JsonMode.cs). +> You can find the complete example in the [Example13_OpenAIAgent_JsonMode](https://github.com/microsoft/autogen/tree/main/dotnet/sample/AutoGen.BasicSamples/Example13_OpenAIAgent_JsonMode.cs). To enable JSON mode for @AutoGen.OpenAI.OpenAIChatAgent, set `responseFormat` to `ChatCompletionsResponseFormat.JsonObject` when creating the agent. Note that when enabling JSON mode, you also need to instruct the agent to output JSON format in its system message. diff --git a/notebook/agentchat_agentoptimizer.ipynb b/notebook/agentchat_agentoptimizer.ipynb index 13df0a0d77a..7177703ab06 100644 --- a/notebook/agentchat_agentoptimizer.ipynb +++ b/notebook/agentchat_agentoptimizer.ipynb @@ -53,7 +53,7 @@ "source": [ "# MathUserProxy with function_call\n", "\n", - "This agent is a customized MathUserProxy inherits from its [partent class](https://github.com/microsoft/autogen/blob/main/autogen/agentchat/contrib/math_user_proxy_agent.py).\n", + "This agent is a customized MathUserProxy inherits from its [parent class](https://github.com/microsoft/autogen/blob/main/autogen/agentchat/contrib/math_user_proxy_agent.py).\n", "\n", "It supports using both function_call and python to solve math problems.\n" ] diff --git a/notebook/agentchat_capability_long_context_handling.ipynb b/notebook/agentchat_capability_long_context_handling.ipynb index 0bc1b4ffdd7..0a9d715e3e5 100644 --- a/notebook/agentchat_capability_long_context_handling.ipynb +++ b/notebook/agentchat_capability_long_context_handling.ipynb @@ -6,8 +6,8 @@ "source": [ "# Handling A Long Context via `TransformChatHistory`\n", "\n", - "
\n", - " Deprecation Notice: TransformChatHistory is no longer supported. Please use TransformMessages as the new standard method. For the latest examples, visit the notebook at notebook/agentchat_transform_messages.ipynb.\n", + "
\n", + " Deprecation Notice: TransformChatHistory is no longer supported and will be removed in version 0.2.30. Please transition to using TransformMessages as the new standard method. For a detailed introduction to this method, including how to limit the number of tokens in message context history to replace TransformChatHistory, visit our guide Introduction to Transform Messages.\n", "
\n", "\n", "This notebook illustrates how you can use the `TransformChatHistory` capability to give any `Conversable` agent an ability to handle a long context. \n", @@ -665,7 +665,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -679,9 +679,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.11.9" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/notebook/agentchat_compression.ipynb b/notebook/agentchat_compression.ipynb index 29cc2d9e224..d7f3a7204db 100644 --- a/notebook/agentchat_compression.ipynb +++ b/notebook/agentchat_compression.ipynb @@ -6,9 +6,9 @@ "source": [ "# Conversations with Chat History Compression Enabled\n", "\n", - "**CompressibleAgent will be deprecated.** \n", - "\n", - "Refer to https://github.com/microsoft/autogen/blob/main/notebook/agentchat_capability_long_context_handling.ipynb for long context handling capability.\n", + "
\n", + " Deprecation Notice: CompressibleAgent has been deprecated and will no longer be available as of version 0.2.30. Please transition to using TransformMessages, which is now the recommended approach. For a detailed guide on implementing this new standard, refer to our user guide on Compressing Text with LLMLingua. This guide provides examples for effectively utilizing LLMLingua transform as a replacement for CompressibleAgent.\n", + "
\n", "\n", "AutoGen offers conversable agents powered by LLM, tools, or humans, which can be used to perform tasks collectively via automated chat. This framework allows tool use and human participance through multi-agent conversation. Please find documentation about this feature [here](https://microsoft.github.io/autogen/docs/Use-Cases/agent_chat).\n", "\n", @@ -854,7 +854,7 @@ ], "metadata": { "kernelspec": { - "display_name": "msft", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -868,10 +868,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.13" - }, - "orig_nbformat": 4 + "version": "3.11.9" + } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/notebook/agentchat_logging.ipynb b/notebook/agentchat_logging.ipynb index 2ad19e7995a..7eb4138b4cc 100644 --- a/notebook/agentchat_logging.ipynb +++ b/notebook/agentchat_logging.ipynb @@ -8,6 +8,10 @@ "\n", "AutoGen offers utilities to log data for debugging and performance analysis. This notebook demonstrates how to use them. \n", "\n", + "we log data in different modes:\n", + "- SQlite Database\n", + "- File \n", + "\n", "In general, users can initiate logging by calling `autogen.runtime_logging.start()` and stop logging by calling `autogen.runtime_logging.stop()`" ] }, @@ -287,6 +291,82 @@ " + str(round(session_cost, 4))\n", ")" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Log data in File mode\n", + "\n", + "By default, the log type is set to `sqlite` as shown above, but we introduced a new parameter for the `autogen.runtime_logging.start()`\n", + "\n", + "the `logger_type = \"file\"` will start to log data in the File mode." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Logging session ID: ed493ebf-d78e-49f0-b832-69557276d557\n", + "\u001b[33muser_proxy\u001b[0m (to assistant):\n", + "\n", + "What is the height of the Eiffel Tower? Only respond with the answer and terminate\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to user_proxy):\n", + "\n", + "The height of the Eiffel Tower is 330 meters.\n", + "TERMINATE\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "\n", + "import pandas as pd\n", + "\n", + "import autogen\n", + "from autogen import AssistantAgent, UserProxyAgent\n", + "\n", + "# Setup API key. Add your own API key to config file or environment variable\n", + "llm_config = {\n", + " \"config_list\": autogen.config_list_from_json(\n", + " env_or_file=\"OAI_CONFIG_LIST\",\n", + " ),\n", + " \"temperature\": 0.9,\n", + "}\n", + "\n", + "# Start logging with logger_type and the filename to log to\n", + "logging_session_id = autogen.runtime_logging.start(logger_type=\"file\", config={\"filename\": \"runtime.log\"})\n", + "print(\"Logging session ID: \" + str(logging_session_id))\n", + "\n", + "# Create an agent workflow and run it\n", + "assistant = AssistantAgent(name=\"assistant\", llm_config=llm_config)\n", + "user_proxy = UserProxyAgent(\n", + " name=\"user_proxy\",\n", + " code_execution_config=False,\n", + " human_input_mode=\"NEVER\",\n", + " is_termination_msg=lambda msg: \"TERMINATE\" in msg[\"content\"],\n", + ")\n", + "\n", + "user_proxy.initiate_chat(\n", + " assistant, message=\"What is the height of the Eiffel Tower? Only respond with the answer and terminate\"\n", + ")\n", + "autogen.runtime_logging.stop()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This should create a `runtime.log` file in your current directory. " + ] } ], "metadata": { @@ -312,7 +392,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.9.13" } }, "nbformat": 4, diff --git a/notebook/agenteval_cq_math.ipynb b/notebook/agenteval_cq_math.ipynb index 71a19b044a7..43ea28de1a3 100644 --- a/notebook/agenteval_cq_math.ipynb +++ b/notebook/agenteval_cq_math.ipynb @@ -17,12 +17,12 @@ "source": [ "# Demonstrating the `AgentEval` framework using the task of solving math problems as an example\n", "\n", - "This notebook aims to demonstrate how to `AgentEval` implemented through [AutoGen](https://github.com/microsoft/autogen) works, where we use a math problem-solving task as an example. \n", - "`AgentEval` consists of two key components:\n", + "This notebook aims to demonstrate how to `AgentEval` implemented through [AutoGen](https://github.com/microsoft/autogen) works in an offline scenario, where we use a math problem-solving task as an example. \n", + "`AgentEval` consists of two key steps:\n", "\n", - "- `CriticAgent`: This is an LLM-based agent that generates a list criteria $(c_1, \\dots, c_n)$ to help to evaluate a utility given task.\n", + "- `generate_criteria`: This is an LLM-based function that generates a list of criteria $(c_1, \\dots, c_n)$ to help to evaluate a utility given task.\n", "\n", - "- `QuantifierAgent`: This agent quantifies the performance of any sample task based on the criteria designed by the `CriticAgent` in the following way: $(c_1=a_1, \\dots, c_n=a_n)$\n", + "- `quantify_criteria`: This function quantifies the performance of any sample task based on the criteria generated in the `generate_criteria` step in the following way: $(c_1=a_1, \\dots, c_n=a_n)$\n", "\n", "![AgentEval](../website/blog/2023-11-20-AgentEval/img/agenteval-CQ.png)\n", "\n", @@ -49,7 +49,70 @@ "id": "68lTZZyJ1_BI", "outputId": "15a55fab-e13a-4654-b8cb-ae117478d6d8" }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: pyautogen>=0.2.3 in /home/vscode/.local/lib/python3.10/site-packages (0.2.17)\n", + "Requirement already satisfied: docker in /home/vscode/.local/lib/python3.10/site-packages (7.0.0)\n", + "Requirement already satisfied: diskcache in /home/vscode/.local/lib/python3.10/site-packages (from pyautogen>=0.2.3) (5.6.3)\n", + "Requirement already satisfied: flaml in /home/vscode/.local/lib/python3.10/site-packages (from pyautogen>=0.2.3) (2.1.2)\n", + "Requirement already satisfied: tiktoken in /home/vscode/.local/lib/python3.10/site-packages (from pyautogen>=0.2.3) (0.6.0)\n", + "Requirement already satisfied: openai>=1.3 in /home/vscode/.local/lib/python3.10/site-packages (from pyautogen>=0.2.3) (1.14.1)\n", + "Requirement already satisfied: pydantic!=2.6.0,<3,>=1.10 in /home/vscode/.local/lib/python3.10/site-packages (from pyautogen>=0.2.3) (2.6.4)\n", + "Requirement already satisfied: termcolor in /home/vscode/.local/lib/python3.10/site-packages (from pyautogen>=0.2.3) (2.4.0)\n", + "Requirement already satisfied: python-dotenv in /home/vscode/.local/lib/python3.10/site-packages (from pyautogen>=0.2.3) (1.0.1)\n", + "Requirement already satisfied: requests>=2.26.0 in /usr/local/lib/python3.10/site-packages (from docker) (2.31.0)\n", + "Requirement already satisfied: packaging>=14.0 in /usr/local/lib/python3.10/site-packages (from docker) (24.0)\n", + "Requirement already satisfied: urllib3>=1.26.0 in /usr/local/lib/python3.10/site-packages (from docker) (2.2.1)\n", + "Requirement already satisfied: tqdm>4 in /home/vscode/.local/lib/python3.10/site-packages (from openai>=1.3->pyautogen>=0.2.3) (4.66.2)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /home/vscode/.local/lib/python3.10/site-packages (from openai>=1.3->pyautogen>=0.2.3) (0.27.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /home/vscode/.local/lib/python3.10/site-packages (from openai>=1.3->pyautogen>=0.2.3) (1.9.0)\n", + "Requirement already satisfied: sniffio in /home/vscode/.local/lib/python3.10/site-packages (from openai>=1.3->pyautogen>=0.2.3) (1.3.1)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /home/vscode/.local/lib/python3.10/site-packages (from openai>=1.3->pyautogen>=0.2.3) (4.3.0)\n", + "Requirement already satisfied: typing-extensions<5,>=4.7 in /home/vscode/.local/lib/python3.10/site-packages (from openai>=1.3->pyautogen>=0.2.3) (4.10.0)\n", + "Requirement already satisfied: annotated-types>=0.4.0 in /home/vscode/.local/lib/python3.10/site-packages (from pydantic!=2.6.0,<3,>=1.10->pyautogen>=0.2.3) (0.6.0)\n", + "Requirement already satisfied: pydantic-core==2.16.3 in /home/vscode/.local/lib/python3.10/site-packages (from pydantic!=2.6.0,<3,>=1.10->pyautogen>=0.2.3) (2.16.3)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/site-packages (from requests>=2.26.0->docker) (2024.2.2)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/site-packages (from requests>=2.26.0->docker) (3.6)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/site-packages (from requests>=2.26.0->docker) (3.3.2)\n", + "Requirement already satisfied: NumPy>=1.17 in /home/vscode/.local/lib/python3.10/site-packages (from flaml->pyautogen>=0.2.3) (1.26.4)\n", + "Requirement already satisfied: regex>=2022.1.18 in /home/vscode/.local/lib/python3.10/site-packages (from tiktoken->pyautogen>=0.2.3) (2023.12.25)\n", + "Requirement already satisfied: exceptiongroup>=1.0.2 in /home/vscode/.local/lib/python3.10/site-packages (from anyio<5,>=3.5.0->openai>=1.3->pyautogen>=0.2.3) (1.2.0)\n", + "Requirement already satisfied: httpcore==1.* in /home/vscode/.local/lib/python3.10/site-packages (from httpx<1,>=0.23.0->openai>=1.3->pyautogen>=0.2.3) (1.0.4)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /home/vscode/.local/lib/python3.10/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai>=1.3->pyautogen>=0.2.3) (0.14.0)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: scipy in /home/vscode/.local/lib/python3.10/site-packages (1.12.0)\n", + "Requirement already satisfied: numpy<1.29.0,>=1.22.4 in /home/vscode/.local/lib/python3.10/site-packages (from scipy) (1.26.4)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: matplotlib in /home/vscode/.local/lib/python3.10/site-packages (3.8.3)\n", + "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/site-packages (from matplotlib) (24.0)\n", + "Requirement already satisfied: pyparsing>=2.3.1 in /home/vscode/.local/lib/python3.10/site-packages (from matplotlib) (3.1.2)\n", + "Requirement already satisfied: contourpy>=1.0.1 in /home/vscode/.local/lib/python3.10/site-packages (from matplotlib) (1.2.0)\n", + "Requirement already satisfied: fonttools>=4.22.0 in /home/vscode/.local/lib/python3.10/site-packages (from matplotlib) (4.50.0)\n", + "Requirement already satisfied: python-dateutil>=2.7 in /home/vscode/.local/lib/python3.10/site-packages (from matplotlib) (2.9.0.post0)\n", + "Requirement already satisfied: cycler>=0.10 in /home/vscode/.local/lib/python3.10/site-packages (from matplotlib) (0.12.1)\n", + "Requirement already satisfied: pillow>=8 in /home/vscode/.local/lib/python3.10/site-packages (from matplotlib) (10.2.0)\n", + "Requirement already satisfied: numpy<2,>=1.21 in /home/vscode/.local/lib/python3.10/site-packages (from matplotlib) (1.26.4)\n", + "Requirement already satisfied: kiwisolver>=1.3.1 in /home/vscode/.local/lib/python3.10/site-packages (from matplotlib) (1.4.5)\n", + "Requirement already satisfied: six>=1.5 in /home/vscode/.local/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], "source": [ "%pip install \"pyautogen>=0.2.3\" docker\n", "%pip install scipy\n", @@ -63,11 +126,6 @@ }, "source": [ "## Set your API Endpoint\n", - "\n", - "* The [`config_list_openai_aoai`](https://microsoft.github.io/autogen/docs/reference/oai/openai_utils#config_list_openai_aoai) function tries to create a list of configurations using Azure OpenAI endpoints and OpenAI endpoints. It assumes the api keys and api bases are stored in the corresponding environment variables or local txt files:\n", - " - OpenAI API key: os.environ[\"OPENAI_API_KEY\"] or `openai_api_key_file=\"key_openai.txt\"`.\n", - " - Azure OpenAI API key: os.environ[\"AZURE_OPENAI_API_KEY\"] or `aoai_api_key_file=\"key_aoai.txt\"`. Multiple keys can be stored, one per line.\n", - " - Azure OpenAI API base: os.environ[\"AZURE_OPENAI_API_BASE\"] or `aoai_api_base_file=\"base_aoai.txt\"`. Multiple bases can be stored, one per line.\n", "* The [`config_list_from_json`](https://microsoft.github.io/autogen/docs/reference/oai/openai_utils#config_list_from_json) function loads a list of configurations from an environment variable or a json file. It first looks for an environment variable with a specified name. The value of the environment variable needs to be a valid json string. If that variable is not found, it looks for a json file with the same name. It filters the configs by filter_dict.\n", "\n", "You can set the value of config_list in any way you prefer. Please refer to this [notebook](https://github.com/microsoft/autogen/blob/main/notebook/oai_openai_utils.ipynb) for full code examples of the different methods.\n" @@ -90,68 +148,11 @@ "import scipy.stats as stats\n", "\n", "import autogen\n", + "from autogen.agentchat.contrib.agent_eval.agent_eval import generate_criteria, quantify_criteria\n", + "from autogen.agentchat.contrib.agent_eval.criterion import Criterion\n", + "from autogen.agentchat.contrib.agent_eval.task import Task\n", "\n", - "config_list = autogen.config_list_from_json(\n", - " \"OAI_CONFIG_LIST\",\n", - " filter_dict={\n", - " \"model\": [\"gpt-4\"],\n", - " },\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fBZ-XFXy1_BJ" - }, - "source": [ - "\n", - "## Construct `CriticAgent`\n", - "\n", - "We construct the planning agent named `critic` and a user proxy agent for the critic named `critic_user`. We specify `human_input_mode` as \"NEVER\" in the user proxy agent, ensuring that it will never ask for human feedback. Additionally, we define the `ask_critic` function to send a message to the critic and retrieve the criteria from the critic.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "9XAeyjd11_BK" - }, - "outputs": [], - "source": [ - "critic = autogen.AssistantAgent(\n", - " name=\"critic\",\n", - " llm_config={\"config_list\": config_list},\n", - " system_message=\"\"\"You are a helpful assistant. You suggest criteria for evaluating different tasks. They should be dinstinguishable, quantifieable and not redundant.\n", - " Convert the evaluation criteria into a dictionary where the keys are the criteria.\n", - " The value of each key is a dictionary as follows {\"description\": criteria description , \"accepted_values\": possible accepted inputs for this key}\n", - " Make sure the keys are criteria for assessing the given task. \"accepted_values\" include the acceptable inputs for each key that are fine-grained and preferably multi-graded levels. \"description\" includes the criterion description.\n", - " Return the dictionary.\"\"\",\n", - ")\n", - "\n", - "critic_user = autogen.UserProxyAgent(\n", - " name=\"critic_user\",\n", - " max_consecutive_auto_reply=0, # terminate without auto-reply\n", - " human_input_mode=\"NEVER\",\n", - " code_execution_config={\n", - " \"use_docker\": False\n", - " }, # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly.\n", - ")\n", - "\n", - "\n", - "def ask_critic(message):\n", - " \"\"\"\n", - " Initiate a chat with the critic user and return the last message received from the planner.\n", - "\n", - " Args:\n", - " - message (str): The message to be sent to the critic user.\n", - "\n", - " Returns:\n", - " - str: The content of the last message received.\n", - " \"\"\"\n", - " critic_user.initiate_chat(critic, message=message)\n", - " # return the last received from the planner\n", - " return critic_user.messagelast_message()[\"content\"]" + "config_list = autogen.config_list_from_json(\"OAI_CONFIG_LIST\")" ] }, { @@ -167,207 +168,138 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": { "id": "5H1WRs_wkiK0" }, - "outputs": [], - "source": [ - "def read_without_groundtruth(file_name):\n", - " \"\"\"\n", - " Read the mathproblem logs - bypassing any information about the ground truths.\n", - "\n", - " Args:\n", - " - file_name (str): The single log file that wants to get evaluated.\n", - "\n", - " Returns:\n", - " - str: The log file without any information about the ground truth answer of the problem.\n", - " \"\"\"\n", - " f = open(file_name, \"r\").readlines()\n", - " output_dictionary = \"\"\n", - " for line in f:\n", - " if \"is_correct\" not in line and \"correct_ans\" not in line and \"check_result\" not in line:\n", - " output_dictionary += line\n", - " elif \"is_correct\" in line:\n", - " correctness = line.replace(\",\", \"\").split(\":\")[-1].rstrip().strip()\n", - " return [output_dictionary, correctness]\n", - "\n", - "\n", - "# Reading one successful and one failed example of the task\n", - "response_successful = read_without_groundtruth(\n", - " \"../test/test_files/agenteval-in-out/samples/sample_math_response_successful.txt\"\n", - ")[0]\n", - "response_failed = read_without_groundtruth(\n", - " \"../test/test_files/agenteval-in-out/samples/sample_math_response_failed.txt\"\n", - ")[0]\n", - "\n", - "task = {\n", - " \"name\": \"Math problem solving\",\n", - " \"description\": \"Given any question, the system needs to solve the problem as consisely and accurately as possible\",\n", - " \"successful_response\": response_successful,\n", - " \"failed_response\": response_failed,\n", - "}\n", - "\n", - "sys_msg = f\"\"\"Task: {task[\"name\"]}.\n", - "Task description: {task[\"description\"]}\n", - "Task successful example: {task[\"successful_response\"]}\n", - "Task failed example: {task[\"failed_response\"]}\n", - "\"\"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Vu70o024lenI" - }, - "source": [ - "# The Criteria\n", - "Now, we print the designed criteria for assessing math problems. " - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "k9DsDB5hqvtG", - "outputId": "0edd7a0c-b031-4f67-efc6-1a1e77066921" - }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\u001b[33mcritic_user\u001b[0m (to critic):\n", + "\u001b[33mcritic_user\u001b[0m (to chat_manager):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " \n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", + "\u001b[33mcritic\u001b[0m (to chat_manager):\n", + "\n", + "[\n", " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", + " \"name\": \"Accuracy\",\n", + " \"description\": \"The solution must be correct and adhere strictly to mathematical principles and techniques appropriate for the problem.\",\n", + " \"accepted_values\": [\"Correct\", \"Minor errors\", \"Major errors\", \"Incorrect\"]\n", " },\n", " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", + " \"name\": \"Relevance\",\n", + " \"description\": \"The content of the response must be relevant to the question posed and should address the specific problem requirements.\",\n", + " \"accepted_values\": [\"Highly relevant\", \"Relevant\", \"Somewhat relevant\", \"Not relevant\"]\n", " },\n", " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", + " \"name\": \"Efficiency\",\n", + " \"description\": \"The solution should be derived in a time-effective manner, considering the complexity of the problem.\",\n", + " \"accepted_values\": [\"Highly efficient\", \"Efficient\", \"Inefficient\", \"Redundant\"]\n", " },\n", " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[33mcritic\u001b[0m (to critic_user):\n", - "\n", - "In evaluating math problem-solving tasks, we can establish certain criteria to assess the level of success in solving the math problems. Below are the criteria with their corresponding descriptions and the accepted values:\n", - "\n", - "```python\n", - "evaluation_criteria = {\n", - " \"accuracy\": {\n", - " \"description\": \"Correctness of the final answer provided.\",\n", - " \"accepted_values\": {\n", - " \"correct\": \"The given answer is correct.\",\n", - " \"incorrect\": \"The given answer is incorrect.\",\n", - " \"partial\": \"The answer is partially correct with minor errors.\"\n", - " }\n", + " \"name\": \"Logic and Structure\",\n", + " \"description\": \"The reasoning should be logical and the information structured in a clear and understandable sequence.\",\n", + " \"accepted_values\": [\"Exceptionally clear\", \"Clear\", \"Somewhat clear\", \"Confusing\"]\n", " },\n", - " \"completeness\": {\n", - " \"description\": \"The extent to which all necessary steps are included and properly documented.\",\n", - " \"accepted_values\": {\n", - " \"complete\": \"All necessary steps are included and properly documented.\",\n", - " \"incomplete\": \"Some steps are missing or not properly documented.\",\n", - " \"overly_detailed\": \"The solution contains unnecessary detail that doesn't contribute to understanding.\"\n", - " }\n", + " {\n", + " \"name\": \"Use of Resources\",\n", + " \"description\": \"The response should make appropriate and optimal use of external resources or tools (e.g., Python scripts) when necessary.\",\n", + " \"accepted_values\": [\"Optimal\", \"Appropriate\", \"Underutilized\", \"Overreliance\"]\n", " },\n", - " \"efficiency\": {\n", - " \"description\": \"The method used to solve the problem is concise and does not include redundant steps.\",\n", - " \"accepted_values\": {\n", - " \"efficient\": \"The solution is found through the most direct method with no superfluous steps.\",\n", - " \"inefficient\": \"The method used is not the most direct and may include redundant steps.\",\n", - " \"acceptable\": \"The method used is reasonably direct with little redundancy.\"\n", - " }\n", + " {\n", + " \"name\": \"Mathematical Notation\",\n", + " \"description\": \"The use of proper and standard mathematical notation in the solution and explanation.\",\n", + " \"accepted_values\": [\"Excellent\", \"Good\", \"Adequate\", \"Poor\"]\n", " },\n", - " \"methodology\": {\n", - " \"description\": \"The approach used to solve the problem, including the use of formulas, theorems, and problem-solving techniques.\",\n", - " \"accepted_values\": {\n", - " \"appropriate\": \"The methodology used is appropriate for the problem.\",\n", - " \"inappropriate\": \"The methodology used is not suitable for the problem.\",\n", - " \"partially_appropriate\": \"The methodology used is partially suitable but could be improved.\"\n", - " }\n", + " {\n", + " \"name\": \"Explanation and Justification\",\n", + " \"description\": \"There should be a clear explanation, rationale, or justification for each step taken towards the solution.\",\n", + " \"accepted_values\": [\"Thorough\", \"Adequate\", \"Insufficient\", \"Missing\"]\n", " },\n", - " \"clarity\": {\n", - " \"description\": \"The ease with which the solution can be understood by others.\",\n", - " \"accepted_values\": {\n", - " \"clear\": \"The solution is presented in a clear, logical manner that is easy to follow.\",\n", - " \"unclear\": \"The solution is difficult to follow or understand.\",\n", - " \"somewhat_clear\": \"The solution is generally clear but could be improved in some areas for better understanding.\"\n", - " }\n", + " {\n", + " \"name\": \"Correctness of Answer Format\",\n", + " \"description\": \"The answer should be presented in the format requested in the problem (e.g., interval notation, simplified form).\",\n", + " \"accepted_values\": [\"Perfectly formatted\", \"Properly formatted\", \"Slightly incorrect format\", \"Improperly formatted\"]\n", " },\n", - " \"use_of_language\": {\n", - " \"description\": \"The correctness and appropriateness of mathematical language and notation.\",\n", - " \"accepted_values\": {\n", - " \"appropriate\": \"The language and notation are mathematically sound and correctly applied.\",\n", - " \"inappropriate\": \"The language and notation have errors or are misapplied.\",\n", - " \"mostly_appropriate\": \"The language and notation are mostly correct, but there are minor errors or inconsistencies.\"\n", - " }\n", + " {\n", + " \"name\": \"Handling of Edge Cases\",\n", + " \"description\": \"The solution should correctly handle any special or edge cases that may arise in the problem.\",\n", + " \"accepted_values\": [\"Complete\", \"Most cases\", \"Some cases\", \"No consideration\"]\n", " }\n", - "}\n", - "```\n", - "\n", - "These criteria should provide a comprehensive framework for evaluating math problem-solving tasks in terms of accuracy, completeness, efficiency, and clarity.\n", + "]\n", "\n", "--------------------------------------------------------------------------------\n" ] } ], "source": [ - "current_task_name = \"_\".join(task[\"name\"].split()).lower()\n", - "gen_criteria = critic_user.initiate_chat(critic, message=sys_msg)\n", - "criteria = critic_user.last_message()\n", + "def remove_ground_truth(test_case):\n", + " test_details = json.loads(test_case)\n", + " # need to remove the ground truth from the test details\n", + " correctness = test_details.pop(\"is_correct\", None)\n", + " test_details.pop(\"correct_ans\", None)\n", + " test_details.pop(\"check_result\", None)\n", + " return str(test_details), correctness\n", + "\n", + "\n", + "# Reading one successful and one failed example of the task\n", + "success_str = open(\"../test/test_files/agenteval-in-out/samples/sample_math_response_successful.txt\", \"r\").read()\n", + "response_successful = remove_ground_truth(success_str)[0]\n", + "failed_str = open(\"../test/test_files/agenteval-in-out/samples/sample_math_response_failed.txt\", \"r\").read()\n", + "response_failed = remove_ground_truth(failed_str)[0]\n", + "\n", + "task = Task(\n", + " **{\n", + " \"name\": \"Math problem solving\",\n", + " \"description\": \"Given any question, the system needs to solve the problem as consisely and accurately as possible\",\n", + " \"successful_response\": response_successful,\n", + " \"failed_response\": response_failed,\n", + " }\n", + ")\n", + "\n", + "criteria = generate_criteria(task=task, llm_config={\"config_list\": config_list}, max_round=8)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Vu70o024lenI" + }, + "source": [ + "# The Criteria\n", + "Now, we print the designed criteria for assessing math problems. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "k9DsDB5hqvtG", + "outputId": "0edd7a0c-b031-4f67-efc6-1a1e77066921" + }, + "outputs": [], + "source": [ + "current_task_name = \"_\".join(task.name.split()).lower()\n", "cr_file = open(f\"../test/test_files/agenteval-in-out/{current_task_name}_criteria.json\", \"w\")\n", - "cr_file.write(criteria[\"content\"])\n", + "cr_file.write(Criterion.write_json(criteria))\n", "cr_file.close()" ] }, @@ -377,7 +309,7 @@ "id": "PETPZluOEGCR" }, "source": [ - "*Note :* You can also define and use your own criteria by editing `criteria.txt`" + "*Note :* You can also define and use your own criteria in order to feed into the quantifier." ] }, { @@ -388,40 +320,21 @@ "source": [ "# The `QuantifierAgent`\n", "\n", - "Once we have the criteria, we need to quantify a new sample based on the designed criteria and its accepted values. This will be done through `QuantifierAgent` agent as follows. \n", - "We note that can skip the designed criteria by the agent and use your own defined criteria in `criteria_file`." + "Once we have the criteria, we need to quantify a new sample based on the designed criteria and its accepted values. This will be done through `quantify_criteria` from agent_eval. \n", + "Again, you can use your own defined criteria in `criteria_file`." ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": { "id": "4uUkZJh_subA" }, "outputs": [], "source": [ "criteria_file = f\"../test/test_files/agenteval-in-out/{current_task_name}_criteria.json\"\n", - "quantifier = autogen.AssistantAgent(\n", - " name=\"quantifier\",\n", - " llm_config={\"config_list\": config_list},\n", - " system_message=\"\"\"You are a helpful assistant. You quantify the output of different tasks based on the given criteria.\n", - " The criterion is given in a dictionary format where each key is a dintinct criteria.\n", - " The value of each key is a dictionary as follows {\"description\": criteria description , \"accepted_values\": possible accepted inputs for this key}\n", - " You are going to quantify each of the crieria for a given task based on the task description.\n", - " Return a dictionary where the keys are the criteria and the values are the assessed performance based on accepted values for each criteria.\n", - " Return only the dictionary.\"\"\",\n", - ")\n", - "\n", - "quantifier_user = autogen.UserProxyAgent(\n", - " name=\"quantifier_user\",\n", - " max_consecutive_auto_reply=0, # terminate without auto-reply\n", - " human_input_mode=\"NEVER\",\n", - " code_execution_config={\n", - " \"use_docker\": False\n", - " }, # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly.\n", - ")\n", - "\n", - "dictionary_for_eval = open(criteria_file, \"r\").read()" + "criteria = open(criteria_file, \"r\").read()\n", + "criteria = Criterion.parse_json_str(criteria)" ] }, { @@ -433,41 +346,6 @@ "## Running the quantifier on a single test case" ] }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "id": "zQ0H3sy8l-Ai" - }, - "outputs": [], - "source": [ - "def get_quantifier(file, criteria_file):\n", - " \"\"\"\n", - " Running quantifier agent on individual log.\n", - "\n", - " Args:\n", - " - file (str): The log path.\n", - " - file (str): The criteria jason file path\n", - " Returns:\n", - " - dict: A dictionary including the actual success of the problem as well as estimated performance by the agent eval.\n", - " {\"actual_success\":actual_label, \"estimated_performance\" : a dictionary of all the criteria and their quantified estimated performance.} }\n", - " \"\"\"\n", - " dictionary_for_eval = open(criteria_file, \"r\").read()\n", - "\n", - " test_case, actual_label = read_without_groundtruth(file)\n", - " print(\"actual label for this case: \", actual_label)\n", - " cq_results = quantifier_user.initiate_chat( # noqa: F841\n", - " quantifier,\n", - " message=sys_msg\n", - " + \"Evaluation dictionary: \"\n", - " + str(dictionary_for_eval)\n", - " + \"actual test case to evaluate: \"\n", - " + test_case,\n", - " )\n", - " quantified_results = quantifier_user.last_message()\n", - " return {\"actual_success\": actual_label, \"estimated_performance\": quantified_results[\"content\"]}" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -477,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -490,176 +368,173 @@ "name": "stdout", "output_type": "stream", "text": [ - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: In evaluating math problem-solving tasks, we can establish certain criteria to assess the level of success in solving the math problems. Below are the criteria with their corresponding descriptions and the accepted values:\n", - "\n", - "```python\n", - "evaluation_criteria = {\n", - " \"accuracy\": {\n", - " \"description\": \"Correctness of the final answer provided.\",\n", - " \"accepted_values\": {\n", - " \"correct\": \"The given answer is correct.\",\n", - " \"incorrect\": \"The given answer is incorrect.\",\n", - " \"partial\": \"The answer is partially correct with minor errors.\"\n", - " }\n", - " },\n", - " \"completeness\": {\n", - " \"description\": \"The extent to which all necessary steps are included and properly documented.\",\n", - " \"accepted_values\": {\n", - " \"complete\": \"All necessary steps are included and properly documented.\",\n", - " \"incomplete\": \"Some steps are missing or not properly documented.\",\n", - " \"overly_detailed\": \"The solution contains unnecessary detail that doesn't contribute to understanding.\"\n", - " }\n", - " },\n", - " \"efficiency\": {\n", - " \"description\": \"The method used to solve the problem is concise and does not include redundant steps.\",\n", - " \"accepted_values\": {\n", - " \"efficient\": \"The solution is found through the most direct method with no superfluous steps.\",\n", - " \"inefficient\": \"The method used is not the most direct and may include redundant steps.\",\n", - " \"acceptable\": \"The method used is reasonably direct with little redundancy.\"\n", - " }\n", - " },\n", - " \"methodology\": {\n", - " \"description\": \"The approach used to solve the problem, including the use of formulas, theorems, and problem-solving techniques.\",\n", - " \"accepted_values\": {\n", - " \"appropriate\": \"The methodology used is appropriate for the problem.\",\n", - " \"inappropriate\": \"The methodology used is not suitable for the problem.\",\n", - " \"partially_appropriate\": \"The methodology used is partially suitable but could be improved.\"\n", - " }\n", - " },\n", - " \"clarity\": {\n", - " \"description\": \"The ease with which the solution can be understood by others.\",\n", - " \"accepted_values\": {\n", - " \"clear\": \"The solution is presented in a clear, logical manner that is easy to follow.\",\n", - " \"unclear\": \"The solution is difficult to follow or understand.\",\n", - " \"somewhat_clear\": \"The solution is generally clear but could be improved in some areas for better understanding.\"\n", - " }\n", - " },\n", - " \"use_of_language\": {\n", - " \"description\": \"The correctness and appropriateness of mathematical language and notation.\",\n", - " \"accepted_values\": {\n", - " \"appropriate\": \"The language and notation are mathematically sound and correctly applied.\",\n", - " \"inappropriate\": \"The language and notation have errors or are misapplied.\",\n", - " \"mostly_appropriate\": \"The language and notation are mostly correct, but there are minor errors or inconsistencies.\"\n", - " }\n", - " }\n", - "}\n", - "```\n", - "\n", - "These criteria should provide a comprehensive framework for evaluating math problem-solving tasks in terms of accuracy, completeness, efficiency, and clarity.actual test case to evaluate: {\n", - " \"problem\": \"Find $24^{-1} \\\\pmod{11^2}$. That is, find the residue $b$ for which $24b \\\\equiv 1\\\\pmod{11^2}$.\\n\\nExpress your answer as an integer from $0$ to $11^2-1$, inclusive.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Since $5 \\\\times 24 = 120 = 121 - 1$, it follows that $-5 \\\\times 24 \\\\equiv 1 \\\\pmod{121}$. Adding 121 to $-5$ to make it positive, we find $(-5 + 121) \\\\times 24 \\\\equiv 116 \\\\times 24 \\\\equiv 1 \\\\pmod{121}$, so it follows that the modular inverse of $24$ is $\\\\boxed{116}$ when taken modulo $121$.\",\n", - " \"problem_id\": \"5\",\n", - " \"response_with_ans\": \"To find the modular inverse of 24 modulo 11^2, we can use the Extended Euclidean Algorithm. Here is a Python function to compute the modular inverse using this algorithm:\\n\\n```python\\ndef mod_inverse(a, m):\\n g, x, _ = extended_gcd(a, m)\\n if g != 1:\\n raise Exception(f\\\"{a} and {m} are not coprime.\\\")\\n return x % m\\n\\ndef extended_gcd(a, b):\\n if a == 0:\\n return b, 0, 1\\n else:\\n g, x, y = extended_gcd(b % a, a)\\n return g, y - (b // a) * x, x\\n```\\n\\nLet's use the above function to find the modular inverse of 24 modulo 11^2:\\n\\n```python\\na = 24\\nm = 11**2\\nmod_inverse(a, m)\\n```\\n\\nI will execute the above code to find the modular inverse of 24 modulo 11^2.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"Find $24^{-1} \\\\pmod{11^2}$. That is, find the residue $b$ for which $24b \\\\equiv 1\\\\pmod{11^2}$.\\n\\nExpress your answer as an integer from $0$ to $11^2-1$, inclusive.\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the modular inverse of 24 modulo 11^2, we can use the Extended Euclidean Algorithm. Here is a Python function to compute the modular inverse using this algorithm:\\n\\n```python\\ndef mod_inverse(a, m):\\n g, x, _ = extended_gcd(a, m)\\n if g != 1:\\n raise Exception(f\\\"{a} and {m} are not coprime.\\\")\\n return x % m\\n\\ndef extended_gcd(a, b):\\n if a == 0:\\n return b, 0, 1\\n else:\\n g, x, y = extended_gcd(b % a, a)\\n return g, y - (b // a) * x, x\\n```\\n\\nLet's use the above function to find the modular inverse of 24 modulo 11^2:\\n\\n```python\\na = 24\\nm = 11**2\\nmod_inverse(a, m)\\n```\\n\\nI will execute the above code to find the modular inverse of 24 modulo 11^2.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 13.481226921081543,\n", - " \"trial\": -1\n", - "}\n", - "\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Accuracy\",\n", + " \"description\": \"The solution must be correct and adhere strictly to mathematical principles and techniques appropriate for the problem.\",\n", + " \"accepted_values\": [\n", + " \"Correct\",\n", + " \"Minor errors\",\n", + " \"Major errors\",\n", + " \"Incorrect\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Conciseness\",\n", + " \"description\": \"The explanation and method provided should be direct and to the point, avoiding unnecessary steps or complexity.\",\n", + " \"accepted_values\": [\n", + " \"Very concise\",\n", + " \"Concise\",\n", + " \"Somewhat verbose\",\n", + " \"Verbose\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Relevance\",\n", + " \"description\": \"The content of the response must be relevant to the question posed and should address the specific problem requirements.\",\n", + " \"accepted_values\": [\n", + " \"Highly relevant\",\n", + " \"Relevant\",\n", + " \"Somewhat relevant\",\n", + " \"Not relevant\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Efficiency\",\n", + " \"description\": \"The solution should be derived in a time-effective manner, considering the complexity of the problem.\",\n", + " \"accepted_values\": [\n", + " \"Highly efficient\",\n", + " \"Efficient\",\n", + " \"Inefficient\",\n", + " \"Redundant\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Logic and Structure\",\n", + " \"description\": \"The reasoning should be logical and the information structured in a clear and understandable sequence.\",\n", + " \"accepted_values\": [\n", + " \"Exceptionally clear\",\n", + " \"Clear\",\n", + " \"Somewhat clear\",\n", + " \"Confusing\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Use of Resources\",\n", + " \"description\": \"The response should make appropriate and optimal use of external resources or tools (e.g., Python scripts) when necessary.\",\n", + " \"accepted_values\": [\n", + " \"Optimal\",\n", + " \"Appropriate\",\n", + " \"Underutilized\",\n", + " \"Overreliance\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Notation\",\n", + " \"description\": \"The use of proper and standard mathematical notation in the solution and explanation.\",\n", + " \"accepted_values\": [\n", + " \"Excellent\",\n", + " \"Good\",\n", + " \"Adequate\",\n", + " \"Poor\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation and Justification\",\n", + " \"description\": \"There should be a clear explanation, rationale, or justification for each step taken towards the solution.\",\n", + " \"accepted_values\": [\n", + " \"Thorough\",\n", + " \"Adequate\",\n", + " \"Insufficient\",\n", + " \"Missing\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Correctness of Answer Format\",\n", + " \"description\": \"The answer should be presented in the format requested in the problem (e.g., interval notation, simplified form).\",\n", + " \"accepted_values\": [\n", + " \"Perfectly formatted\",\n", + " \"Properly formatted\",\n", + " \"Slightly incorrect format\",\n", + " \"Improperly formatted\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Handling of Edge Cases\",\n", + " \"description\": \"The solution should correctly handle any special or edge cases that may arise in the problem.\",\n", + " \"accepted_values\": [\n", + " \"Complete\",\n", + " \"Most cases\",\n", + " \"Some cases\",\n", + " \"No consideration\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " }\n", + "]actual test case to evaluate: {'problem': 'Find $24^{-1} \\\\pmod{11^2}$. That is, find the residue $b$ for which $24b \\\\equiv 1\\\\pmod{11^2}$.\\n\\nExpress your answer as an integer from $0$ to $11^2-1$, inclusive.', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Since $5 \\\\times 24 = 120 = 121 - 1$, it follows that $-5 \\\\times 24 \\\\equiv 1 \\\\pmod{121}$. Adding 121 to $-5$ to make it positive, we find $(-5 + 121) \\\\times 24 \\\\equiv 116 \\\\times 24 \\\\equiv 1 \\\\pmod{121}$, so it follows that the modular inverse of $24$ is $\\\\boxed{116}$ when taken modulo $121$.', 'problem_id': '5', 'response_with_ans': 'To find the modular inverse of 24 modulo 11^2, we can use the Extended Euclidean Algorithm. Here is a Python function to compute the modular inverse using this algorithm:\\n\\n```python\\ndef mod_inverse(a, m):\\n g, x, _ = extended_gcd(a, m)\\n if g != 1:\\n raise Exception(f\"{a} and {m} are not coprime.\")\\n return x % m\\n\\ndef extended_gcd(a, b):\\n if a == 0:\\n return b, 0, 1\\n else:\\n g, x, y = extended_gcd(b % a, a)\\n return g, y - (b // a) * x, x\\n```\\n\\nLet\\'s use the above function to find the modular inverse of 24 modulo 11^2:\\n\\n```python\\na = 24\\nm = 11**2\\nmod_inverse(a, m)\\n```\\n\\nI will execute the above code to find the modular inverse of 24 modulo 11^2.', 'round': 0, 'messages': [{'content': 'Find $24^{-1} \\\\pmod{11^2}$. That is, find the residue $b$ for which $24b \\\\equiv 1\\\\pmod{11^2}$.\\n\\nExpress your answer as an integer from $0$ to $11^2-1$, inclusive.', 'role': 'user'}, {'content': 'To find the modular inverse of 24 modulo 11^2, we can use the Extended Euclidean Algorithm. Here is a Python function to compute the modular inverse using this algorithm:\\n\\n```python\\ndef mod_inverse(a, m):\\n g, x, _ = extended_gcd(a, m)\\n if g != 1:\\n raise Exception(f\"{a} and {m} are not coprime.\")\\n return x % m\\n\\ndef extended_gcd(a, b):\\n if a == 0:\\n return b, 0, 1\\n else:\\n g, x, y = extended_gcd(b % a, a)\\n return g, y - (b // a) * x, x\\n```\\n\\nLet\\'s use the above function to find the modular inverse of 24 modulo 11^2:\\n\\n```python\\na = 24\\nm = 11**2\\nmod_inverse(a, m)\\n```\\n\\nI will execute the above code to find the modular inverse of 24 modulo 11^2.', 'role': 'assistant'}], 'time': 13.481226921081543, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", - "```json\n", "{\n", - " \"accuracy\": \"correct\",\n", - " \"completeness\": \"complete\",\n", - " \"efficiency\": \"efficient\",\n", - " \"methodology\": \"appropriate\",\n", - " \"clarity\": \"clear\",\n", - " \"use_of_language\": \"appropriate\"\n", + " \"Accuracy\": \"Correct\",\n", + " \"Conciseness\": \"Concise\",\n", + " \"Relevance\": \"Highly relevant\",\n", + " \"Efficiency\": \"Efficient\",\n", + " \"Logic and Structure\": \"Clear\",\n", + " \"Use of Resources\": \"Optimal\",\n", + " \"Mathematical Notation\": \"Good\",\n", + " \"Explanation and Justification\": \"Adequate\",\n", + " \"Correctness of Answer Format\": \"Perfectly formatted\",\n", + " \"Handling of Edge Cases\": \"Complete\"\n", "}\n", - "```\n", "\n", "--------------------------------------------------------------------------------\n", - "actual correctness: true\n", - "predicted coprrectness:\n", - " ```json\n", - "{\n", - " \"accuracy\": \"correct\",\n", - " \"completeness\": \"complete\",\n", - " \"efficiency\": \"efficient\",\n", - " \"methodology\": \"appropriate\",\n", - " \"clarity\": \"clear\",\n", - " \"use_of_language\": \"appropriate\"\n", - "}\n", - "```\n" + "actual correctness: True\n", + "predicted correctness:\n", + " {\n", + " \"Accuracy\": \"Correct\",\n", + " \"Conciseness\": \"Concise\",\n", + " \"Relevance\": \"Highly relevant\",\n", + " \"Efficiency\": \"Efficient\",\n", + " \"Logic and Structure\": \"Clear\",\n", + " \"Use of Resources\": \"Optimal\",\n", + " \"Mathematical Notation\": \"Good\",\n", + " \"Explanation and Justification\": \"Adequate\",\n", + " \"Correctness of Answer Format\": \"Perfectly formatted\",\n", + " \"Handling of Edge Cases\": \"Complete\"\n", + "}\n" ] } ], "source": [ - "test_case = \"../test/test_files/agenteval-in-out/samples/sample_test_case.json\"\n", - "quantifier_output = get_quantifier(test_case, criteria_file)\n", + "test_case = open(\"../test/test_files/agenteval-in-out/samples/sample_test_case.json\", \"r\").read()\n", + "test_case, ground_truth = remove_ground_truth(test_case)\n", + "quantifier_output = quantify_criteria(\n", + " llm_config={\"config_list\": config_list},\n", + " criteria=criteria,\n", + " task=task,\n", + " test_case=test_case,\n", + " ground_truth=ground_truth,\n", + ")\n", "print(\"actual correctness:\", quantifier_output[\"actual_success\"])\n", - "print(\"predicted coprrectness:\\n\", quantifier_output[\"estimated_performance\"])" + "print(\"predicted correctness:\\n\", quantifier_output[\"estimated_performance\"])" ] }, { @@ -676,28 +551,28 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "--2024-01-06 19:06:41-- https://github.com/julianakiseleva/autogen/raw/ddabd4f0e7c13a50e33cf8462e79358666371477/test/test_files/agenteval-in-out/prealgebra.zip\n", - "Resolving github.com (github.com)... 140.82.121.4\n", - "Connecting to github.com (github.com)|140.82.121.4|:443... connected.\n", + "--2024-05-08 17:42:25-- https://github.com/julianakiseleva/autogen/raw/ddabd4f0e7c13a50e33cf8462e79358666371477/test/test_files/agenteval-in-out/prealgebra.zip\n", + "Resolving github.com (github.com)... 140.82.116.3\n", + "Connecting to github.com (github.com)|140.82.116.3|:443... connected.\n", "HTTP request sent, awaiting response... 302 Found\n", "Location: https://raw.githubusercontent.com/julianakiseleva/autogen/ddabd4f0e7c13a50e33cf8462e79358666371477/test/test_files/agenteval-in-out/prealgebra.zip [following]\n", - "--2024-01-06 19:06:41-- https://raw.githubusercontent.com/julianakiseleva/autogen/ddabd4f0e7c13a50e33cf8462e79358666371477/test/test_files/agenteval-in-out/prealgebra.zip\n", - "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.108.133, 185.199.110.133, ...\n", + "--2024-05-08 17:42:25-- https://raw.githubusercontent.com/julianakiseleva/autogen/ddabd4f0e7c13a50e33cf8462e79358666371477/test/test_files/agenteval-in-out/prealgebra.zip\n", + "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.110.133, 185.199.111.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 28567 (28K) [application/zip]\n", "Saving to: ‘prealgebra.zip’\n", "\n", - "prealgebra.zip 100%[===================>] 27.90K --.-KB/s in 0.005s \n", + "prealgebra.zip 100%[===================>] 27.90K --.-KB/s in 0s \n", "\n", - "2024-01-06 19:06:41 (5.85 MB/s) - ‘prealgebra.zip’ saved [28567/28567]\n", + "2024-05-08 17:42:25 (63.0 MB/s) - ‘prealgebra.zip’ saved [28567/28567]\n", "\n", "Archive: prealgebra.zip\n", "warning: skipped \"../\" path component(s) in ../prealgebra/\n", @@ -762,7 +637,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 8, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -775,358 +650,285 @@ "name": "stdout", "output_type": "stream", "text": [ - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"Amaretta's birthday is July 27, and her brother Enzo's birthday is September 3. Every year, Amaretta and Enzo celebrate by eating cake every day from Amaretta's birthday through Enzo's birthday (including both birthdays). If they did this for the first time in 2008, how many cake-eating days will they have observed by the end of 2016?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"There are $39$ cake-eating days each year: the last $5$ days of July, all $31$ days of August, and the first $3$ days of September.\\n\\nThere are $9$ years in the list $$2008,2009,2010,2011,2012,2013,2014,2015,2016.$$ Besides listing them out, we can also see this by subtracting $2007$ from each year, which gives us the list $1,2,3,4,5,6,7,8,9$ (which clearly has $9$ entries).\\n\\n$39$ cake-eating days each year for $9$ years make $39\\\\cdot 9 = \\\\boxed{351}$ days in total.\",\n", - " \"problem_id\": \"3\",\n", - " \"response_with_ans\": \"To calculate the total number of cake-eating days, we will first calculate the number of days between Amaretta's birthday and Enzo's birthday in a non-leap year and in a leap year. Then, we will count the number of leap years and non-leap years in the given range (2008-2016). Finally, we will sum the total number of days for each type of year for both non-leap and leap years.\\n\\nPlease run the following Python code:\\n\\n```python\\nfrom datetime import date\\n\\namaretta_birthday = date(2008, 7, 27)\\nenzo_birthday = date(2008, 9, 3)\\n\\nnormal_year_days = (enzo_birthday - amaretta_birthday).days + 1\\nleap_year_days = normal_year_days + 1\\n\\nleap_years = [year for year in range(2008, 2017) if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)]\\nnon_leap_years_count = 9 - len(leap_years)\\n\\ntotal_cake_eating_days = non_leap_years_count * normal_year_days + len(leap_years) * leap_year_days\\nprint(total_cake_eating_days)\\n```\\n\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"Amaretta's birthday is July 27, and her brother Enzo's birthday is September 3. Every year, Amaretta and Enzo celebrate by eating cake every day from Amaretta's birthday through Enzo's birthday (including both birthdays). If they did this for the first time in 2008, how many cake-eating days will they have observed by the end of 2016?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To calculate the total number of cake-eating days, we will first calculate the number of days between Amaretta's birthday and Enzo's birthday in a non-leap year and in a leap year. Then, we will count the number of leap years and non-leap years in the given range (2008-2016). Finally, we will sum the total number of days for each type of year for both non-leap and leap years.\\n\\nPlease run the following Python code:\\n\\n```python\\nfrom datetime import date\\n\\namaretta_birthday = date(2008, 7, 27)\\nenzo_birthday = date(2008, 9, 3)\\n\\nnormal_year_days = (enzo_birthday - amaretta_birthday).days + 1\\nleap_year_days = normal_year_days + 1\\n\\nleap_years = [year for year in range(2008, 2017) if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)]\\nnon_leap_years_count = 9 - len(leap_years)\\n\\ntotal_cake_eating_days = non_leap_years_count * normal_year_days + len(leap_years) * leap_year_days\\nprint(total_cake_eating_days)\\n```\\n\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 15.208062410354614,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'Bekah has exactly three brass house number digits: 2, 3 and 5. How many distinct numbers can she form using one or more of the digits?', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'If Bekah uses only one digit, she can form three numbers. If she uses two digits, she has three choices for the tens place and two for the units, so she can form six numbers. Finally, if Bekah uses all three digits, she has three choices for the hundreds place, two for the tens, and one for the units, so she can form six numbers. Thus, Bekah can form $3 + 6 + 6 = \\\\boxed{15}$ distinct numbers.', 'problem_id': '11', 'response_with_ans': 'To find the answer, we\\'ll consider all possible combinations that Bekah can make using the three digits she has:\\n1. Single-digit numbers: 2, 3, and 5\\n2. Two-digits numbers using the digits: 23, 25, 32, 35, 52, and 53\\n3. Three-digits number using all the digits: 235, 253, 325, 352, 523, and 532\\n\\nLet\\'s calculate the number of distinct numbers Bekah can form using one or more of the digits with Python code:\\n\\n```python\\nfrom itertools import permutations\\n\\ndigits = [2, 3, 5]\\n\\n# Single-digit numbers\\none_digit_numbers = set(digits)\\n\\n# Two-digits numbers\\ntwo_digit_numbers = set(int(\\'\\'.join(map(str, p))) for p in permutations(digits, 2))\\n\\n# Three-digits numbers\\nthree_digit_numbers = set(int(\\'\\'.join(map(str, p))) for p in permutations(digits, 3))\\n\\ndistinct_numbers = one_digit_numbers | two_digit_numbers | three_digit_numbers\\ntotal_distinct_numbers = len(distinct_numbers)\\n\\nprint(\"Distinct numbers:\", distinct_numbers)\\nprint(\"Total distinct numbers:\", total_distinct_numbers)\\n```\\n\\nPlease run the above Python code to calculate the total number of distinct numbers Bekah can form using one or more of the digits.', 'round': 0, 'messages': [{'content': 'Bekah has exactly three brass house number digits: 2, 3 and 5. How many distinct numbers can she form using one or more of the digits?', 'role': 'user'}, {'content': 'To find the answer, we\\'ll consider all possible combinations that Bekah can make using the three digits she has:\\n1. Single-digit numbers: 2, 3, and 5\\n2. Two-digits numbers using the digits: 23, 25, 32, 35, 52, and 53\\n3. Three-digits number using all the digits: 235, 253, 325, 352, 523, and 532\\n\\nLet\\'s calculate the number of distinct numbers Bekah can form using one or more of the digits with Python code:\\n\\n```python\\nfrom itertools import permutations\\n\\ndigits = [2, 3, 5]\\n\\n# Single-digit numbers\\none_digit_numbers = set(digits)\\n\\n# Two-digits numbers\\ntwo_digit_numbers = set(int(\\'\\'.join(map(str, p))) for p in permutations(digits, 2))\\n\\n# Three-digits numbers\\nthree_digit_numbers = set(int(\\'\\'.join(map(str, p))) for p in permutations(digits, 3))\\n\\ndistinct_numbers = one_digit_numbers | two_digit_numbers | three_digit_numbers\\ntotal_distinct_numbers = len(distinct_numbers)\\n\\nprint(\"Distinct numbers:\", distinct_numbers)\\nprint(\"Total distinct numbers:\", total_distinct_numbers)\\n```\\n\\nPlease run the above Python code to calculate the total number of distinct numbers Bekah can form using one or more of the digits.', 'role': 'assistant'}], 'time': 15.620970249176025, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "{\n", - " \"Problem Interpretation\": \"completely accurate\",\n", - " \"Mathematical Methodology\": \"completely effective\",\n", - " \"Calculation Correctness\": \"completely correct\",\n", - " \"Explanation Clarity\": \"very clear\",\n", - " \"Code Efficiency\": \"very efficient\",\n", - " \"Code Correctness\": \"completely correct\"\n", + " \"Problem Interpretation\": \"completely accurate\",\n", + " \"Mathematical Methodology\": \"completely effective\",\n", + " \"Calculation Correctness\": \"completely correct\",\n", + " \"Explanation Clarity\": \"very clear\",\n", + " \"Code Efficiency\": \"very efficient\",\n", + " \"Code Correctness\": \"completely correct\"\n", "}\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"Two numbers are said to be 'relatively prime' if their greatest common factor is 1. How many integers greater than 10 and less than 30 are relatively prime with 28?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"Since $28=2^2\\\\cdot 7$, a positive integer is relatively prime with $28$ if and only if it contains neither $2$ nor $7$ in its prime factorization. In other words, we want to count the number of integers between $11$ and $29$ inclusive which are divisible by neither $2$ nor $7$.\\n\\nAll of the odd numbers are not divisible by 2; there are 10 such numbers. The only one of these that is divisible by 7 is 21, so there are $10- 1 =\\\\boxed{9}$ numbers between 10 and 30 that are relatively prime with 28.\",\n", - " \"problem_id\": \"12\",\n", - " \"response_with_ans\": \"I will find the relatively prime numbers with 28 in the range greater than 10 and less than 30 using Python code. I will use the math.gcd function and a loop to check each number in the specified range.\\n\\n```python\\nimport math\\n\\ncount = 0\\nrelatively_prime_numbers = []\\nfor number in range(11, 30):\\n if math.gcd(28, number) == 1:\\n relatively_prime_numbers.append(number)\\n count += 1\\n\\nprint(f\\\"Relatively prime numbers with 28: {relatively_prime_numbers}\\\")\\nprint(f\\\"Number of relatively prime numbers with 28: {count}\\\")\\n```\\nExecute the above code to find the integers relatively prime with 28 and their count.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"Two numbers are said to be 'relatively prime' if their greatest common factor is 1. How many integers greater than 10 and less than 30 are relatively prime with 28?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I will find the relatively prime numbers with 28 in the range greater than 10 and less than 30 using Python code. I will use the math.gcd function and a loop to check each number in the specified range.\\n\\n```python\\nimport math\\n\\ncount = 0\\nrelatively_prime_numbers = []\\nfor number in range(11, 30):\\n if math.gcd(28, number) == 1:\\n relatively_prime_numbers.append(number)\\n count += 1\\n\\nprint(f\\\"Relatively prime numbers with 28: {relatively_prime_numbers}\\\")\\nprint(f\\\"Number of relatively prime numbers with 28: {count}\\\")\\n```\\nExecute the above code to find the integers relatively prime with 28 and their count.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 6.9820802211761475,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'What is $.0\\\\overline{3} \\\\div .\\\\overline{03}$? Express your answer as a mixed number.', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'It is almost always easier to use fractions than decimals when dividing. So the first task is to convert these repeating decimals to fractions. First, $.0\\\\overline{3}$: \\\\[\\n10 \\\\cdot .0\\\\overline{3} = .\\\\overline{3} = \\\\frac{1}{3}\\\\\\\\\\n\\\\Rightarrow .0\\\\overline{3} = \\\\frac{1}{3} \\\\div 10 = \\\\frac{1}{3} \\\\cdot \\\\frac{1}{10} = \\\\frac{1}{30}.\\n\\\\]Next, $.\\\\overline{03}$: \\\\[\\n99 \\\\cdot .\\\\overline{03} = (100-1) \\\\cdot .\\\\overline{03} = 3.\\\\overline{03} - .\\\\overline{03} = 3\\\\\\\\\\n\\\\Rightarrow .\\\\overline{03} = \\\\frac{3}{99} = \\\\frac{3}{3 \\\\cdot 33} = \\\\frac{1}{33}.\\n\\\\]We now have the tools to make our calculation: \\\\begin{align*}\\n.0\\\\overline{3} \\\\div .\\\\overline{03} &= \\\\frac{1}{30} \\\\div \\\\frac{1}{33}= \\\\frac{1}{30} \\\\cdot \\\\frac{33}{1}\\\\\\\\\\n&= \\\\frac{33}{30} = \\\\frac{3 \\\\cdot 11}{3 \\\\cdot 10} = \\\\frac{11}{10}\\\\\\\\\\n&= \\\\frac{10+1}{10} = \\\\boxed{1\\\\frac{1}{10}}.\\n\\\\end{align*}', 'problem_id': '15', 'response_with_ans': \"Now, let's simplify the fraction:\\n\\n$\\\\frac{33}{3} = 11$\\n\\nSo, the result of the division is:\\n\\n$.0\\\\overline{3} \\\\div .\\\\overline{03} = 11$\\n\\nSince there is no remainder or mixed number component, the final answer is 11.\\n\\nTERMINATE\", 'round': 3, 'messages': [{'content': 'What is $.0\\\\overline{3} \\\\div .\\\\overline{03}$? Express your answer as a mixed number.', 'role': 'user'}, {'content': \"To solve the problem, let's first convert the repeating decimal numbers into fractions.\\n\\n1. Convert $0.\\\\overline{3}$ to a fraction:\\n\\nLet x = $0.\\\\overline{3}$\\nThen, 10x = $3.\\\\overline{3}$\\nSubtracting the two equations, we get:\\n9x = 3\\nx = $\\\\frac{1}{3}$\\n\\nSo, $0.\\\\overline{3}$ can be written as $\\\\frac{1}{3}$.\\n\\n2. Convert $.\\\\overline{03}$ to a fraction:\\n\\nLet y = $.\\\\overline{03}$\\nThen, 100y = $3.\\\\overline{03}$\\nSubtracting the two equations, we get:\\n99y = 3\\ny = $\\\\frac{1}{33}$\\n\\nSo, $.\\\\overline{03}$ can be written as $\\\\frac{1}{33}$.\\n\\nNow that we have the fractions, we can compute: \\n$(\\\\frac{1}{3}) \\\\div (\\\\frac{1}{33})$\\nTo divide fractions, we multiply the first one by the reciprocal of the second one:\\n\\n$(\\\\frac{1}{3}) \\\\times (\\\\frac{33}{1})$\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"Let's compute the product of the fractions:\\n\\n$\\\\frac{1}{3} \\\\times \\\\frac{33}{1} = \\\\frac{1 \\\\times 33}{3 \\\\times 1}$\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"Now, let's solve the multiplication in the numerator and the denominator:\\n\\n$\\\\frac{1 \\\\times 33}{3 \\\\times 1} = \\\\frac{33}{3}$\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"Now, let's simplify the fraction:\\n\\n$\\\\frac{33}{3} = 11$\\n\\nSo, the result of the division is:\\n\\n$.0\\\\overline{3} \\\\div .\\\\overline{03} = 11$\\n\\nSince there is no remainder or mixed number component, the final answer is 11.\\n\\nTERMINATE\", 'role': 'assistant'}], 'time': 34.40860724449158, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "{\n", - " \"Problem Interpretation\": \"completely accurate\",\n", - " \"Mathematical Methodology\": \"completely effective\",\n", - " \"Calculation Correctness\": \"completely correct\",\n", - " \"Explanation Clarity\": \"very clear\",\n", - " \"Code Efficiency\": \"moderately efficient\",\n", - " \"Code Correctness\": \"completely correct\"\n", + " \"Problem Interpretation\": \"completely accurate\",\n", + " \"Mathematical Methodology\": \"completely effective\",\n", + " \"Calculation Correctness\": \"completely incorrect\",\n", + " \"Explanation Clarity\": \"moderately clear\",\n", + " \"Code Efficiency\": \"not applicable\",\n", + " \"Code Correctness\": \"not applicable\"\n", "}\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", - " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"How many positive and negative integers is $12$ a multiple of?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"The number $12$ is a multiple of $-12, -6, -4, -3, -2, -1, 1, 2, 3, 4, 6,$ and $12,$ for a total of $\\\\boxed{12}$ integers.\",\n", - " \"problem_id\": \"1\",\n", - " \"response_with_ans\": \"```python\\nnum = 12\\ndivisors = [i for i in range(1, num + 1) if num % i == 0]\\n\\npositive_divisors_count = len(divisors)\\nnegative_divisors_count = positive_divisors_count\\ntotal_divisors_count = positive_divisors_count + negative_divisors_count\\n\\nprint(f\\\"Positive divisors of 12: {divisors}\\\")\\ndivisors_negative = [-x for x in divisors]\\nprint(f\\\"Negative divisors of 12: {divisors_negative}\\\")\\nprint(f\\\"Total divisors count: {total_divisors_count}\\\")\\n```\\n\\nExecute this code snippet to find the divisors of 12 and count how many positive and negative integers 12 is a multiple of.\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"How many positive and negative integers is $12$ a multiple of?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To determine how many positive and negative integers 12 is a multiple of, we need to find the divisors of 12. Then we will double the number of divisors, as each positive divisor has a corresponding negative divisor.\\n\\nLet's use a Python code snippet to find all the divisors of 12 and count them.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"```python\\nnum = 12\\ndivisors = [i for i in range(1, num + 1) if num % i == 0]\\n\\npositive_divisors_count = len(divisors)\\nnegative_divisors_count = positive_divisors_count\\ntotal_divisors_count = positive_divisors_count + negative_divisors_count\\n\\nprint(f\\\"Positive divisors of 12: {divisors}\\\")\\ndivisors_negative = [-x for x in divisors]\\nprint(f\\\"Negative divisors of 12: {divisors_negative}\\\")\\nprint(f\\\"Total divisors count: {total_divisors_count}\\\")\\n```\\n\\nExecute this code snippet to find the divisors of 12 and count how many positive and negative integers 12 is a multiple of.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 17.360238790512085,\n", - " \"trial\": -1\n", - "}\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " }\n", + "]actual test case to evaluate: {'problem': 'How many integers $n$ satisfy both of the inequalities $4n + 3 < 25$ and $-7n + 5 < 24$?', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'Subtract 3 and divide by 4 on both sides of the first inequality to obtain \\\\begin{align*}\\n4n + 3 &< 25 \\\\\\\\\\n\\\\Rightarrow\\\\qquad 4n &< 22 \\\\\\\\\\n\\\\Rightarrow\\\\qquad n &< 5.5.\\n\\\\end{align*}Similarly, the second inequality yields \\\\begin{align*}\\n-7n + 5 &< 24 \\\\\\\\\\n\\\\Rightarrow\\\\qquad -7n &< 19 \\\\\\\\\\n\\\\Rightarrow\\\\qquad n &> -\\\\frac{19}{7}.\\n\\\\end{align*}Therefore, we are looking for all the integers between $-\\\\frac{19}{7}$ and $5.5$. Since $-\\\\frac{19}{7}$ is between $-3$ and $-2$ and the largest integer less than $5.5$ is 5, we need to count the number of integers between $-2$ and $5$, inclusive. There are $5$ positive integers, $2$ negative integers, and zero, so there are $\\\\boxed{8}$ integers that satisfy both $4n + 3 < 25$ and $-7n + 5 < 24$.', 'problem_id': '10', 'response_with_ans': 'Here\\'s the code to solve the inequalities and find the intersection of their solutions:\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\nn = symbols(\"n\")\\ninequality_1 = 4 * n + 3 < 25\\ninequality_2 = -7 * n + 5 < 24\\n\\nsolution_1 = solve(inequality_1, n)\\nsolution_2 = solve(inequality_2, n)\\n\\nintersection = (max(solution_1[0], solution_2[0]), min(solution_1[1], solution_2[1]))\\n\\nprint(f\"Solution to inequality 1: {solution_1}\")\\nprint(f\"Solution to inequality 2: {solution_2}\")\\nprint(f\"Intersection of solutions: {intersection}\")\\n```\\n\\nExecute this code, and let\\'s see the solutions for both inequalities and their intersection.', 'round': 1, 'messages': [{'content': 'How many integers $n$ satisfy both of the inequalities $4n + 3 < 25$ and $-7n + 5 < 24$?', 'role': 'user'}, {'content': \"To find the number of integers $n$ that satisfy both inequalities, we'll first solve each inequality individually, and then find the intersection of the solutions.\\n\\nStep 1: Solve the inequalities\\n1. $4n + 3 < 25$\\n2. $-7n + 5 < 24$\\n\\nStep 2: Find the intersection of the solutions\\n\\nStep 3: Count the number of integers in the intersection\\n\\nFirst, let's solve the inequalities using the python code.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': 'Here\\'s the code to solve the inequalities and find the intersection of their solutions:\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\nn = symbols(\"n\")\\ninequality_1 = 4 * n + 3 < 25\\ninequality_2 = -7 * n + 5 < 24\\n\\nsolution_1 = solve(inequality_1, n)\\nsolution_2 = solve(inequality_2, n)\\n\\nintersection = (max(solution_1[0], solution_2[0]), min(solution_1[1], solution_2[1]))\\n\\nprint(f\"Solution to inequality 1: {solution_1}\")\\nprint(f\"Solution to inequality 2: {solution_2}\")\\nprint(f\"Intersection of solutions: {intersection}\")\\n```\\n\\nExecute this code, and let\\'s see the solutions for both inequalities and their intersection.', 'role': 'assistant'}], 'time': 19.949471950531006, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", + "```json\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", " \"Mathematical Methodology\": \"completely effective\",\n", @@ -1135,494 +937,289 @@ " \"Code Efficiency\": \"moderately efficient\",\n", " \"Code Correctness\": \"completely correct\"\n", "}\n", + "```\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: false\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"In isosceles right triangle $ABC$, point $D$ is on hypotenuse $\\\\overline{BC}$ such that $\\\\overline{AD}$ is an altitude of $\\\\triangle ABC$ and $DC = 5$. What is the area of triangle $ABC$?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"In isosceles right triangle $\\\\triangle ABC$ below, $\\\\overline{AD}$ is the altitude to the hypotenuse.\\n\\n[asy]\\nimport olympiad;\\nunitsize(0.8inch);\\npair A,B,C,D;\\nA = (0,1);\\nB= (1,0);\\nC = -B;\\nD = (0,0);\\ndraw(A--B--C--A,linewidth(1));\\ndraw(A--D,linewidth(0.8));\\ndraw(rightanglemark(C,A,B,s=4));\\ndraw(rightanglemark(C,D,A,s=4));\\nlabel(\\\"$A$\\\",A,N);\\nlabel(\\\"$B$\\\",B,S);\\nlabel(\\\"$C$\\\",C,S);\\nlabel(\\\"$D$\\\",D,S);\\n[/asy]\\n\\nBecause $\\\\triangle ABC$ is an isosceles right triangle, $\\\\angle ABC = 45^\\\\circ$. Since $\\\\angle ADB = 90^\\\\circ$, we know that $\\\\angle DAB = 45^\\\\circ$, so $\\\\triangle ABD$ is also a 45-45-90 triangle. Similarly, $\\\\triangle ACD$ is a 45-45-90 triangle. Therefore, $DA=DB = DC = 5$, so $BC = BD+DC = 10$, and \\\\[[ABC] = \\\\frac{(AD)(BC)}{2} = \\\\frac{(5)({10})}{2} = \\\\boxed{25}.\\\\]\",\n", - " \"problem_id\": \"13\",\n", - " \"response_with_ans\": \"```python\\nfrom sympy import Eq, solve, symbols\\r\\n\\r\\n# Step 1: Find the length of side AC using the Pythagorean theorem\\r\\na, b, c = symbols(\\\"a b c\\\")\\r\\nPythagorean = Eq(a**2 + b**2, c**2)\\r\\n\\r\\n# We know BD = 5 and it's an isosceles right triangle so:\\r\\nBD = 5\\r\\nBC = BD * 2\\r\\nside_A = BD # Side A is equal to BD\\r\\n\\r\\nisosceles_triangle = Pythagorean.subs({a: side_A, b: side_A, c: BC}) # Substitute values into the equation\\r\\nlength_AC = solve(isosceles_triangle, side_A)[0] # Solve for side_AC\\r\\n\\r\\n# Step 2: Find the length of side AD\\r\\nlength_AD = (BD * length_AC) / BC # Using the proportion\\r\\n\\r\\n# Step 3: Find the area of triangle ABC\\r\\narea_triangle_ABC = 0.5 * (length_AD * length_AC)\\r\\n\\r\\nlength_AC, length_AD, area_triangle_ABC\\r\\n```\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"In isosceles right triangle $ABC$, point $D$ is on hypotenuse $\\\\overline{BC}$ such that $\\\\overline{AD}$ is an altitude of $\\\\triangle ABC$ and $DC = 5$. What is the area of triangle $ABC$?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Let's first analyze the problem and draw a plan to solve it.\\n\\nPlan:\\n1. Use the Pythagorean theorem to find the length of side AC.\\n2. Since right triangle ADC is similar to triangle ABC, we can set up a proportion to find the length of side AD.\\n3. Use the area formula of a triangle to find the area of triangle ABC.\\n\\nNow, let's execute the plan using code to calculate side AC, then side AD, and finally the area of triangle ABC.\\n\\n\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"```python\\nfrom sympy import Eq, solve, symbols\\r\\n\\r\\n# Step 1: Find the length of side AC using the Pythagorean theorem\\r\\na, b, c = symbols(\\\"a b c\\\")\\r\\nPythagorean = Eq(a**2 + b**2, c**2)\\r\\n\\r\\n# We know BD = 5 and it's an isosceles right triangle so:\\r\\nBD = 5\\r\\nBC = BD * 2\\r\\nside_A = BD # Side A is equal to BD\\r\\n\\r\\nisosceles_triangle = Pythagorean.subs({a: side_A, b: side_A, c: BC}) # Substitute values into the equation\\r\\nlength_AC = solve(isosceles_triangle, side_A)[0] # Solve for side_AC\\r\\n\\r\\n# Step 2: Find the length of side AD\\r\\nlength_AD = (BD * length_AC) / BC # Using the proportion\\r\\n\\r\\n# Step 3: Find the area of triangle ABC\\r\\narea_triangle_ABC = 0.5 * (length_AD * length_AC)\\r\\n\\r\\nlength_AC, length_AD, area_triangle_ABC\\r\\n```\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 22.85700249671936,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'What is the sum of the lengths, in centimeters, of the two legs of a 30-60-90 right triangle, if the length of the hypotenuse is $2\\\\sqrt{6}$ centimeters?', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'We know that the ratio of the lengths of the sides of a 30-60-90 triangle is $1:\\\\sqrt{3}:2$. We know that the length of the hypotenuse is $2\\\\sqrt{6}$ and the ratio of the length shortest leg to that of the hypotenuse is $1:2$. Therefore, the length of the shorter leg is $\\\\sqrt{6}$. Since the ratio of the shorter leg to the longer leg is $1:\\\\sqrt{3}$, the length of the longer leg is $\\\\sqrt{6} \\\\cdot \\\\sqrt{3} = 3\\\\sqrt{2}$. The sum of the lengths of these two legs is $\\\\boxed{\\\\sqrt{6} + 3\\\\sqrt{2}}$ centimeters.', 'problem_id': '17', 'response_with_ans': \"Let's solve for x and find the lengths of the legs.\\n\\n```python\\nimport math\\n\\nhypotenuse = 2 * math.sqrt(6)\\nx = hypotenuse / 2\\n\\nleg_1 = x\\nleg_2 = x * math.sqrt(3)\\nsum_of_legs = leg_1 + leg_2\\n\\nleg_1, leg_2, sum_of_legs\\n```\\n\\nNow, execute the above code to find the sum of the lengths of the two legs.\", 'round': 1, 'messages': [{'content': 'What is the sum of the lengths, in centimeters, of the two legs of a 30-60-90 right triangle, if the length of the hypotenuse is $2\\\\sqrt{6}$ centimeters?', 'role': 'user'}, {'content': \"To find the sum of the lengths of the two legs, let's first analyze a 30-60-90 right triangle. In a 30-60-90 triangle, the ratios of the side lengths are:\\n\\n1. Opposite the 30-degree angle: $x$\\n2. Opposite the 60-degree angle: $x\\\\sqrt{3}$\\n3. Hypotenuse (opposite the 90-degree angle): $2x$\\n\\nNow, we are given that the hypotenuse is $2\\\\sqrt{6}$ centimeters. We can set up the following equation and solve for $x$:\\n\\n$2x = 2\\\\sqrt{6}$\\n\\nNext, we will find the lengths of the legs and sum them up.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"Let's solve for x and find the lengths of the legs.\\n\\n```python\\nimport math\\n\\nhypotenuse = 2 * math.sqrt(6)\\nx = hypotenuse / 2\\n\\nleg_1 = x\\nleg_2 = x * math.sqrt(3)\\nsum_of_legs = leg_1 + leg_2\\n\\nleg_1, leg_2, sum_of_legs\\n```\\n\\nNow, execute the above code to find the sum of the lengths of the two legs.\", 'role': 'assistant'}], 'time': 18.742590188980103, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", + "```json\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", " \"Mathematical Methodology\": \"completely effective\",\n", " \"Calculation Correctness\": \"completely correct\",\n", " \"Explanation Clarity\": \"very clear\",\n", - " \"Code Efficiency\": \"moderately efficient\",\n", - " \"Code Correctness\": \"mostly correct\"\n", + " \"Code Efficiency\": \"very efficient\",\n", + " \"Code Correctness\": \"completely correct\"\n", "}\n", + "```\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: false\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"John counts up from 1 to 13, and then immediately counts down again to 1, and then back up to 13, and so on, alternately counting up and down: \\\\begin{align*}\\n&(1, 2, 3,4,5,6,7,8,9,10,11,12,13,\\\\\\\\\\n&\\\\qquad\\\\qquad12,11,10,9,8,7,6,5,4,3,2,1,2,3,4,\\\\ldots ).\\n\\\\end{align*} What is the $5000^{\\\\text{th}}$ integer in his list?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"We can treat this list as a sequence with a repetitive pattern. We see the sequence repeats itself every 24 elements (from 1 to 13 then back to 2). When 5000 is divided by 24, its remainder is 8. Therefore we see the $5000^{\\\\text{th}}$ integer is the same as the $8^{\\\\text{th}}$ integer, which is $\\\\boxed{8}$.\",\n", - " \"problem_id\": \"4\",\n", - " \"response_with_ans\": \"We can solve this problem by finding which \\\"group\\\" the 5000th number belongs to, and then determine the number within that group. Each group is either counting up from 1 to 13 or counting down from 13 to 1. There are 13 numbers in each direction, but since 1 is counted twice (once at the end of a down-count and once at the beginning of an up-count), counting up and down would consist of 25 numbers (13 up + 12 down).\\n\\nLet's first find out which group the 5000th number belongs to and how many numbers in total are in the complete up and down groups before 5000th number using code:\\n\\n```python\\nnum_group = 5000 // 25\\nremainder = 5000 % 25\\ntotal_numbers_before = num_group * 25\\nprint('Number of complete groups:', num_group)\\nprint('Remainder:', remainder)\\nprint('Numbers before 5000th number:', total_numbers_before)\\n```\\n\\nAfter knowing these values, I'll determine if the 5000th number is in up-count or down-count and its value.\\n\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"John counts up from 1 to 13, and then immediately counts down again to 1, and then back up to 13, and so on, alternately counting up and down: \\\\begin{align*}\\n&(1, 2, 3,4,5,6,7,8,9,10,11,12,13,\\\\\\\\\\n&\\\\qquad\\\\qquad12,11,10,9,8,7,6,5,4,3,2,1,2,3,4,\\\\ldots ).\\n\\\\end{align*} What is the $5000^{\\\\text{th}}$ integer in his list?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"We can solve this problem by finding which \\\"group\\\" the 5000th number belongs to, and then determine the number within that group. Each group is either counting up from 1 to 13 or counting down from 13 to 1. There are 13 numbers in each direction, but since 1 is counted twice (once at the end of a down-count and once at the beginning of an up-count), counting up and down would consist of 25 numbers (13 up + 12 down).\\n\\nLet's first find out which group the 5000th number belongs to and how many numbers in total are in the complete up and down groups before 5000th number using code:\\n\\n```python\\nnum_group = 5000 // 25\\nremainder = 5000 % 25\\ntotal_numbers_before = num_group * 25\\nprint('Number of complete groups:', num_group)\\nprint('Remainder:', remainder)\\nprint('Numbers before 5000th number:', total_numbers_before)\\n```\\n\\nAfter knowing these values, I'll determine if the 5000th number is in up-count or down-count and its value.\\n\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 16.342331409454346,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'How many positive and negative integers is $12$ a multiple of?', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'The number $12$ is a multiple of $-12, -6, -4, -3, -2, -1, 1, 2, 3, 4, 6,$ and $12,$ for a total of $\\\\boxed{12}$ integers.', 'problem_id': '1', 'response_with_ans': '```python\\nnum = 12\\ndivisors = [i for i in range(1, num + 1) if num % i == 0]\\n\\npositive_divisors_count = len(divisors)\\nnegative_divisors_count = positive_divisors_count\\ntotal_divisors_count = positive_divisors_count + negative_divisors_count\\n\\nprint(f\"Positive divisors of 12: {divisors}\")\\ndivisors_negative = [-x for x in divisors]\\nprint(f\"Negative divisors of 12: {divisors_negative}\")\\nprint(f\"Total divisors count: {total_divisors_count}\")\\n```\\n\\nExecute this code snippet to find the divisors of 12 and count how many positive and negative integers 12 is a multiple of.', 'round': 1, 'messages': [{'content': 'How many positive and negative integers is $12$ a multiple of?', 'role': 'user'}, {'content': \"To determine how many positive and negative integers 12 is a multiple of, we need to find the divisors of 12. Then we will double the number of divisors, as each positive divisor has a corresponding negative divisor.\\n\\nLet's use a Python code snippet to find all the divisors of 12 and count them.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '```python\\nnum = 12\\ndivisors = [i for i in range(1, num + 1) if num % i == 0]\\n\\npositive_divisors_count = len(divisors)\\nnegative_divisors_count = positive_divisors_count\\ntotal_divisors_count = positive_divisors_count + negative_divisors_count\\n\\nprint(f\"Positive divisors of 12: {divisors}\")\\ndivisors_negative = [-x for x in divisors]\\nprint(f\"Negative divisors of 12: {divisors_negative}\")\\nprint(f\"Total divisors count: {total_divisors_count}\")\\n```\\n\\nExecute this code snippet to find the divisors of 12 and count how many positive and negative integers 12 is a multiple of.', 'role': 'assistant'}], 'time': 17.360238790512085, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "```json\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", - " \"Mathematical Methodology\": \"mostly effective\",\n", - " \"Calculation Correctness\": \"mostly correct\",\n", + " \"Mathematical Methodology\": \"completely effective\",\n", + " \"Calculation Correctness\": \"completely correct\",\n", " \"Explanation Clarity\": \"very clear\",\n", " \"Code Efficiency\": \"moderately efficient\",\n", - " \"Code Correctness\": \"mostly correct\"\n", + " \"Code Correctness\": \"completely correct\"\n", "}\n", "```\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: false\n", - "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", - "\n", - "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", - " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"An equilateral triangle has a side of length 12 inches. What is the area of the triangle, in square inches? Express your answer in simplest radical form.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"The area of an equilateral triangle with side length $s$ is $s^2\\\\sqrt{3}/4$. We have $s = 12$, so our area is $12^2\\\\sqrt{3}/4 = \\\\boxed{36\\\\sqrt{3}}$.\",\n", - " \"problem_id\": \"18\",\n", - " \"response_with_ans\": \"```python\\nimport math\\n\\nside_length = 12\\narea = (math.sqrt(3) * side_length**2) / 4\\nprint(area)\\n```\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"An equilateral triangle has a side of length 12 inches. What is the area of the triangle, in square inches? Express your answer in simplest radical form.\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the area of an equilateral triangle with a side of length s, we can use the formula:\\n\\narea = (sqrt(3) * s\\u00b2) / 4\\n\\nIn this case, s = 12 inches. Let's calculate the area.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"```python\\nimport math\\n\\nside_length = 12\\narea = (math.sqrt(3) * side_length**2) / 4\\nprint(area)\\n```\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 14.153439283370972,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", - "\n", - "{\n", - " \"Problem Interpretation\": \"completely accurate\",\n", - " \"Mathematical Methodology\": \"completely effective\",\n", - " \"Calculation Correctness\": \"completely correct\",\n", - " \"Explanation Clarity\": \"very clear\",\n", - " \"Code Efficiency\": \"very efficient\",\n", - " \"Code Correctness\": \"completely correct\"\n", - "}\n", - "\n", - "--------------------------------------------------------------------------------\n", - "actual label for this case: false\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"What is the product of $0.\\\\overline{05}$ and $1.8$ expressed as a fraction in simplest form?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"First let's convert $0.\\\\overline{05}$ to a fraction. Let's define $x=0.\\\\overline{05}$. If we multiply both sides by $100$ we get $100x=5.\\\\overline{05}$ so $99x=5$ and $x=0.\\\\overline{05}=\\\\frac{5}{99}$. Since $1.8=\\\\frac{9}{5}$ we get $$\\\\frac{\\\\cancel{5}}{99}\\\\cdot\\\\frac{9}{\\\\cancel{5}}=\\\\frac{9}{99}=\\\\boxed{\\\\frac{1}{11}}.$$\",\n", - " \"problem_id\": \"9\",\n", - " \"response_with_ans\": \"```python\\nfrom fractions import Fraction\\n\\nfraction_1 = Fraction(5, 99)\\ndecimal_2 = 1.8\\n\\nproduct = fraction_1 * decimal_2\\n\\nsimplified_fraction = Fraction(product).limit_denominator()\\n\\nprint(simplified_fraction)\\n```\\n\\nPlease run the above Python code to find the product of $\\\\frac{5}{99}$ and $1.8$ expressed as a fraction in simplest form.\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the product of $0.\\\\overline{05}$ and $1.8$ expressed as a fraction in simplest form?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the product of $0.\\\\overline{05}$ and $1.8$, first we need to convert the repeating decimal $0.\\\\overline{05}$ into a fraction.\\n\\nLet's call the repeating decimal $x$:\\n\\n$$x = 0.\\\\overline{05}$$\\n\\nTo convert the repeating decimal into a fraction, we can multiply both sides by $100$ to shift the decimal point two places to the right:\\n\\n$$100x = 5.\\\\overline{05}$$\\n\\nNow, subtract the first equation from the second equation:\\n\\n$$100x - x = 5.\\\\overline{05} - 0.\\\\overline{05}$$\\n$$99x = 5$$\\n\\nNow, we can solve for $x$:\\n\\n$$x = \\\\frac{5}{99}$$\\n\\nSo the repeating decimal $0.\\\\overline{05}$ is equivalent to the fraction $\\\\frac{5}{99}$.\\n\\nNow, we can find the product of $\\\\frac{5}{99}$ and $1.8$:\\n\\n$$\\\\frac{5}{99} \\\\times 1.8$$\\n\\nFinally, we will simplify the fraction. Let's write the code to compute the product and simplify the result as a fraction.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"```python\\nfrom fractions import Fraction\\n\\nfraction_1 = Fraction(5, 99)\\ndecimal_2 = 1.8\\n\\nproduct = fraction_1 * decimal_2\\n\\nsimplified_fraction = Fraction(product).limit_denominator()\\n\\nprint(simplified_fraction)\\n```\\n\\nPlease run the above Python code to find the product of $\\\\frac{5}{99}$ and $1.8$ expressed as a fraction in simplest form.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 18.04914665222168,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': \"Amaretta's birthday is July 27, and her brother Enzo's birthday is September 3. Every year, Amaretta and Enzo celebrate by eating cake every day from Amaretta's birthday through Enzo's birthday (including both birthdays). If they did this for the first time in 2008, how many cake-eating days will they have observed by the end of 2016?\", 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'There are $39$ cake-eating days each year: the last $5$ days of July, all $31$ days of August, and the first $3$ days of September.\\n\\nThere are $9$ years in the list $$2008,2009,2010,2011,2012,2013,2014,2015,2016.$$ Besides listing them out, we can also see this by subtracting $2007$ from each year, which gives us the list $1,2,3,4,5,6,7,8,9$ (which clearly has $9$ entries).\\n\\n$39$ cake-eating days each year for $9$ years make $39\\\\cdot 9 = \\\\boxed{351}$ days in total.', 'problem_id': '3', 'response_with_ans': \"To calculate the total number of cake-eating days, we will first calculate the number of days between Amaretta's birthday and Enzo's birthday in a non-leap year and in a leap year. Then, we will count the number of leap years and non-leap years in the given range (2008-2016). Finally, we will sum the total number of days for each type of year for both non-leap and leap years.\\n\\nPlease run the following Python code:\\n\\n```python\\nfrom datetime import date\\n\\namaretta_birthday = date(2008, 7, 27)\\nenzo_birthday = date(2008, 9, 3)\\n\\nnormal_year_days = (enzo_birthday - amaretta_birthday).days + 1\\nleap_year_days = normal_year_days + 1\\n\\nleap_years = [year for year in range(2008, 2017) if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)]\\nnon_leap_years_count = 9 - len(leap_years)\\n\\ntotal_cake_eating_days = non_leap_years_count * normal_year_days + len(leap_years) * leap_year_days\\nprint(total_cake_eating_days)\\n```\\n\", 'round': 0, 'messages': [{'content': \"Amaretta's birthday is July 27, and her brother Enzo's birthday is September 3. Every year, Amaretta and Enzo celebrate by eating cake every day from Amaretta's birthday through Enzo's birthday (including both birthdays). If they did this for the first time in 2008, how many cake-eating days will they have observed by the end of 2016?\", 'role': 'user'}, {'content': \"To calculate the total number of cake-eating days, we will first calculate the number of days between Amaretta's birthday and Enzo's birthday in a non-leap year and in a leap year. Then, we will count the number of leap years and non-leap years in the given range (2008-2016). Finally, we will sum the total number of days for each type of year for both non-leap and leap years.\\n\\nPlease run the following Python code:\\n\\n```python\\nfrom datetime import date\\n\\namaretta_birthday = date(2008, 7, 27)\\nenzo_birthday = date(2008, 9, 3)\\n\\nnormal_year_days = (enzo_birthday - amaretta_birthday).days + 1\\nleap_year_days = normal_year_days + 1\\n\\nleap_years = [year for year in range(2008, 2017) if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)]\\nnon_leap_years_count = 9 - len(leap_years)\\n\\ntotal_cake_eating_days = non_leap_years_count * normal_year_days + len(leap_years) * leap_year_days\\nprint(total_cake_eating_days)\\n```\\n\", 'role': 'assistant'}], 'time': 15.208062410354614, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "{\n", @@ -1635,1660 +1232,1254 @@ "}\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: false\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"What is $.0\\\\overline{3} \\\\div .\\\\overline{03}$? Express your answer as a mixed number.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"It is almost always easier to use fractions than decimals when dividing. So the first task is to convert these repeating decimals to fractions. First, $.0\\\\overline{3}$: \\\\[\\n10 \\\\cdot .0\\\\overline{3} = .\\\\overline{3} = \\\\frac{1}{3}\\\\\\\\\\n\\\\Rightarrow .0\\\\overline{3} = \\\\frac{1}{3} \\\\div 10 = \\\\frac{1}{3} \\\\cdot \\\\frac{1}{10} = \\\\frac{1}{30}.\\n\\\\]Next, $.\\\\overline{03}$: \\\\[\\n99 \\\\cdot .\\\\overline{03} = (100-1) \\\\cdot .\\\\overline{03} = 3.\\\\overline{03} - .\\\\overline{03} = 3\\\\\\\\\\n\\\\Rightarrow .\\\\overline{03} = \\\\frac{3}{99} = \\\\frac{3}{3 \\\\cdot 33} = \\\\frac{1}{33}.\\n\\\\]We now have the tools to make our calculation: \\\\begin{align*}\\n.0\\\\overline{3} \\\\div .\\\\overline{03} &= \\\\frac{1}{30} \\\\div \\\\frac{1}{33}= \\\\frac{1}{30} \\\\cdot \\\\frac{33}{1}\\\\\\\\\\n&= \\\\frac{33}{30} = \\\\frac{3 \\\\cdot 11}{3 \\\\cdot 10} = \\\\frac{11}{10}\\\\\\\\\\n&= \\\\frac{10+1}{10} = \\\\boxed{1\\\\frac{1}{10}}.\\n\\\\end{align*}\",\n", - " \"problem_id\": \"15\",\n", - " \"response_with_ans\": \"Now, let's simplify the fraction:\\n\\n$\\\\frac{33}{3} = 11$\\n\\nSo, the result of the division is:\\n\\n$.0\\\\overline{3} \\\\div .\\\\overline{03} = 11$\\n\\nSince there is no remainder or mixed number component, the final answer is 11.\\n\\nTERMINATE\",\n", - " \"round\": 3,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is $.0\\\\overline{3} \\\\div .\\\\overline{03}$? Express your answer as a mixed number.\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To solve the problem, let's first convert the repeating decimal numbers into fractions.\\n\\n1. Convert $0.\\\\overline{3}$ to a fraction:\\n\\nLet x = $0.\\\\overline{3}$\\nThen, 10x = $3.\\\\overline{3}$\\nSubtracting the two equations, we get:\\n9x = 3\\nx = $\\\\frac{1}{3}$\\n\\nSo, $0.\\\\overline{3}$ can be written as $\\\\frac{1}{3}$.\\n\\n2. Convert $.\\\\overline{03}$ to a fraction:\\n\\nLet y = $.\\\\overline{03}$\\nThen, 100y = $3.\\\\overline{03}$\\nSubtracting the two equations, we get:\\n99y = 3\\ny = $\\\\frac{1}{33}$\\n\\nSo, $.\\\\overline{03}$ can be written as $\\\\frac{1}{33}$.\\n\\nNow that we have the fractions, we can compute: \\n$(\\\\frac{1}{3}) \\\\div (\\\\frac{1}{33})$\\nTo divide fractions, we multiply the first one by the reciprocal of the second one:\\n\\n$(\\\\frac{1}{3}) \\\\times (\\\\frac{33}{1})$\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Let's compute the product of the fractions:\\n\\n$\\\\frac{1}{3} \\\\times \\\\frac{33}{1} = \\\\frac{1 \\\\times 33}{3 \\\\times 1}$\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Now, let's solve the multiplication in the numerator and the denominator:\\n\\n$\\\\frac{1 \\\\times 33}{3 \\\\times 1} = \\\\frac{33}{3}$\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Now, let's simplify the fraction:\\n\\n$\\\\frac{33}{3} = 11$\\n\\nSo, the result of the division is:\\n\\n$.0\\\\overline{3} \\\\div .\\\\overline{03} = 11$\\n\\nSince there is no remainder or mixed number component, the final answer is 11.\\n\\nTERMINATE\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 34.40860724449158,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'In the diagram, $AB,$ $BC,$ $CD,$ $DE,$ $EF,$ $FG,$ $GH,$ and $HK$ all have length $4,$ and all angles are right angles, with the exception of the angles at $D$ and $F.$\\n\\n[asy]\\ndraw((0,0)--(0,4)--(4,4)--(4,8)--(6.8284,5.1716)--(9.6569,8)--(9.6569,4)--(13.6569,4)--(13.6569,0)--cycle,black+linewidth(1));\\ndraw((0,0)--(0.5,0)--(0.5,0.5)--(0,0.5)--cycle,black+linewidth(1));\\ndraw((0,4)--(0.5,4)--(0.5,3.5)--(0,3.5)--cycle,black+linewidth(1));\\ndraw((4,4)--(4,4.5)--(3.5,4.5)--(3.5,4)--cycle,black+linewidth(1));\\ndraw((6.8284,5.1716)--(7.0784,5.4216)--(6.8284,5.6716)--(6.5784,5.4216)--cycle,black+linewidth(1));\\ndraw((9.6569,4)--(10.1569,4)--(10.1569,4.5)--(9.6569,4.5)--cycle,black+linewidth(1));\\ndraw((13.6569,4)--(13.1569,4)--(13.1569,3.5)--(13.6569,3.5)--cycle,black+linewidth(1));\\ndraw((13.6569,0)--(13.1569,0)--(13.1569,0.5)--(13.6569,0.5)--cycle,black+linewidth(1));\\nlabel(\"$A$\",(0,0),W);\\nlabel(\"$B$\",(0,4),NW);\\nlabel(\"$C$\",(4,4),S);\\nlabel(\"$D$\",(4,8),N);\\nlabel(\"$E$\",(6.8284,5.1716),S);\\nlabel(\"$F$\",(9.6569,8),N);\\nlabel(\"$G$\",(9.6569,4),S);\\nlabel(\"$H$\",(13.6569,4),NE);\\nlabel(\"$K$\",(13.6569,0),E);\\n[/asy]\\n\\nDetermine the length of $DF.$\\n\\n[asy]\\ndraw((0,0)--(2.8284,-2.8284)--(5.6568,0),black+linewidth(1));\\ndraw((0,0)--(5.6568,0),black+linewidth(1)+dashed);\\ndraw((2.8284,-2.8284)--(3.0784,-2.5784)--(2.8284,-2.3284)--(2.5784,-2.5784)--cycle,black+linewidth(1));\\nlabel(\"$D$\",(0,0),N);\\nlabel(\"$E$\",(2.8284,-2.8284),S);\\nlabel(\"$F$\",(5.6568,0),N);\\n[/asy]', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'Since $DE=EF=4$ and $\\\\angle DEF = 90^\\\\circ,$ by the Pythagorean Theorem, \\\\begin{align*}\\nDF^2 &= DE^2+EF^2 \\\\\\\\\\n&= 4^2+4^2 \\\\\\\\\\n&=32,\\n\\\\end{align*}so that $DF = \\\\sqrt{32}=\\\\boxed{4\\\\sqrt{2}}.$', 'problem_id': '16', 'response_with_ans': \"Now let's calculate the square of DF using Python.\\n\\n```python\\nDH = 9.6569\\nHG = 5.6569\\ncos_alpha_beta = 0\\n\\nDF_squared = DH**2 + HG**2 - 2 * DH * HG * cos_alpha_beta\\nDF_squared\\n```\", 'round': 2, 'messages': [{'content': 'In the diagram, $AB,$ $BC,$ $CD,$ $DE,$ $EF,$ $FG,$ $GH,$ and $HK$ all have length $4,$ and all angles are right angles, with the exception of the angles at $D$ and $F.$\\n\\n[asy]\\ndraw((0,0)--(0,4)--(4,4)--(4,8)--(6.8284,5.1716)--(9.6569,8)--(9.6569,4)--(13.6569,4)--(13.6569,0)--cycle,black+linewidth(1));\\ndraw((0,0)--(0.5,0)--(0.5,0.5)--(0,0.5)--cycle,black+linewidth(1));\\ndraw((0,4)--(0.5,4)--(0.5,3.5)--(0,3.5)--cycle,black+linewidth(1));\\ndraw((4,4)--(4,4.5)--(3.5,4.5)--(3.5,4)--cycle,black+linewidth(1));\\ndraw((6.8284,5.1716)--(7.0784,5.4216)--(6.8284,5.6716)--(6.5784,5.4216)--cycle,black+linewidth(1));\\ndraw((9.6569,4)--(10.1569,4)--(10.1569,4.5)--(9.6569,4.5)--cycle,black+linewidth(1));\\ndraw((13.6569,4)--(13.1569,4)--(13.1569,3.5)--(13.6569,3.5)--cycle,black+linewidth(1));\\ndraw((13.6569,0)--(13.1569,0)--(13.1569,0.5)--(13.6569,0.5)--cycle,black+linewidth(1));\\nlabel(\"$A$\",(0,0),W);\\nlabel(\"$B$\",(0,4),NW);\\nlabel(\"$C$\",(4,4),S);\\nlabel(\"$D$\",(4,8),N);\\nlabel(\"$E$\",(6.8284,5.1716),S);\\nlabel(\"$F$\",(9.6569,8),N);\\nlabel(\"$G$\",(9.6569,4),S);\\nlabel(\"$H$\",(13.6569,4),NE);\\nlabel(\"$K$\",(13.6569,0),E);\\n[/asy]\\n\\nDetermine the length of $DF.$\\n\\n[asy]\\ndraw((0,0)--(2.8284,-2.8284)--(5.6568,0),black+linewidth(1));\\ndraw((0,0)--(5.6568,0),black+linewidth(1)+dashed);\\ndraw((2.8284,-2.8284)--(3.0784,-2.5784)--(2.8284,-2.3284)--(2.5784,-2.5784)--cycle,black+linewidth(1));\\nlabel(\"$D$\",(0,0),N);\\nlabel(\"$E$\",(2.8284,-2.8284),S);\\nlabel(\"$F$\",(5.6568,0),N);\\n[/asy]', 'role': 'user'}, {'content': \"Let's use coordinate geometry to determine the coordinates of points D, E, and F, which will allow us to compute the Euclidean distance between points D and F.\\n\\n1. Assign coordinates to points A, B, C, D, E, F, G, H, and K.\\n2. Calculate the slope and equation of line DE and line FG.\\n3. Use the slopes and the coordinates of points E and G to compute the coordinates of point F.\\n4. Calculate the length of DF using the distance formula.\\n\\nLet's start by assigning coordinates:\\n\\n1. A(0, 0), B(0, 4), C(4, 4), D(4, 8), G(9.6569, 4), H(13.6569, 4), and K(13.6569, 0).\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"Since the angles at points D and F are not right angles, we need to use some trigonometry to find points E and F. We already have the coordinates of points D and G.\\n\\nLet α be the angle between the horizontal line and the line DE, and β be the angle between the horizontal line and the line FG.\\n\\nLet P be the intersection point between the horizontal lines passing through points D and F. Therefore, the triangle DPF is a right triangle. \\n\\nSince we know the angles α, β, and the length DG, we can determine the length DF using the law of cosines:\\n\\nLet's calculate the length of DG first:\\n\\nDG = DH + HG = (13.6569 - 4) + (9.6569 - 4) = 9.6569 + 5.6569 = 15.3138\\n\\nNow, let α be the angle between line DH and DF, and β be the angle between line HG and DF. We can use the law of cosines to calculate the length of DF:\\n\\nDF^2 = DH^2 + HG^2 - 2 * DH * HG * cos(α + β)\\n\\nWe also know angle DAB = α, angle GHB = β and AB = FG = 4.\\n\\nWe can calculate cos(α + β) using the cosine formula for the addition of angles:\\n\\ncos(α + β) = cos(α) * cos(β) - sin(α) * sin(β)\\n\\nTo find sin(α) and cos(α), we can use right triangle with sides DA, AB, and DB. Since DB = BA = 4, we have:\\n\\ncos(α) = AD / DB = 4 / 4 = 1\\nsin(α) = BA / DB = 4 / 4 = 1\\n\\nSimilarly, for angle β, we have right triangle with sides GH, HB, and GB. Since GB = BH = 4, we have:\\n\\ncos(β) = HG / GB = 4 / 4 = 1\\nsin(β) = HB / GB = 4 / 4 = 1\\n\\nNow, let's calculate cos(α + β):\\n\\ncos(α + β) = cos(α) * cos(β) - sin(α) * sin(β) = 1 * 1 - 1 * 1 = 0\\n\\nFinally, let's calculate DF using the law of cosines:\\n\\nDF^2 = DH^2 + HG^2 - 2 * DH * HG * cos(α + β)\\nDF^2 = 9.6569^2 + 5.6569^2 - 2 * 9.6569 * 5.6569 * 0\\n\\nNow let's compute the square of DF.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"Now let's calculate the square of DF using Python.\\n\\n```python\\nDH = 9.6569\\nHG = 5.6569\\ncos_alpha_beta = 0\\n\\nDF_squared = DH**2 + HG**2 - 2 * DH * HG * cos_alpha_beta\\nDF_squared\\n```\", 'role': 'assistant'}], 'time': 44.82826566696167, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", + "```json\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", - " \"Mathematical Methodology\": \"mostly effective\",\n", + " \"Mathematical Methodology\": \"inappropriate\",\n", " \"Calculation Correctness\": \"completely incorrect\",\n", " \"Explanation Clarity\": \"moderately clear\",\n", - " \"Code Efficiency\": \"not applicable\",\n", - " \"Code Correctness\": \"not applicable\"\n", + " \"Code Efficiency\": \"not at all efficient\",\n", + " \"Code Correctness\": \"completely incorrect\"\n", "}\n", + "```\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"The perfect squares from $1$ through $2500,$ inclusive, are printed in a sequence of digits $1491625\\\\ldots2500.$ How many digits are in the sequence?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"We consider it by four cases:\\n\\n$\\\\bullet$ Case 1: There are $3$ perfect squares that only have $1$ digit, $1^{2},$ $2^{2},$ and $3^{2}.$\\n\\n$\\\\bullet$ Case 2: The smallest perfect square that has $2$ digits is $4^{2},$ and the largest is $9^{2},$ so that's a total of $6$ perfect squares with $2$ digits.\\n\\n$\\\\bullet$ Case 3: The smallest perfect square with $3$ digits is $10^{2},$ and the largest is $31^{2},$ yielding a total of $22.$\\n\\n$\\\\bullet$ Case 4: The smallest perfect square with $4$ digits is $32^{2},$ and the last one that is no greater than $2500$ is $50^{2},$ giving a total of $19.$\\n\\nSo we have a total of $1\\\\times3+2\\\\times6+3\\\\times22+4\\\\times19=\\\\boxed{157}$ digits.\",\n", - " \"problem_id\": \"5\",\n", - " \"response_with_ans\": \"```python\\ndef count_digits(n):\\n return len(str(n))\\n\\nperfect_squares = [i ** 2 for i in range(1, int(2500 ** 0.5) + 1)]\\ntotal_digits = sum(count_digits(square) for square in perfect_squares)\\n\\nprint(total_digits)\\n```\\n\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"The perfect squares from $1$ through $2500,$ inclusive, are printed in a sequence of digits $1491625\\\\ldots2500.$ How many digits are in the sequence?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To determine the number of digits in the sequence, we can first find all perfect squares from 1 through 2500, inclusive. Then, we can count the number of digits in each perfect square and sum them up. Let's implement this in Python code:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"```python\\ndef count_digits(n):\\n return len(str(n))\\n\\nperfect_squares = [i ** 2 for i in range(1, int(2500 ** 0.5) + 1)]\\ntotal_digits = sum(count_digits(square) for square in perfect_squares)\\n\\nprint(total_digits)\\n```\\n\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 9.788081884384155,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'A $30^\\\\circ$-$60^\\\\circ$-$90^\\\\circ$ triangle is drawn on the exterior of an equilateral triangle so the hypotenuse of the right triangle is one side of the equilateral triangle. If the shorter leg of the right triangle is 6 units, what is the distance between the two vertices that the triangles do not have in common? Express your answer in simplest radical form. [asy]\\ndraw((2,0)--(0,0)--(1,1.732)--(2,1.732)--(2,0)--(1,1.732));\\ndraw((2,1.632)--(1.9,1.632)--(1.9,1.732));\\nlabel(\"$60^\\\\circ$\",(1,1.732),2SE+E);\\nlabel(\"$30^\\\\circ$\",(2,0),5NNW+4N);\\nlabel(\"6\",(1.5,1.732),N);\\n[/asy]', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'Multiply the short leg of the right triangle by $\\\\sqrt{3}$ to find that the length of the longer leg is $6\\\\sqrt{3}$ units. Double the short leg of the right triangle to find that the length of the hypotenuse of the right triangle is 12 units. Since the hypotenuse of the right triangle is a side of the equilateral triangle, the side length of the equilateral triangle is also 12 units. By the Pythagorean theorem, the distance between the two vertices that the two triangles do not have in common is $\\\\sqrt{(6\\\\sqrt{3})^2+12^2}=\\\\sqrt{252}=\\\\boxed{6\\\\sqrt{7}}$ units. [asy]\\ndraw((2,0)--(0,0)--(1,sqrt(3))--(2,sqrt(3))--(2,0)--(1,sqrt(3)));\\ndraw((2,sqrt(3)-0.1)--(1.9,sqrt(3)-0.1)--(1.9,sqrt(3)));\\ndraw((0,0)--(2,sqrt(3)));\\nlabel(\"$60^\\\\circ$\",(1,sqrt(3)),2SE+E);\\nlabel(\"$30^\\\\circ$\",(2,0),5NNW+4N);\\nlabel(\"6\",(1.5,sqrt(3)),N);\\nlabel(\"$6\\\\sqrt{3}$\",(2,sqrt(3)/2),E);\\nlabel(\"12\",(1.5,sqrt(3)/2),SW);\\nlabel(\"12\",(1,0),S);\\n[/asy]', 'problem_id': '7', 'response_with_ans': 'We have found the distance between the two vertices that the triangles do not have in common (C and D):\\n\\nx = √(252 + 72√3)\\n\\nThis is the simplest radical form for the required distance. \\n\\nTERMINATE', 'round': 3, 'messages': [{'content': 'A $30^\\\\circ$-$60^\\\\circ$-$90^\\\\circ$ triangle is drawn on the exterior of an equilateral triangle so the hypotenuse of the right triangle is one side of the equilateral triangle. If the shorter leg of the right triangle is 6 units, what is the distance between the two vertices that the triangles do not have in common? Express your answer in simplest radical form. [asy]\\ndraw((2,0)--(0,0)--(1,1.732)--(2,1.732)--(2,0)--(1,1.732));\\ndraw((2,1.632)--(1.9,1.632)--(1.9,1.732));\\nlabel(\"$60^\\\\circ$\",(1,1.732),2SE+E);\\nlabel(\"$30^\\\\circ$\",(2,0),5NNW+4N);\\nlabel(\"6\",(1.5,1.732),N);\\n[/asy]', 'role': 'user'}, {'content': \"Let's denote the vertices of the equilateral triangle as A, B, and C, where A is the right angle of the $30^\\\\circ$-$60^\\\\circ$-$90^\\\\circ$ triangle, B is adjacent to the $30^\\\\circ$ angle, and C is adjacent to the $60^\\\\circ$ angle. Let D be the vertex of the right triangle that is not a part of the equilateral triangle (the $60^\\\\circ$ angle).\\n\\nSince the shorter leg of the right triangle is 6 units (AD) and it is a $30^\\\\circ$-$60^\\\\circ$-$90^\\\\circ$ triangle, we know that:\\n1. The longer leg (BD) has a length equal to the shorter leg times $\\\\sqrt{3}$ ($6\\\\sqrt{3}$ units).\\n2. The hypotenuse (AB) has a length twice the size of the shorter leg (12 units).\\n\\nNow, we are trying to find the distance between vertices C and D. Let's call this distance x.\\n\\nTo calculate x, we can use the law of cosines on triangle $\\\\bigtriangleup BCD$. Here, the angle at vertex B is $180^\\\\circ - 60^\\\\circ = 120^\\\\circ$. We have the lengths of sides BC and BD, so we can calculate x using the law of cosines and then simplify the result.\\n\\nThe law of cosines formula is:\\nx^2 = (BC^2) + (BD^2) - 2(BC)(BD)cos(120^\\\\circ)\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': 'First, we need the length of side BC, which is equal to the length of side AB (since it is an equilateral triangle). Thus, BC = 12 units. Now we can use the law of cosines formula to find the value of x^2:\\n\\nx^2 = (12^2) + (6√3)^2 - 2(12)(6√3)cos(120º)\\n\\nThe cosine of 120º is -1/2, so:\\n\\nx^2 = 144 + 108 - 2(12)(6√3)(-1/2)\\n', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"Let's simplify the expression for x^2:\\n\\nx^2 = 144 + 108 + (12)(6√3)\\n\\nNow we will calculate the result:\\n\\nx^2 = 144 + 108 + 72√3\\n\\nx^2 = 252 + 72√3\\n\\nNow, to find the value of x, we'll take the square root of both sides:\\n\\nx = √(252 + 72√3)\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': 'We have found the distance between the two vertices that the triangles do not have in common (C and D):\\n\\nx = √(252 + 72√3)\\n\\nThis is the simplest radical form for the required distance. \\n\\nTERMINATE', 'role': 'assistant'}], 'time': 35.88237428665161, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "```json\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", - " \"Mathematical Methodology\": \"mostly effective\",\n", - " \"Calculation Correctness\": \"completely correct\",\n", - " \"Explanation Clarity\": \"very clear\",\n", - " \"Code Efficiency\": \"moderately efficient\",\n", - " \"Code Correctness\": \"completely correct\"\n", + " \"Mathematical Methodology\": \"completely effective\",\n", + " \"Calculation Correctness\": \"mostly correct\",\n", + " \"Explanation Clarity\": \"mostly clear\",\n", + " \"Code Efficiency\": \"N/A\",\n", + " \"Code Correctness\": \"N/A\"\n", "}\n", "```\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"All 50 states as well as the District of Columbia and Puerto Rico, have distinct two-letter postal abbreviations. If a two-letter sequence of letters (such as CO or EE) is chosen at random, what is the probability that it is a postal abbreviation for one of the 50 states, the District of Columbia, or Puerto Rico? Express your answer as a common fraction.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"There are $26\\\\cdot 26$ possible two-letter sequences of letters, since we have 26 choices for the first and then 26 choices for the second. But only 52 of these possibilities are valid, so our answer is $\\\\frac{52}{26\\\\cdot 26} =\\\\boxed{ \\\\frac{1}{13}}$.\",\n", - " \"problem_id\": \"8\",\n", - " \"response_with_ans\": \"We'll use a reliable source like USPS to fetch the postal abbreviations, and then calculate the probability.\\n\\n```python\\nimport requests\\nfrom bs4 import BeautifulSoup\\nimport fractions\\n\\nurl = 'https://pe.usps.com/text/pub28/28apb.htm'\\nresponse = requests.get(url)\\nsoup = BeautifulSoup(response.text, 'html.parser')\\n\\n# Find the table containing the state info\\nstate_table = soup.find_all('table')[0]\\n\\n# Extract state abbreviations from the table\\nstate_abbreviations = [row.find_all('td')[-1].text.strip() for row in state_table.find_all('tr')[3:-2]]\\n\\n# Add District of Columbia and Puerto Rico.\\nstate_abbreviations.extend([\\\"DC\\\", \\\"PR\\\"])\\n\\nnum_valid_abbreviations = len(state_abbreviations)\\ntotal_possible_combinations = 26 * 26\\nprobability = fractions.Fraction(num_valid_abbreviations, total_possible_combinations)\\n\\nprint(f\\\"Valid postal abbreviations: {state_abbreviations}\\\")\\nprint(f\\\"Total valid postal abbreviations: {num_valid_abbreviations}\\\")\\nprint(f\\\"Total possible two-letter sequences: {total_possible_combinations}\\\")\\nprint(f\\\"Probability: {probability}\\\")\\n```\\n\\nPlease run the above code, and the output will show you the list of valid postal abbreviations, the total number of valid abbreviations, the total possible two-letter sequences, and the probability.\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"All 50 states as well as the District of Columbia and Puerto Rico, have distinct two-letter postal abbreviations. If a two-letter sequence of letters (such as CO or EE) is chosen at random, what is the probability that it is a postal abbreviation for one of the 50 states, the District of Columbia, or Puerto Rico? Express your answer as a common fraction.\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To calculate the probability, we first need to know the total number of possible two-letter sequences and the number of valid postal abbreviations.\\n\\nTo get the total number of possible two-letter sequences, we'll calculate all possible combinations using 26 letters of the alphabet. There are 26 options for choosing the first letter and 26 options for choosing the second letter, so the possible sequences are 26 * 26.\\n\\nNext, let's find the valid postal abbreviations for all 50 states, District of Columbia, and Puerto Rico. To do this, we can create code to download the postal abbreviations.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"We'll use a reliable source like USPS to fetch the postal abbreviations, and then calculate the probability.\\n\\n```python\\nimport requests\\nfrom bs4 import BeautifulSoup\\nimport fractions\\n\\nurl = 'https://pe.usps.com/text/pub28/28apb.htm'\\nresponse = requests.get(url)\\nsoup = BeautifulSoup(response.text, 'html.parser')\\n\\n# Find the table containing the state info\\nstate_table = soup.find_all('table')[0]\\n\\n# Extract state abbreviations from the table\\nstate_abbreviations = [row.find_all('td')[-1].text.strip() for row in state_table.find_all('tr')[3:-2]]\\n\\n# Add District of Columbia and Puerto Rico.\\nstate_abbreviations.extend([\\\"DC\\\", \\\"PR\\\"])\\n\\nnum_valid_abbreviations = len(state_abbreviations)\\ntotal_possible_combinations = 26 * 26\\nprobability = fractions.Fraction(num_valid_abbreviations, total_possible_combinations)\\n\\nprint(f\\\"Valid postal abbreviations: {state_abbreviations}\\\")\\nprint(f\\\"Total valid postal abbreviations: {num_valid_abbreviations}\\\")\\nprint(f\\\"Total possible two-letter sequences: {total_possible_combinations}\\\")\\nprint(f\\\"Probability: {probability}\\\")\\n```\\n\\nPlease run the above code, and the output will show you the list of valid postal abbreviations, the total number of valid abbreviations, the total possible two-letter sequences, and the probability.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 27.01692771911621,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'The perfect squares from $1$ through $2500,$ inclusive, are printed in a sequence of digits $1491625\\\\ldots2500.$ How many digits are in the sequence?', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': \"We consider it by four cases:\\n\\n$\\\\bullet$ Case 1: There are $3$ perfect squares that only have $1$ digit, $1^{2},$ $2^{2},$ and $3^{2}.$\\n\\n$\\\\bullet$ Case 2: The smallest perfect square that has $2$ digits is $4^{2},$ and the largest is $9^{2},$ so that's a total of $6$ perfect squares with $2$ digits.\\n\\n$\\\\bullet$ Case 3: The smallest perfect square with $3$ digits is $10^{2},$ and the largest is $31^{2},$ yielding a total of $22.$\\n\\n$\\\\bullet$ Case 4: The smallest perfect square with $4$ digits is $32^{2},$ and the last one that is no greater than $2500$ is $50^{2},$ giving a total of $19.$\\n\\nSo we have a total of $1\\\\times3+2\\\\times6+3\\\\times22+4\\\\times19=\\\\boxed{157}$ digits.\", 'problem_id': '5', 'response_with_ans': '```python\\ndef count_digits(n):\\n return len(str(n))\\n\\nperfect_squares = [i ** 2 for i in range(1, int(2500 ** 0.5) + 1)]\\ntotal_digits = sum(count_digits(square) for square in perfect_squares)\\n\\nprint(total_digits)\\n```\\n', 'round': 1, 'messages': [{'content': 'The perfect squares from $1$ through $2500,$ inclusive, are printed in a sequence of digits $1491625\\\\ldots2500.$ How many digits are in the sequence?', 'role': 'user'}, {'content': \"To determine the number of digits in the sequence, we can first find all perfect squares from 1 through 2500, inclusive. Then, we can count the number of digits in each perfect square and sum them up. Let's implement this in Python code:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '```python\\ndef count_digits(n):\\n return len(str(n))\\n\\nperfect_squares = [i ** 2 for i in range(1, int(2500 ** 0.5) + 1)]\\ntotal_digits = sum(count_digits(square) for square in perfect_squares)\\n\\nprint(total_digits)\\n```\\n', 'role': 'assistant'}], 'time': 9.788081884384155, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", + "```json\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", " \"Mathematical Methodology\": \"completely effective\",\n", " \"Calculation Correctness\": \"completely correct\",\n", " \"Explanation Clarity\": \"very clear\",\n", " \"Code Efficiency\": \"moderately efficient\",\n", - " \"Code Correctness\": \"mostly correct\"\n", + " \"Code Correctness\": \"completely correct\"\n", "}\n", + "```\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"What is the sum of the lengths, in centimeters, of the two legs of a 30-60-90 right triangle, if the length of the hypotenuse is $2\\\\sqrt{6}$ centimeters?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"We know that the ratio of the lengths of the sides of a 30-60-90 triangle is $1:\\\\sqrt{3}:2$. We know that the length of the hypotenuse is $2\\\\sqrt{6}$ and the ratio of the length shortest leg to that of the hypotenuse is $1:2$. Therefore, the length of the shorter leg is $\\\\sqrt{6}$. Since the ratio of the shorter leg to the longer leg is $1:\\\\sqrt{3}$, the length of the longer leg is $\\\\sqrt{6} \\\\cdot \\\\sqrt{3} = 3\\\\sqrt{2}$. The sum of the lengths of these two legs is $\\\\boxed{\\\\sqrt{6} + 3\\\\sqrt{2}}$ centimeters.\",\n", - " \"problem_id\": \"17\",\n", - " \"response_with_ans\": \"Let's solve for x and find the lengths of the legs.\\n\\n```python\\nimport math\\n\\nhypotenuse = 2 * math.sqrt(6)\\nx = hypotenuse / 2\\n\\nleg_1 = x\\nleg_2 = x * math.sqrt(3)\\nsum_of_legs = leg_1 + leg_2\\n\\nleg_1, leg_2, sum_of_legs\\n```\\n\\nNow, execute the above code to find the sum of the lengths of the two legs.\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of the lengths, in centimeters, of the two legs of a 30-60-90 right triangle, if the length of the hypotenuse is $2\\\\sqrt{6}$ centimeters?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of the lengths of the two legs, let's first analyze a 30-60-90 right triangle. In a 30-60-90 triangle, the ratios of the side lengths are:\\n\\n1. Opposite the 30-degree angle: $x$\\n2. Opposite the 60-degree angle: $x\\\\sqrt{3}$\\n3. Hypotenuse (opposite the 90-degree angle): $2x$\\n\\nNow, we are given that the hypotenuse is $2\\\\sqrt{6}$ centimeters. We can set up the following equation and solve for $x$:\\n\\n$2x = 2\\\\sqrt{6}$\\n\\nNext, we will find the lengths of the legs and sum them up.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Let's solve for x and find the lengths of the legs.\\n\\n```python\\nimport math\\n\\nhypotenuse = 2 * math.sqrt(6)\\nx = hypotenuse / 2\\n\\nleg_1 = x\\nleg_2 = x * math.sqrt(3)\\nsum_of_legs = leg_1 + leg_2\\n\\nleg_1, leg_2, sum_of_legs\\n```\\n\\nNow, execute the above code to find the sum of the lengths of the two legs.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 18.742590188980103,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'In isosceles right triangle $ABC$, point $D$ is on hypotenuse $\\\\overline{BC}$ such that $\\\\overline{AD}$ is an altitude of $\\\\triangle ABC$ and $DC = 5$. What is the area of triangle $ABC$?', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'In isosceles right triangle $\\\\triangle ABC$ below, $\\\\overline{AD}$ is the altitude to the hypotenuse.\\n\\n[asy]\\nimport olympiad;\\nunitsize(0.8inch);\\npair A,B,C,D;\\nA = (0,1);\\nB= (1,0);\\nC = -B;\\nD = (0,0);\\ndraw(A--B--C--A,linewidth(1));\\ndraw(A--D,linewidth(0.8));\\ndraw(rightanglemark(C,A,B,s=4));\\ndraw(rightanglemark(C,D,A,s=4));\\nlabel(\"$A$\",A,N);\\nlabel(\"$B$\",B,S);\\nlabel(\"$C$\",C,S);\\nlabel(\"$D$\",D,S);\\n[/asy]\\n\\nBecause $\\\\triangle ABC$ is an isosceles right triangle, $\\\\angle ABC = 45^\\\\circ$. Since $\\\\angle ADB = 90^\\\\circ$, we know that $\\\\angle DAB = 45^\\\\circ$, so $\\\\triangle ABD$ is also a 45-45-90 triangle. Similarly, $\\\\triangle ACD$ is a 45-45-90 triangle. Therefore, $DA=DB = DC = 5$, so $BC = BD+DC = 10$, and \\\\[[ABC] = \\\\frac{(AD)(BC)}{2} = \\\\frac{(5)({10})}{2} = \\\\boxed{25}.\\\\]', 'problem_id': '13', 'response_with_ans': '```python\\nfrom sympy import Eq, solve, symbols\\r\\n\\r\\n# Step 1: Find the length of side AC using the Pythagorean theorem\\r\\na, b, c = symbols(\"a b c\")\\r\\nPythagorean = Eq(a**2 + b**2, c**2)\\r\\n\\r\\n# We know BD = 5 and it\\'s an isosceles right triangle so:\\r\\nBD = 5\\r\\nBC = BD * 2\\r\\nside_A = BD # Side A is equal to BD\\r\\n\\r\\nisosceles_triangle = Pythagorean.subs({a: side_A, b: side_A, c: BC}) # Substitute values into the equation\\r\\nlength_AC = solve(isosceles_triangle, side_A)[0] # Solve for side_AC\\r\\n\\r\\n# Step 2: Find the length of side AD\\r\\nlength_AD = (BD * length_AC) / BC # Using the proportion\\r\\n\\r\\n# Step 3: Find the area of triangle ABC\\r\\narea_triangle_ABC = 0.5 * (length_AD * length_AC)\\r\\n\\r\\nlength_AC, length_AD, area_triangle_ABC\\r\\n```', 'round': 1, 'messages': [{'content': 'In isosceles right triangle $ABC$, point $D$ is on hypotenuse $\\\\overline{BC}$ such that $\\\\overline{AD}$ is an altitude of $\\\\triangle ABC$ and $DC = 5$. What is the area of triangle $ABC$?', 'role': 'user'}, {'content': \"Let's first analyze the problem and draw a plan to solve it.\\n\\nPlan:\\n1. Use the Pythagorean theorem to find the length of side AC.\\n2. Since right triangle ADC is similar to triangle ABC, we can set up a proportion to find the length of side AD.\\n3. Use the area formula of a triangle to find the area of triangle ABC.\\n\\nNow, let's execute the plan using code to calculate side AC, then side AD, and finally the area of triangle ABC.\\n\\n\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '```python\\nfrom sympy import Eq, solve, symbols\\r\\n\\r\\n# Step 1: Find the length of side AC using the Pythagorean theorem\\r\\na, b, c = symbols(\"a b c\")\\r\\nPythagorean = Eq(a**2 + b**2, c**2)\\r\\n\\r\\n# We know BD = 5 and it\\'s an isosceles right triangle so:\\r\\nBD = 5\\r\\nBC = BD * 2\\r\\nside_A = BD # Side A is equal to BD\\r\\n\\r\\nisosceles_triangle = Pythagorean.subs({a: side_A, b: side_A, c: BC}) # Substitute values into the equation\\r\\nlength_AC = solve(isosceles_triangle, side_A)[0] # Solve for side_AC\\r\\n\\r\\n# Step 2: Find the length of side AD\\r\\nlength_AD = (BD * length_AC) / BC # Using the proportion\\r\\n\\r\\n# Step 3: Find the area of triangle ABC\\r\\narea_triangle_ABC = 0.5 * (length_AD * length_AC)\\r\\n\\r\\nlength_AC, length_AD, area_triangle_ABC\\r\\n```', 'role': 'assistant'}], 'time': 22.85700249671936, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", + "```json\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", - " \"Mathematical Methodology\": \"completely effective\",\n", - " \"Calculation Correctness\": \"completely correct\",\n", + " \"Mathematical Methodology\": \"mostly effective\",\n", + " \"Calculation Correctness\": \"mostly correct\",\n", " \"Explanation Clarity\": \"very clear\",\n", " \"Code Efficiency\": \"moderately efficient\",\n", - " \"Code Correctness\": \"completely correct\"\n", + " \"Code Correctness\": \"mostly correct\"\n", "}\n", + "```\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"Each interior angle of a polygon measures 170 degrees. How many sides does the polygon have?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"The sum of the interior angles of a polygon is $180(n-2)$, where $n$ is the number of sides. That means each interior angle has a measure of $\\\\frac{180(n-2)}{n}$ degrees. We set this equal to 170 degrees and solve for $n$. \\\\begin{align*}\\n\\\\frac{180(n-2)}{n}&=170\\\\\\\\\\n\\\\Rightarrow \\\\qquad 180n-360&=170n\\\\\\\\\\\\Rightarrow \\\\qquad 10n&=360\\\\\\\\\\n\\\\Rightarrow\\\\qquad n&=36.\\n\\\\end{align*} The polygon has $\\\\boxed{36}$ sides.\",\n", - " \"problem_id\": \"14\",\n", - " \"response_with_ans\": \"Quick note: A single backtick should be used for inline code, and triple backticks should be used for code blocks. It seems I accidentally used single backticks for the code block in my previous response. Please use the code below instead:\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\nn = symbols('n')\\nequation = Eq(n * 170, (n - 2) * 180)\\nsides = solve(equation, n)[0]\\nprint(sides)\\n```\\n\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"Each interior angle of a polygon measures 170 degrees. How many sides does the polygon have?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"We can use the formula for the sum of the interior angles of a polygon to solve this problem. The formula is:\\n\\nsum_of_interior_angles = (n - 2) * 180\\n\\nwhere n is the number of sides.\\n\\nAs each interior angle measures 170 degrees, we can write the equation:\\n\\nn * 170 = (n - 2) * 180\\n\\nLet's solve for the number of sides (n) using Python code.\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\nn = symbols('n')\\nequation = Eq(n * 170, (n - 2) * 180)\\nsides = solve(equation, n)[0]\\nsides\\n\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Quick note: A single backtick should be used for inline code, and triple backticks should be used for code blocks. It seems I accidentally used single backticks for the code block in my previous response. Please use the code below instead:\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\nn = symbols('n')\\nequation = Eq(n * 170, (n - 2) * 180)\\nsides = solve(equation, n)[0]\\nprint(sides)\\n```\\n\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 16.42014193534851,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': \"Two numbers are said to be 'relatively prime' if their greatest common factor is 1. How many integers greater than 10 and less than 30 are relatively prime with 28?\", 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'Since $28=2^2\\\\cdot 7$, a positive integer is relatively prime with $28$ if and only if it contains neither $2$ nor $7$ in its prime factorization. In other words, we want to count the number of integers between $11$ and $29$ inclusive which are divisible by neither $2$ nor $7$.\\n\\nAll of the odd numbers are not divisible by 2; there are 10 such numbers. The only one of these that is divisible by 7 is 21, so there are $10- 1 =\\\\boxed{9}$ numbers between 10 and 30 that are relatively prime with 28.', 'problem_id': '12', 'response_with_ans': 'I will find the relatively prime numbers with 28 in the range greater than 10 and less than 30 using Python code. I will use the math.gcd function and a loop to check each number in the specified range.\\n\\n```python\\nimport math\\n\\ncount = 0\\nrelatively_prime_numbers = []\\nfor number in range(11, 30):\\n if math.gcd(28, number) == 1:\\n relatively_prime_numbers.append(number)\\n count += 1\\n\\nprint(f\"Relatively prime numbers with 28: {relatively_prime_numbers}\")\\nprint(f\"Number of relatively prime numbers with 28: {count}\")\\n```\\nExecute the above code to find the integers relatively prime with 28 and their count.', 'round': 0, 'messages': [{'content': \"Two numbers are said to be 'relatively prime' if their greatest common factor is 1. How many integers greater than 10 and less than 30 are relatively prime with 28?\", 'role': 'user'}, {'content': 'I will find the relatively prime numbers with 28 in the range greater than 10 and less than 30 using Python code. I will use the math.gcd function and a loop to check each number in the specified range.\\n\\n```python\\nimport math\\n\\ncount = 0\\nrelatively_prime_numbers = []\\nfor number in range(11, 30):\\n if math.gcd(28, number) == 1:\\n relatively_prime_numbers.append(number)\\n count += 1\\n\\nprint(f\"Relatively prime numbers with 28: {relatively_prime_numbers}\")\\nprint(f\"Number of relatively prime numbers with 28: {count}\")\\n```\\nExecute the above code to find the integers relatively prime with 28 and their count.', 'role': 'assistant'}], 'time': 6.9820802211761475, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", " \"Mathematical Methodology\": \"completely effective\",\n", " \"Calculation Correctness\": \"completely correct\",\n", - " \"Explanation Clarity\": \"completely clear\",\n", - " \"Code Efficiency\": \"very efficient\",\n", + " \"Explanation Clarity\": \"very clear\",\n", + " \"Code Efficiency\": \"moderately efficient\",\n", " \"Code Correctness\": \"completely correct\"\n", "}\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: false\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"Simplify $\\\\frac{2m+8}{3}-\\\\frac{2-m}{3}$.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"Both fractions have the same denominator, so we can subtract them: \\\\[\\\\frac{2m+8}{3}-\\\\frac{2-m}{3}=\\\\frac{(2m+8)-(2-m)}{3}\\\\] Distributing the negative sign across the parentheses, we get \\\\[\\\\frac{2m+8-2-(-m)}{3}=\\\\frac{2m+8-2+m}{3}=\\\\frac{3m+6}{3}\\\\] Notice that every number in the numerator has a common factor of 3. We can use the distributive law in reverse to get \\\\[\\\\frac{3m+6}{3}=\\\\frac{3(m+2)}{3}=\\\\frac{\\\\cancel{3}(m+2)}{\\\\cancel{3}}=\\\\boxed{m+2}.\\\\]\",\n", - " \"problem_id\": \"19\",\n", - " \"response_with_ans\": \"\",\n", - " \"round\": 15,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"Simplify $\\\\frac{2m+8}{3}-\\\\frac{2-m}{3}$.\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To simplify the expression, we can combine the fractions as they have the same denominator:\\n\\n$\\\\frac{2m + 8}{3} - \\\\frac{2 - m}{3} = \\\\frac{(2m + 8) - (2 - m)}{3}$\\n\\nNow, I will calculate the numerator and finish simplifying the expression.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\\n\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"My apologies for the confusion in the previous responses. Let me simplify the expression step by step now. \\n\\nWe have the expression:\\n\\n$\\\\frac{2m + 8}{3} - \\\\frac{2 - m}{3}$\\n\\nWe can combine the fractions as they have the same denominator:\\n\\n$\\\\frac{(2m + 8) - (2 - m)}{3}$\\n\\nLet's further simplify the expression using Python code.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 37.317880630493164,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'What is the product of $0.\\\\overline{05}$ and $1.8$ expressed as a fraction in simplest form?', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': \"First let's convert $0.\\\\overline{05}$ to a fraction. Let's define $x=0.\\\\overline{05}$. If we multiply both sides by $100$ we get $100x=5.\\\\overline{05}$ so $99x=5$ and $x=0.\\\\overline{05}=\\\\frac{5}{99}$. Since $1.8=\\\\frac{9}{5}$ we get $$\\\\frac{\\\\cancel{5}}{99}\\\\cdot\\\\frac{9}{\\\\cancel{5}}=\\\\frac{9}{99}=\\\\boxed{\\\\frac{1}{11}}.$$\", 'problem_id': '9', 'response_with_ans': '```python\\nfrom fractions import Fraction\\n\\nfraction_1 = Fraction(5, 99)\\ndecimal_2 = 1.8\\n\\nproduct = fraction_1 * decimal_2\\n\\nsimplified_fraction = Fraction(product).limit_denominator()\\n\\nprint(simplified_fraction)\\n```\\n\\nPlease run the above Python code to find the product of $\\\\frac{5}{99}$ and $1.8$ expressed as a fraction in simplest form.', 'round': 1, 'messages': [{'content': 'What is the product of $0.\\\\overline{05}$ and $1.8$ expressed as a fraction in simplest form?', 'role': 'user'}, {'content': \"To find the product of $0.\\\\overline{05}$ and $1.8$, first we need to convert the repeating decimal $0.\\\\overline{05}$ into a fraction.\\n\\nLet's call the repeating decimal $x$:\\n\\n$$x = 0.\\\\overline{05}$$\\n\\nTo convert the repeating decimal into a fraction, we can multiply both sides by $100$ to shift the decimal point two places to the right:\\n\\n$$100x = 5.\\\\overline{05}$$\\n\\nNow, subtract the first equation from the second equation:\\n\\n$$100x - x = 5.\\\\overline{05} - 0.\\\\overline{05}$$\\n$$99x = 5$$\\n\\nNow, we can solve for $x$:\\n\\n$$x = \\\\frac{5}{99}$$\\n\\nSo the repeating decimal $0.\\\\overline{05}$ is equivalent to the fraction $\\\\frac{5}{99}$.\\n\\nNow, we can find the product of $\\\\frac{5}{99}$ and $1.8$:\\n\\n$$\\\\frac{5}{99} \\\\times 1.8$$\\n\\nFinally, we will simplify the fraction. Let's write the code to compute the product and simplify the result as a fraction.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '```python\\nfrom fractions import Fraction\\n\\nfraction_1 = Fraction(5, 99)\\ndecimal_2 = 1.8\\n\\nproduct = fraction_1 * decimal_2\\n\\nsimplified_fraction = Fraction(product).limit_denominator()\\n\\nprint(simplified_fraction)\\n```\\n\\nPlease run the above Python code to find the product of $\\\\frac{5}{99}$ and $1.8$ expressed as a fraction in simplest form.', 'role': 'assistant'}], 'time': 18.04914665222168, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", " \"Mathematical Methodology\": \"completely effective\",\n", " \"Calculation Correctness\": \"completely correct\",\n", - " \"Explanation Clarity\": \"not at all clear\",\n", - " \"Code Efficiency\": \"not applicable\",\n", - " \"Code Correctness\": \"not applicable\"\n", + " \"Explanation Clarity\": \"very clear\",\n", + " \"Code Efficiency\": \"moderately efficient\",\n", + " \"Code Correctness\": \"completely correct\"\n", "}\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", - "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", - "\n", - "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", - " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"A $30^\\\\circ$-$60^\\\\circ$-$90^\\\\circ$ triangle is drawn on the exterior of an equilateral triangle so the hypotenuse of the right triangle is one side of the equilateral triangle. If the shorter leg of the right triangle is 6 units, what is the distance between the two vertices that the triangles do not have in common? Express your answer in simplest radical form. [asy]\\ndraw((2,0)--(0,0)--(1,1.732)--(2,1.732)--(2,0)--(1,1.732));\\ndraw((2,1.632)--(1.9,1.632)--(1.9,1.732));\\nlabel(\\\"$60^\\\\circ$\\\",(1,1.732),2SE+E);\\nlabel(\\\"$30^\\\\circ$\\\",(2,0),5NNW+4N);\\nlabel(\\\"6\\\",(1.5,1.732),N);\\n[/asy]\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"Multiply the short leg of the right triangle by $\\\\sqrt{3}$ to find that the length of the longer leg is $6\\\\sqrt{3}$ units. Double the short leg of the right triangle to find that the length of the hypotenuse of the right triangle is 12 units. Since the hypotenuse of the right triangle is a side of the equilateral triangle, the side length of the equilateral triangle is also 12 units. By the Pythagorean theorem, the distance between the two vertices that the two triangles do not have in common is $\\\\sqrt{(6\\\\sqrt{3})^2+12^2}=\\\\sqrt{252}=\\\\boxed{6\\\\sqrt{7}}$ units. [asy]\\ndraw((2,0)--(0,0)--(1,sqrt(3))--(2,sqrt(3))--(2,0)--(1,sqrt(3)));\\ndraw((2,sqrt(3)-0.1)--(1.9,sqrt(3)-0.1)--(1.9,sqrt(3)));\\ndraw((0,0)--(2,sqrt(3)));\\nlabel(\\\"$60^\\\\circ$\\\",(1,sqrt(3)),2SE+E);\\nlabel(\\\"$30^\\\\circ$\\\",(2,0),5NNW+4N);\\nlabel(\\\"6\\\",(1.5,sqrt(3)),N);\\nlabel(\\\"$6\\\\sqrt{3}$\\\",(2,sqrt(3)/2),E);\\nlabel(\\\"12\\\",(1.5,sqrt(3)/2),SW);\\nlabel(\\\"12\\\",(1,0),S);\\n[/asy]\",\n", - " \"problem_id\": \"7\",\n", - " \"response_with_ans\": \"We have found the distance between the two vertices that the triangles do not have in common (C and D):\\n\\nx = \\u221a(252 + 72\\u221a3)\\n\\nThis is the simplest radical form for the required distance. \\n\\nTERMINATE\",\n", - " \"round\": 3,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"A $30^\\\\circ$-$60^\\\\circ$-$90^\\\\circ$ triangle is drawn on the exterior of an equilateral triangle so the hypotenuse of the right triangle is one side of the equilateral triangle. If the shorter leg of the right triangle is 6 units, what is the distance between the two vertices that the triangles do not have in common? Express your answer in simplest radical form. [asy]\\ndraw((2,0)--(0,0)--(1,1.732)--(2,1.732)--(2,0)--(1,1.732));\\ndraw((2,1.632)--(1.9,1.632)--(1.9,1.732));\\nlabel(\\\"$60^\\\\circ$\\\",(1,1.732),2SE+E);\\nlabel(\\\"$30^\\\\circ$\\\",(2,0),5NNW+4N);\\nlabel(\\\"6\\\",(1.5,1.732),N);\\n[/asy]\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Let's denote the vertices of the equilateral triangle as A, B, and C, where A is the right angle of the $30^\\\\circ$-$60^\\\\circ$-$90^\\\\circ$ triangle, B is adjacent to the $30^\\\\circ$ angle, and C is adjacent to the $60^\\\\circ$ angle. Let D be the vertex of the right triangle that is not a part of the equilateral triangle (the $60^\\\\circ$ angle).\\n\\nSince the shorter leg of the right triangle is 6 units (AD) and it is a $30^\\\\circ$-$60^\\\\circ$-$90^\\\\circ$ triangle, we know that:\\n1. The longer leg (BD) has a length equal to the shorter leg times $\\\\sqrt{3}$ ($6\\\\sqrt{3}$ units).\\n2. The hypotenuse (AB) has a length twice the size of the shorter leg (12 units).\\n\\nNow, we are trying to find the distance between vertices C and D. Let's call this distance x.\\n\\nTo calculate x, we can use the law of cosines on triangle $\\\\bigtriangleup BCD$. Here, the angle at vertex B is $180^\\\\circ - 60^\\\\circ = 120^\\\\circ$. We have the lengths of sides BC and BD, so we can calculate x using the law of cosines and then simplify the result.\\n\\nThe law of cosines formula is:\\nx^2 = (BC^2) + (BD^2) - 2(BC)(BD)cos(120^\\\\circ)\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"First, we need the length of side BC, which is equal to the length of side AB (since it is an equilateral triangle). Thus, BC = 12 units. Now we can use the law of cosines formula to find the value of x^2:\\n\\nx^2 = (12^2) + (6\\u221a3)^2 - 2(12)(6\\u221a3)cos(120\\u00ba)\\n\\nThe cosine of 120\\u00ba is -1/2, so:\\n\\nx^2 = 144 + 108 - 2(12)(6\\u221a3)(-1/2)\\n\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Let's simplify the expression for x^2:\\n\\nx^2 = 144 + 108 + (12)(6\\u221a3)\\n\\nNow we will calculate the result:\\n\\nx^2 = 144 + 108 + 72\\u221a3\\n\\nx^2 = 252 + 72\\u221a3\\n\\nNow, to find the value of x, we'll take the square root of both sides:\\n\\nx = \\u221a(252 + 72\\u221a3)\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"We have found the distance between the two vertices that the triangles do not have in common (C and D):\\n\\nx = \\u221a(252 + 72\\u221a3)\\n\\nThis is the simplest radical form for the required distance. \\n\\nTERMINATE\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 35.88237428665161,\n", - " \"trial\": -1\n", - "}\n", + "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", + "\n", + "Task: Math problem solving.\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " }\n", + "]actual test case to evaluate: {'problem': 'All 50 states as well as the District of Columbia and Puerto Rico, have distinct two-letter postal abbreviations. If a two-letter sequence of letters (such as CO or EE) is chosen at random, what is the probability that it is a postal abbreviation for one of the 50 states, the District of Columbia, or Puerto Rico? Express your answer as a common fraction.', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'There are $26\\\\cdot 26$ possible two-letter sequences of letters, since we have 26 choices for the first and then 26 choices for the second. But only 52 of these possibilities are valid, so our answer is $\\\\frac{52}{26\\\\cdot 26} =\\\\boxed{ \\\\frac{1}{13}}$.', 'problem_id': '8', 'response_with_ans': 'We\\'ll use a reliable source like USPS to fetch the postal abbreviations, and then calculate the probability.\\n\\n```python\\nimport requests\\nfrom bs4 import BeautifulSoup\\nimport fractions\\n\\nurl = \\'https://pe.usps.com/text/pub28/28apb.htm\\'\\nresponse = requests.get(url)\\nsoup = BeautifulSoup(response.text, \\'html.parser\\')\\n\\n# Find the table containing the state info\\nstate_table = soup.find_all(\\'table\\')[0]\\n\\n# Extract state abbreviations from the table\\nstate_abbreviations = [row.find_all(\\'td\\')[-1].text.strip() for row in state_table.find_all(\\'tr\\')[3:-2]]\\n\\n# Add District of Columbia and Puerto Rico.\\nstate_abbreviations.extend([\"DC\", \"PR\"])\\n\\nnum_valid_abbreviations = len(state_abbreviations)\\ntotal_possible_combinations = 26 * 26\\nprobability = fractions.Fraction(num_valid_abbreviations, total_possible_combinations)\\n\\nprint(f\"Valid postal abbreviations: {state_abbreviations}\")\\nprint(f\"Total valid postal abbreviations: {num_valid_abbreviations}\")\\nprint(f\"Total possible two-letter sequences: {total_possible_combinations}\")\\nprint(f\"Probability: {probability}\")\\n```\\n\\nPlease run the above code, and the output will show you the list of valid postal abbreviations, the total number of valid abbreviations, the total possible two-letter sequences, and the probability.', 'round': 1, 'messages': [{'content': 'All 50 states as well as the District of Columbia and Puerto Rico, have distinct two-letter postal abbreviations. If a two-letter sequence of letters (such as CO or EE) is chosen at random, what is the probability that it is a postal abbreviation for one of the 50 states, the District of Columbia, or Puerto Rico? Express your answer as a common fraction.', 'role': 'user'}, {'content': \"To calculate the probability, we first need to know the total number of possible two-letter sequences and the number of valid postal abbreviations.\\n\\nTo get the total number of possible two-letter sequences, we'll calculate all possible combinations using 26 letters of the alphabet. There are 26 options for choosing the first letter and 26 options for choosing the second letter, so the possible sequences are 26 * 26.\\n\\nNext, let's find the valid postal abbreviations for all 50 states, District of Columbia, and Puerto Rico. To do this, we can create code to download the postal abbreviations.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': 'We\\'ll use a reliable source like USPS to fetch the postal abbreviations, and then calculate the probability.\\n\\n```python\\nimport requests\\nfrom bs4 import BeautifulSoup\\nimport fractions\\n\\nurl = \\'https://pe.usps.com/text/pub28/28apb.htm\\'\\nresponse = requests.get(url)\\nsoup = BeautifulSoup(response.text, \\'html.parser\\')\\n\\n# Find the table containing the state info\\nstate_table = soup.find_all(\\'table\\')[0]\\n\\n# Extract state abbreviations from the table\\nstate_abbreviations = [row.find_all(\\'td\\')[-1].text.strip() for row in state_table.find_all(\\'tr\\')[3:-2]]\\n\\n# Add District of Columbia and Puerto Rico.\\nstate_abbreviations.extend([\"DC\", \"PR\"])\\n\\nnum_valid_abbreviations = len(state_abbreviations)\\ntotal_possible_combinations = 26 * 26\\nprobability = fractions.Fraction(num_valid_abbreviations, total_possible_combinations)\\n\\nprint(f\"Valid postal abbreviations: {state_abbreviations}\")\\nprint(f\"Total valid postal abbreviations: {num_valid_abbreviations}\")\\nprint(f\"Total possible two-letter sequences: {total_possible_combinations}\")\\nprint(f\"Probability: {probability}\")\\n```\\n\\nPlease run the above code, and the output will show you the list of valid postal abbreviations, the total number of valid abbreviations, the total possible two-letter sequences, and the probability.', 'role': 'assistant'}], 'time': 27.01692771911621, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", + "```json\n", "{\n", - " \"Problem Interpretation\": \"mostly accurate\",\n", + " \"Problem Interpretation\": \"completely accurate\",\n", " \"Mathematical Methodology\": \"completely effective\",\n", - " \"Calculation Correctness\": \"mostly correct\",\n", - " \"Explanation Clarity\": \"moderately clear\",\n", - " \"Code Efficiency\": \"not applicable\",\n", - " \"Code Correctness\": \"not applicable\"\n", + " \"Calculation Correctness\": \"completely correct\",\n", + " \"Explanation Clarity\": \"very clear\",\n", + " \"Code Efficiency\": \"moderately efficient\",\n", + " \"Code Correctness\": \"completely correct\"\n", "}\n", + "```\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"On a number line, the coordinates of $P$ and $Q$ are 8 and 48, respectively. The midpoint of $\\\\overline{PQ}$ is $B$, the midpoint of $\\\\overline{BQ}$ is $C$, and the midpoint of $\\\\overline{PC}$ is $D$. What is the coordinate of $D$?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"Since $B$ is midpoint of $\\\\overline{PQ}$, the coordinate of $B$ is $(8+48)/2 = 4+24 = 28$. Since $C$ is the midpoint of $\\\\overline{BQ}$, the coordinate of $C$ is $(28+48)/2 = 14+24=38$. Since $D$ is the midpoint of $\\\\overline{PC}$, the coordinate of $D$ is $(8+38)/2 = 4 + 19 = \\\\boxed{23}$.\\n\\n[asy]\\npair P, Q, B, C, D;\\n\\nP = (8,0);\\nQ = (48,0);\\nB = (P+Q)/2;\\nC = (B+Q)/2;\\nD = (P+C)/2;\\n\\ndot(P);\\ndot(Q);\\ndot(B);\\ndot(C);\\ndot(D);\\n\\ndraw(P--Q);\\nlabel(\\\"$P$\\\",P,S);\\nlabel(\\\"$Q$\\\",Q,S);\\nlabel(\\\"$B$\\\",B,S);\\nlabel(\\\"$C$\\\",C,S);\\nlabel(\\\"$D$\\\",D,S);\\n[/asy]\",\n", - " \"problem_id\": \"6\",\n", - " \"response_with_ans\": \"Let's find the coordinates using Python code.\\n\\n```python\\nP = 8\\nQ = 48\\n\\n# Find the midpoint of PQ (B)\\nB = (P + Q) / 2\\n\\n# Find the midpoint of BQ (C)\\nC = (B + Q) / 2\\n\\n# Find the midpoint of PC (D)\\nD = (P + C) / 2\\n\\nD\\n```\\n\\nExecute the above Python code to find the coordinate of D.\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"On a number line, the coordinates of $P$ and $Q$ are 8 and 48, respectively. The midpoint of $\\\\overline{PQ}$ is $B$, the midpoint of $\\\\overline{BQ}$ is $C$, and the midpoint of $\\\\overline{PC}$ is $D$. What is the coordinate of $D$?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the coordinate of D, we need to follow these steps:\\n1. Find the coordinate of B (midpoint of PQ)\\n2. Find the coordinate of C (midpoint of BQ)\\n3. Find the coordinate of D (midpoint of PC)\\n\\nI'll use Python code to find the coordinates of B, C, and D.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Let's find the coordinates using Python code.\\n\\n```python\\nP = 8\\nQ = 48\\n\\n# Find the midpoint of PQ (B)\\nB = (P + Q) / 2\\n\\n# Find the midpoint of BQ (C)\\nC = (B + Q) / 2\\n\\n# Find the midpoint of PC (D)\\nD = (P + C) / 2\\n\\nD\\n```\\n\\nExecute the above Python code to find the coordinate of D.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 9.041668176651001,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'On a number line, the coordinates of $P$ and $Q$ are 8 and 48, respectively. The midpoint of $\\\\overline{PQ}$ is $B$, the midpoint of $\\\\overline{BQ}$ is $C$, and the midpoint of $\\\\overline{PC}$ is $D$. What is the coordinate of $D$?', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'Since $B$ is midpoint of $\\\\overline{PQ}$, the coordinate of $B$ is $(8+48)/2 = 4+24 = 28$. Since $C$ is the midpoint of $\\\\overline{BQ}$, the coordinate of $C$ is $(28+48)/2 = 14+24=38$. Since $D$ is the midpoint of $\\\\overline{PC}$, the coordinate of $D$ is $(8+38)/2 = 4 + 19 = \\\\boxed{23}$.\\n\\n[asy]\\npair P, Q, B, C, D;\\n\\nP = (8,0);\\nQ = (48,0);\\nB = (P+Q)/2;\\nC = (B+Q)/2;\\nD = (P+C)/2;\\n\\ndot(P);\\ndot(Q);\\ndot(B);\\ndot(C);\\ndot(D);\\n\\ndraw(P--Q);\\nlabel(\"$P$\",P,S);\\nlabel(\"$Q$\",Q,S);\\nlabel(\"$B$\",B,S);\\nlabel(\"$C$\",C,S);\\nlabel(\"$D$\",D,S);\\n[/asy]', 'problem_id': '6', 'response_with_ans': \"Let's find the coordinates using Python code.\\n\\n```python\\nP = 8\\nQ = 48\\n\\n# Find the midpoint of PQ (B)\\nB = (P + Q) / 2\\n\\n# Find the midpoint of BQ (C)\\nC = (B + Q) / 2\\n\\n# Find the midpoint of PC (D)\\nD = (P + C) / 2\\n\\nD\\n```\\n\\nExecute the above Python code to find the coordinate of D.\", 'round': 1, 'messages': [{'content': 'On a number line, the coordinates of $P$ and $Q$ are 8 and 48, respectively. The midpoint of $\\\\overline{PQ}$ is $B$, the midpoint of $\\\\overline{BQ}$ is $C$, and the midpoint of $\\\\overline{PC}$ is $D$. What is the coordinate of $D$?', 'role': 'user'}, {'content': \"To find the coordinate of D, we need to follow these steps:\\n1. Find the coordinate of B (midpoint of PQ)\\n2. Find the coordinate of C (midpoint of BQ)\\n3. Find the coordinate of D (midpoint of PC)\\n\\nI'll use Python code to find the coordinates of B, C, and D.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"Let's find the coordinates using Python code.\\n\\n```python\\nP = 8\\nQ = 48\\n\\n# Find the midpoint of PQ (B)\\nB = (P + Q) / 2\\n\\n# Find the midpoint of BQ (C)\\nC = (B + Q) / 2\\n\\n# Find the midpoint of PC (D)\\nD = (P + C) / 2\\n\\nD\\n```\\n\\nExecute the above Python code to find the coordinate of D.\", 'role': 'assistant'}], 'time': 9.041668176651001, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", + "```json\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", " \"Mathematical Methodology\": \"completely effective\",\n", " \"Calculation Correctness\": \"completely correct\",\n", " \"Explanation Clarity\": \"very clear\",\n", - " \"Code Efficiency\": \"very efficient\",\n", + " \"Code Efficiency\": \"moderately efficient\",\n", " \"Code Correctness\": \"completely correct\"\n", "}\n", + "```\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"Triangle $ABC$ is a right triangle. If the measure of angle $PAB$ is $x^\\\\circ$ and the measure of angle $ACB$ is expressed in the form $(Mx+N)^\\\\circ$ with $M=1$, what is the value of $M+N$?\\n\\n[asy]\\ndraw((-10,0)--(20,0),linewidth(1),Arrows);\\ndraw((0,0)--(10,10/sqrt(3))--(10+10/3,0),linewidth(1));\\n\\ndraw((10,10/sqrt(3))+dir(-150)--(10,10/sqrt(3))+dir(-150)+dir(-60)--(10,10/sqrt(3))+dir(-60),linewidth(1));\\n\\ndot((-3,0));\\n\\ndraw(dir(180)..dir(105)..dir(30),linewidth(1));\\n\\nlabel(\\\"P\\\",(-3,0),NW);\\nlabel(\\\"A\\\",(0,0),S);\\nlabel(\\\"$x^\\\\circ$\\\",(-1,1),N);\\nlabel(\\\"B\\\",(10,10/sqrt(3)),N);\\nlabel(\\\"C\\\",(10+10/3,0),NE);\\n\\n[/asy]\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"Since $\\\\angle PAB$ and $\\\\angle BAC$ are supplementary, $\\\\angle BAC = 180^{\\\\circ} - x^\\\\circ$. Since the three angles of a triangle add up to $ 180^{\\\\circ} $, we have $\\\\angle ACB = 180^{\\\\circ} - 90^{\\\\circ} - (180^{\\\\circ} - x^\\\\circ) = x^\\\\circ - 90^{\\\\circ}$. Thus, $M + N = \\\\boxed{-89}$.\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"We know that $x + y = 180^\\\\circ$. From this equation, we can express $y$ in terms of $x$: $y = 180^\\\\circ - x$.\\n\\nNow we substitute the expression for $y$ in the expression of angle $ACB$:\\n$ACB = 90^\\\\circ - y = 90^\\\\circ - (180^\\\\circ - x) = x - 90^\\\\circ$.\\n\\nComparing this expression with the given form $(Mx + N)^\\\\circ$, we can see that $M = 1$ and $N = -90$. Therefore, $M + N = 1 + (-90) = -89$.\\n\\nThe value of $M + N$ is $-89$.\",\n", - " \"round\": 2,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"Triangle $ABC$ is a right triangle. If the measure of angle $PAB$ is $x^\\\\circ$ and the measure of angle $ACB$ is expressed in the form $(Mx+N)^\\\\circ$ with $M=1$, what is the value of $M+N$?\\n\\n[asy]\\ndraw((-10,0)--(20,0),linewidth(1),Arrows);\\ndraw((0,0)--(10,10/sqrt(3))--(10+10/3,0),linewidth(1));\\n\\ndraw((10,10/sqrt(3))+dir(-150)--(10,10/sqrt(3))+dir(-150)+dir(-60)--(10,10/sqrt(3))+dir(-60),linewidth(1));\\n\\ndot((-3,0));\\n\\ndraw(dir(180)..dir(105)..dir(30),linewidth(1));\\n\\nlabel(\\\"P\\\",(-3,0),NW);\\nlabel(\\\"A\\\",(0,0),S);\\nlabel(\\\"$x^\\\\circ$\\\",(-1,1),N);\\nlabel(\\\"B\\\",(10,10/sqrt(3)),N);\\nlabel(\\\"C\\\",(10+10/3,0),NE);\\n\\n[/asy]\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To solve this problem, we need to find the relationship between angle $x$ and angle $ACB$. We know that triangle $ABC$ is a right triangle, so the sum of angles $ACB$ and $ABC$ must be $90^\\\\circ$. Let's call the angle $ABC$ as $y$. Therefore, angle $ACB = 90^\\\\circ - y$.\\n\\nNow, we need to find the relationship between angles $x$ and $y$. Since $PAB$ and $ABC$ share the same vertex $A$ and angle $PAB$ is opposite angle $ABC$, they are explementary (their sum is $180^\\\\circ$). So, we have $x + y = 180^\\\\circ$.\\n\\nNow, we can express $y$ in terms of $x$ and then substitute the expression for $y$ in the expression of angle $ACB$. Finally, we can compare angle $ACB$'s expression with the given form $(Mx + N)^\\\\circ$ to find the values of $M$ and $N$.\\n\\nLet me do the calculations.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"We know that $x + y = 180^\\\\circ$. From this equation, we can express $y$ in terms of $x$: $y = 180^\\\\circ - x$.\\n\\nNow we substitute the expression for $y$ in the expression of angle $ACB$:\\n$ACB = 90^\\\\circ - y = 90^\\\\circ - (180^\\\\circ - x) = x - 90^\\\\circ$.\\n\\nComparing this expression with the given form $(Mx + N)^\\\\circ$, we can see that $M = 1$ and $N = -90$. Therefore, $M + N = 1 + (-90) = -89$.\\n\\nThe value of $M + N$ is $-89$.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"TERMINATE\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 28.305670976638794,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'Triangle $ABC$ is a right triangle. If the measure of angle $PAB$ is $x^\\\\circ$ and the measure of angle $ACB$ is expressed in the form $(Mx+N)^\\\\circ$ with $M=1$, what is the value of $M+N$?\\n\\n[asy]\\ndraw((-10,0)--(20,0),linewidth(1),Arrows);\\ndraw((0,0)--(10,10/sqrt(3))--(10+10/3,0),linewidth(1));\\n\\ndraw((10,10/sqrt(3))+dir(-150)--(10,10/sqrt(3))+dir(-150)+dir(-60)--(10,10/sqrt(3))+dir(-60),linewidth(1));\\n\\ndot((-3,0));\\n\\ndraw(dir(180)..dir(105)..dir(30),linewidth(1));\\n\\nlabel(\"P\",(-3,0),NW);\\nlabel(\"A\",(0,0),S);\\nlabel(\"$x^\\\\circ$\",(-1,1),N);\\nlabel(\"B\",(10,10/sqrt(3)),N);\\nlabel(\"C\",(10+10/3,0),NE);\\n\\n[/asy]', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'Since $\\\\angle PAB$ and $\\\\angle BAC$ are supplementary, $\\\\angle BAC = 180^{\\\\circ} - x^\\\\circ$. Since the three angles of a triangle add up to $ 180^{\\\\circ} $, we have $\\\\angle ACB = 180^{\\\\circ} - 90^{\\\\circ} - (180^{\\\\circ} - x^\\\\circ) = x^\\\\circ - 90^{\\\\circ}$. Thus, $M + N = \\\\boxed{-89}$.', 'problem_id': '0', 'response_with_ans': 'We know that $x + y = 180^\\\\circ$. From this equation, we can express $y$ in terms of $x$: $y = 180^\\\\circ - x$.\\n\\nNow we substitute the expression for $y$ in the expression of angle $ACB$:\\n$ACB = 90^\\\\circ - y = 90^\\\\circ - (180^\\\\circ - x) = x - 90^\\\\circ$.\\n\\nComparing this expression with the given form $(Mx + N)^\\\\circ$, we can see that $M = 1$ and $N = -90$. Therefore, $M + N = 1 + (-90) = -89$.\\n\\nThe value of $M + N$ is $-89$.', 'round': 2, 'messages': [{'content': 'Triangle $ABC$ is a right triangle. If the measure of angle $PAB$ is $x^\\\\circ$ and the measure of angle $ACB$ is expressed in the form $(Mx+N)^\\\\circ$ with $M=1$, what is the value of $M+N$?\\n\\n[asy]\\ndraw((-10,0)--(20,0),linewidth(1),Arrows);\\ndraw((0,0)--(10,10/sqrt(3))--(10+10/3,0),linewidth(1));\\n\\ndraw((10,10/sqrt(3))+dir(-150)--(10,10/sqrt(3))+dir(-150)+dir(-60)--(10,10/sqrt(3))+dir(-60),linewidth(1));\\n\\ndot((-3,0));\\n\\ndraw(dir(180)..dir(105)..dir(30),linewidth(1));\\n\\nlabel(\"P\",(-3,0),NW);\\nlabel(\"A\",(0,0),S);\\nlabel(\"$x^\\\\circ$\",(-1,1),N);\\nlabel(\"B\",(10,10/sqrt(3)),N);\\nlabel(\"C\",(10+10/3,0),NE);\\n\\n[/asy]', 'role': 'user'}, {'content': \"To solve this problem, we need to find the relationship between angle $x$ and angle $ACB$. We know that triangle $ABC$ is a right triangle, so the sum of angles $ACB$ and $ABC$ must be $90^\\\\circ$. Let's call the angle $ABC$ as $y$. Therefore, angle $ACB = 90^\\\\circ - y$.\\n\\nNow, we need to find the relationship between angles $x$ and $y$. Since $PAB$ and $ABC$ share the same vertex $A$ and angle $PAB$ is opposite angle $ABC$, they are explementary (their sum is $180^\\\\circ$). So, we have $x + y = 180^\\\\circ$.\\n\\nNow, we can express $y$ in terms of $x$ and then substitute the expression for $y$ in the expression of angle $ACB$. Finally, we can compare angle $ACB$'s expression with the given form $(Mx + N)^\\\\circ$ to find the values of $M$ and $N$.\\n\\nLet me do the calculations.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': 'We know that $x + y = 180^\\\\circ$. From this equation, we can express $y$ in terms of $x$: $y = 180^\\\\circ - x$.\\n\\nNow we substitute the expression for $y$ in the expression of angle $ACB$:\\n$ACB = 90^\\\\circ - y = 90^\\\\circ - (180^\\\\circ - x) = x - 90^\\\\circ$.\\n\\nComparing this expression with the given form $(Mx + N)^\\\\circ$, we can see that $M = 1$ and $N = -90$. Therefore, $M + N = 1 + (-90) = -89$.\\n\\nThe value of $M + N$ is $-89$.', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': 'TERMINATE', 'role': 'assistant'}], 'time': 28.305670976638794, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "{\n", - " \"Problem Interpretation\": \"completely accurate\",\n", - " \"Mathematical Methodology\": \"completely effective\",\n", - " \"Calculation Correctness\": \"completely correct\",\n", - " \"Explanation Clarity\": \"very clear\",\n", - " \"Code Efficiency\": \"not at all efficient\",\n", - " \"Code Correctness\": \"completely correct\"\n", + " \"Problem Interpretation\": \"completely accurate\",\n", + " \"Mathematical Methodology\": \"completely effective\",\n", + " \"Calculation Correctness\": \"completely correct\",\n", + " \"Explanation Clarity\": \"very clear\",\n", + " \"Code Efficiency\": \"not applicable\",\n", + " \"Code Correctness\": \"not applicable\"\n", "}\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"Simplify: $\\\\frac{\\\\sqrt{2.5^2-0.7^2}}{2.7-2.5}$.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"We have \\\\begin{align*}\\n\\\\frac{\\\\sqrt{2.5^2 - 0.7^2}}{2.7-2.5} &= \\\\frac{\\\\sqrt{6.25 - 0.49}}{2.7-2.5} = \\\\frac{\\\\sqrt{5.76}}{0.2} = \\\\frac{\\\\sqrt{576/100}}{0.2}\\\\\\\\\\n&= \\\\frac{\\\\sqrt{576}/\\\\sqrt{100}}{0.2} = \\\\frac{24/10}{0.2} = \\\\frac{2.4}{0.2} = \\\\boxed{12}.\\\\end{align*}\",\n", - " \"problem_id\": \"2\",\n", - " \"response_with_ans\": \"```python\\nimport math\\n\\nnumerator = math.sqrt(2.5 ** 2 - 0.7 ** 2)\\ndenominator = 2.7 - 2.5\\n\\nresult = numerator / denominator\\nresult\\n```\\n\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"Simplify: $\\\\frac{\\\\sqrt{2.5^2-0.7^2}}{2.7-2.5}$.\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To simplify the given expression, let's break it down step by step and use Python to calculate the values:\\n\\nStep 1: Calculate the values inside the square root: $2.5^2 - 0.7^2$\\nStep 2: Take the square root of the result\\nStep 3: Calculate the denominator: $2.7 - 2.5$\\nStep 4: Divide the result of Step 2 by the result of Step 3\\n\\nNow, let's execute Python code to get the result.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"```python\\nimport math\\n\\nnumerator = math.sqrt(2.5 ** 2 - 0.7 ** 2)\\ndenominator = 2.7 - 2.5\\n\\nresult = numerator / denominator\\nresult\\n```\\n\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 8.612567901611328,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'John counts up from 1 to 13, and then immediately counts down again to 1, and then back up to 13, and so on, alternately counting up and down: \\\\begin{align*}\\n&(1, 2, 3,4,5,6,7,8,9,10,11,12,13,\\\\\\\\\\n&\\\\qquad\\\\qquad12,11,10,9,8,7,6,5,4,3,2,1,2,3,4,\\\\ldots ).\\n\\\\end{align*} What is the $5000^{\\\\text{th}}$ integer in his list?', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'We can treat this list as a sequence with a repetitive pattern. We see the sequence repeats itself every 24 elements (from 1 to 13 then back to 2). When 5000 is divided by 24, its remainder is 8. Therefore we see the $5000^{\\\\text{th}}$ integer is the same as the $8^{\\\\text{th}}$ integer, which is $\\\\boxed{8}$.', 'problem_id': '4', 'response_with_ans': 'We can solve this problem by finding which \"group\" the 5000th number belongs to, and then determine the number within that group. Each group is either counting up from 1 to 13 or counting down from 13 to 1. There are 13 numbers in each direction, but since 1 is counted twice (once at the end of a down-count and once at the beginning of an up-count), counting up and down would consist of 25 numbers (13 up + 12 down).\\n\\nLet\\'s first find out which group the 5000th number belongs to and how many numbers in total are in the complete up and down groups before 5000th number using code:\\n\\n```python\\nnum_group = 5000 // 25\\nremainder = 5000 % 25\\ntotal_numbers_before = num_group * 25\\nprint(\\'Number of complete groups:\\', num_group)\\nprint(\\'Remainder:\\', remainder)\\nprint(\\'Numbers before 5000th number:\\', total_numbers_before)\\n```\\n\\nAfter knowing these values, I\\'ll determine if the 5000th number is in up-count or down-count and its value.\\n', 'round': 0, 'messages': [{'content': 'John counts up from 1 to 13, and then immediately counts down again to 1, and then back up to 13, and so on, alternately counting up and down: \\\\begin{align*}\\n&(1, 2, 3,4,5,6,7,8,9,10,11,12,13,\\\\\\\\\\n&\\\\qquad\\\\qquad12,11,10,9,8,7,6,5,4,3,2,1,2,3,4,\\\\ldots ).\\n\\\\end{align*} What is the $5000^{\\\\text{th}}$ integer in his list?', 'role': 'user'}, {'content': 'We can solve this problem by finding which \"group\" the 5000th number belongs to, and then determine the number within that group. Each group is either counting up from 1 to 13 or counting down from 13 to 1. There are 13 numbers in each direction, but since 1 is counted twice (once at the end of a down-count and once at the beginning of an up-count), counting up and down would consist of 25 numbers (13 up + 12 down).\\n\\nLet\\'s first find out which group the 5000th number belongs to and how many numbers in total are in the complete up and down groups before 5000th number using code:\\n\\n```python\\nnum_group = 5000 // 25\\nremainder = 5000 % 25\\ntotal_numbers_before = num_group * 25\\nprint(\\'Number of complete groups:\\', num_group)\\nprint(\\'Remainder:\\', remainder)\\nprint(\\'Numbers before 5000th number:\\', total_numbers_before)\\n```\\n\\nAfter knowing these values, I\\'ll determine if the 5000th number is in up-count or down-count and its value.\\n', 'role': 'assistant'}], 'time': 16.342331409454346, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "{\n", - " \"Problem Interpretation\": \"completely accurate\",\n", - " \"Mathematical Methodology\": \"completely effective\",\n", - " \"Calculation Correctness\": \"completely correct\",\n", - " \"Explanation Clarity\": \"very clear\",\n", - " \"Code Efficiency\": \"moderately efficient\",\n", - " \"Code Correctness\": \"completely correct\"\n", + " \"Problem Interpretation\": \"completely accurate\",\n", + " \"Mathematical Methodology\": \"mostly effective\",\n", + " \"Calculation Correctness\": \"mostly correct\",\n", + " \"Explanation Clarity\": \"very clear\",\n", + " \"Code Efficiency\": \"moderately efficient\",\n", + " \"Code Correctness\": \"mostly correct\"\n", "}\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"Bekah has exactly three brass house number digits: 2, 3 and 5. How many distinct numbers can she form using one or more of the digits?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"If Bekah uses only one digit, she can form three numbers. If she uses two digits, she has three choices for the tens place and two for the units, so she can form six numbers. Finally, if Bekah uses all three digits, she has three choices for the hundreds place, two for the tens, and one for the units, so she can form six numbers. Thus, Bekah can form $3 + 6 + 6 = \\\\boxed{15}$ distinct numbers.\",\n", - " \"problem_id\": \"11\",\n", - " \"response_with_ans\": \"To find the answer, we'll consider all possible combinations that Bekah can make using the three digits she has:\\n1. Single-digit numbers: 2, 3, and 5\\n2. Two-digits numbers using the digits: 23, 25, 32, 35, 52, and 53\\n3. Three-digits number using all the digits: 235, 253, 325, 352, 523, and 532\\n\\nLet's calculate the number of distinct numbers Bekah can form using one or more of the digits with Python code:\\n\\n```python\\nfrom itertools import permutations\\n\\ndigits = [2, 3, 5]\\n\\n# Single-digit numbers\\none_digit_numbers = set(digits)\\n\\n# Two-digits numbers\\ntwo_digit_numbers = set(int(''.join(map(str, p))) for p in permutations(digits, 2))\\n\\n# Three-digits numbers\\nthree_digit_numbers = set(int(''.join(map(str, p))) for p in permutations(digits, 3))\\n\\ndistinct_numbers = one_digit_numbers | two_digit_numbers | three_digit_numbers\\ntotal_distinct_numbers = len(distinct_numbers)\\n\\nprint(\\\"Distinct numbers:\\\", distinct_numbers)\\nprint(\\\"Total distinct numbers:\\\", total_distinct_numbers)\\n```\\n\\nPlease run the above Python code to calculate the total number of distinct numbers Bekah can form using one or more of the digits.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"Bekah has exactly three brass house number digits: 2, 3 and 5. How many distinct numbers can she form using one or more of the digits?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the answer, we'll consider all possible combinations that Bekah can make using the three digits she has:\\n1. Single-digit numbers: 2, 3, and 5\\n2. Two-digits numbers using the digits: 23, 25, 32, 35, 52, and 53\\n3. Three-digits number using all the digits: 235, 253, 325, 352, 523, and 532\\n\\nLet's calculate the number of distinct numbers Bekah can form using one or more of the digits with Python code:\\n\\n```python\\nfrom itertools import permutations\\n\\ndigits = [2, 3, 5]\\n\\n# Single-digit numbers\\none_digit_numbers = set(digits)\\n\\n# Two-digits numbers\\ntwo_digit_numbers = set(int(''.join(map(str, p))) for p in permutations(digits, 2))\\n\\n# Three-digits numbers\\nthree_digit_numbers = set(int(''.join(map(str, p))) for p in permutations(digits, 3))\\n\\ndistinct_numbers = one_digit_numbers | two_digit_numbers | three_digit_numbers\\ntotal_distinct_numbers = len(distinct_numbers)\\n\\nprint(\\\"Distinct numbers:\\\", distinct_numbers)\\nprint(\\\"Total distinct numbers:\\\", total_distinct_numbers)\\n```\\n\\nPlease run the above Python code to calculate the total number of distinct numbers Bekah can form using one or more of the digits.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 15.620970249176025,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'Each interior angle of a polygon measures 170 degrees. How many sides does the polygon have?', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'The sum of the interior angles of a polygon is $180(n-2)$, where $n$ is the number of sides. That means each interior angle has a measure of $\\\\frac{180(n-2)}{n}$ degrees. We set this equal to 170 degrees and solve for $n$. \\\\begin{align*}\\n\\\\frac{180(n-2)}{n}&=170\\\\\\\\\\n\\\\Rightarrow \\\\qquad 180n-360&=170n\\\\\\\\\\\\Rightarrow \\\\qquad 10n&=360\\\\\\\\\\n\\\\Rightarrow\\\\qquad n&=36.\\n\\\\end{align*} The polygon has $\\\\boxed{36}$ sides.', 'problem_id': '14', 'response_with_ans': \"Quick note: A single backtick should be used for inline code, and triple backticks should be used for code blocks. It seems I accidentally used single backticks for the code block in my previous response. Please use the code below instead:\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\nn = symbols('n')\\nequation = Eq(n * 170, (n - 2) * 180)\\nsides = solve(equation, n)[0]\\nprint(sides)\\n```\\n\", 'round': 1, 'messages': [{'content': 'Each interior angle of a polygon measures 170 degrees. How many sides does the polygon have?', 'role': 'user'}, {'content': \"We can use the formula for the sum of the interior angles of a polygon to solve this problem. The formula is:\\n\\nsum_of_interior_angles = (n - 2) * 180\\n\\nwhere n is the number of sides.\\n\\nAs each interior angle measures 170 degrees, we can write the equation:\\n\\nn * 170 = (n - 2) * 180\\n\\nLet's solve for the number of sides (n) using Python code.\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\nn = symbols('n')\\nequation = Eq(n * 170, (n - 2) * 180)\\nsides = solve(equation, n)[0]\\nsides\\n\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"Quick note: A single backtick should be used for inline code, and triple backticks should be used for code blocks. It seems I accidentally used single backticks for the code block in my previous response. Please use the code below instead:\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\nn = symbols('n')\\nequation = Eq(n * 170, (n - 2) * 180)\\nsides = solve(equation, n)[0]\\nprint(sides)\\n```\\n\", 'role': 'assistant'}], 'time': 16.42014193534851, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "{\n", - " \"Problem Interpretation\": \"completely accurate\",\n", - " \"Mathematical Methodology\": \"completely effective\",\n", - " \"Calculation Correctness\": \"completely correct\",\n", - " \"Explanation Clarity\": \"completely clear\",\n", - " \"Code Efficiency\": \"very efficient\",\n", - " \"Code Correctness\": \"completely correct\"\n", + " \"Problem Interpretation\": \"completely accurate\",\n", + " \"Mathematical Methodology\": \"completely effective\",\n", + " \"Calculation Correctness\": \"completely correct\",\n", + " \"Explanation Clarity\": \"very clear\",\n", + " \"Code Efficiency\": \"moderately efficient\",\n", + " \"Code Correctness\": \"completely correct\"\n", "}\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: false\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " }\n", + "]actual test case to evaluate: {'problem': 'An equilateral triangle has a side of length 12 inches. What is the area of the triangle, in square inches? Express your answer in simplest radical form.', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'The area of an equilateral triangle with side length $s$ is $s^2\\\\sqrt{3}/4$. We have $s = 12$, so our area is $12^2\\\\sqrt{3}/4 = \\\\boxed{36\\\\sqrt{3}}$.', 'problem_id': '18', 'response_with_ans': '```python\\nimport math\\n\\nside_length = 12\\narea = (math.sqrt(3) * side_length**2) / 4\\nprint(area)\\n```', 'round': 1, 'messages': [{'content': 'An equilateral triangle has a side of length 12 inches. What is the area of the triangle, in square inches? Express your answer in simplest radical form.', 'role': 'user'}, {'content': \"To find the area of an equilateral triangle with a side of length s, we can use the formula:\\n\\narea = (sqrt(3) * s²) / 4\\n\\nIn this case, s = 12 inches. Let's calculate the area.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '```python\\nimport math\\n\\nside_length = 12\\narea = (math.sqrt(3) * side_length**2) / 4\\nprint(area)\\n```', 'role': 'assistant'}], 'time': 14.153439283370972, 'trial': -1}\n", "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", + "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", + "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", + "\n", + "{\n", + " \"Problem Interpretation\": \"completely accurate\",\n", + " \"Mathematical Methodology\": \"completely effective\",\n", + " \"Calculation Correctness\": \"completely correct\",\n", + " \"Explanation Clarity\": \"completely clear\",\n", + " \"Code Efficiency\": \"moderately efficient\",\n", + " \"Code Correctness\": \"completely correct\"\n", "}\n", "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", + "\n", + "Task: Math problem solving.\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"In the diagram, $AB,$ $BC,$ $CD,$ $DE,$ $EF,$ $FG,$ $GH,$ and $HK$ all have length $4,$ and all angles are right angles, with the exception of the angles at $D$ and $F.$\\n\\n[asy]\\ndraw((0,0)--(0,4)--(4,4)--(4,8)--(6.8284,5.1716)--(9.6569,8)--(9.6569,4)--(13.6569,4)--(13.6569,0)--cycle,black+linewidth(1));\\ndraw((0,0)--(0.5,0)--(0.5,0.5)--(0,0.5)--cycle,black+linewidth(1));\\ndraw((0,4)--(0.5,4)--(0.5,3.5)--(0,3.5)--cycle,black+linewidth(1));\\ndraw((4,4)--(4,4.5)--(3.5,4.5)--(3.5,4)--cycle,black+linewidth(1));\\ndraw((6.8284,5.1716)--(7.0784,5.4216)--(6.8284,5.6716)--(6.5784,5.4216)--cycle,black+linewidth(1));\\ndraw((9.6569,4)--(10.1569,4)--(10.1569,4.5)--(9.6569,4.5)--cycle,black+linewidth(1));\\ndraw((13.6569,4)--(13.1569,4)--(13.1569,3.5)--(13.6569,3.5)--cycle,black+linewidth(1));\\ndraw((13.6569,0)--(13.1569,0)--(13.1569,0.5)--(13.6569,0.5)--cycle,black+linewidth(1));\\nlabel(\\\"$A$\\\",(0,0),W);\\nlabel(\\\"$B$\\\",(0,4),NW);\\nlabel(\\\"$C$\\\",(4,4),S);\\nlabel(\\\"$D$\\\",(4,8),N);\\nlabel(\\\"$E$\\\",(6.8284,5.1716),S);\\nlabel(\\\"$F$\\\",(9.6569,8),N);\\nlabel(\\\"$G$\\\",(9.6569,4),S);\\nlabel(\\\"$H$\\\",(13.6569,4),NE);\\nlabel(\\\"$K$\\\",(13.6569,0),E);\\n[/asy]\\n\\nDetermine the length of $DF.$\\n\\n[asy]\\ndraw((0,0)--(2.8284,-2.8284)--(5.6568,0),black+linewidth(1));\\ndraw((0,0)--(5.6568,0),black+linewidth(1)+dashed);\\ndraw((2.8284,-2.8284)--(3.0784,-2.5784)--(2.8284,-2.3284)--(2.5784,-2.5784)--cycle,black+linewidth(1));\\nlabel(\\\"$D$\\\",(0,0),N);\\nlabel(\\\"$E$\\\",(2.8284,-2.8284),S);\\nlabel(\\\"$F$\\\",(5.6568,0),N);\\n[/asy]\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"Since $DE=EF=4$ and $\\\\angle DEF = 90^\\\\circ,$ by the Pythagorean Theorem, \\\\begin{align*}\\nDF^2 &= DE^2+EF^2 \\\\\\\\\\n&= 4^2+4^2 \\\\\\\\\\n&=32,\\n\\\\end{align*}so that $DF = \\\\sqrt{32}=\\\\boxed{4\\\\sqrt{2}}.$\",\n", - " \"problem_id\": \"16\",\n", - " \"response_with_ans\": \"Now let's calculate the square of DF using Python.\\n\\n```python\\nDH = 9.6569\\nHG = 5.6569\\ncos_alpha_beta = 0\\n\\nDF_squared = DH**2 + HG**2 - 2 * DH * HG * cos_alpha_beta\\nDF_squared\\n```\",\n", - " \"round\": 2,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"In the diagram, $AB,$ $BC,$ $CD,$ $DE,$ $EF,$ $FG,$ $GH,$ and $HK$ all have length $4,$ and all angles are right angles, with the exception of the angles at $D$ and $F.$\\n\\n[asy]\\ndraw((0,0)--(0,4)--(4,4)--(4,8)--(6.8284,5.1716)--(9.6569,8)--(9.6569,4)--(13.6569,4)--(13.6569,0)--cycle,black+linewidth(1));\\ndraw((0,0)--(0.5,0)--(0.5,0.5)--(0,0.5)--cycle,black+linewidth(1));\\ndraw((0,4)--(0.5,4)--(0.5,3.5)--(0,3.5)--cycle,black+linewidth(1));\\ndraw((4,4)--(4,4.5)--(3.5,4.5)--(3.5,4)--cycle,black+linewidth(1));\\ndraw((6.8284,5.1716)--(7.0784,5.4216)--(6.8284,5.6716)--(6.5784,5.4216)--cycle,black+linewidth(1));\\ndraw((9.6569,4)--(10.1569,4)--(10.1569,4.5)--(9.6569,4.5)--cycle,black+linewidth(1));\\ndraw((13.6569,4)--(13.1569,4)--(13.1569,3.5)--(13.6569,3.5)--cycle,black+linewidth(1));\\ndraw((13.6569,0)--(13.1569,0)--(13.1569,0.5)--(13.6569,0.5)--cycle,black+linewidth(1));\\nlabel(\\\"$A$\\\",(0,0),W);\\nlabel(\\\"$B$\\\",(0,4),NW);\\nlabel(\\\"$C$\\\",(4,4),S);\\nlabel(\\\"$D$\\\",(4,8),N);\\nlabel(\\\"$E$\\\",(6.8284,5.1716),S);\\nlabel(\\\"$F$\\\",(9.6569,8),N);\\nlabel(\\\"$G$\\\",(9.6569,4),S);\\nlabel(\\\"$H$\\\",(13.6569,4),NE);\\nlabel(\\\"$K$\\\",(13.6569,0),E);\\n[/asy]\\n\\nDetermine the length of $DF.$\\n\\n[asy]\\ndraw((0,0)--(2.8284,-2.8284)--(5.6568,0),black+linewidth(1));\\ndraw((0,0)--(5.6568,0),black+linewidth(1)+dashed);\\ndraw((2.8284,-2.8284)--(3.0784,-2.5784)--(2.8284,-2.3284)--(2.5784,-2.5784)--cycle,black+linewidth(1));\\nlabel(\\\"$D$\\\",(0,0),N);\\nlabel(\\\"$E$\\\",(2.8284,-2.8284),S);\\nlabel(\\\"$F$\\\",(5.6568,0),N);\\n[/asy]\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Let's use coordinate geometry to determine the coordinates of points D, E, and F, which will allow us to compute the Euclidean distance between points D and F.\\n\\n1. Assign coordinates to points A, B, C, D, E, F, G, H, and K.\\n2. Calculate the slope and equation of line DE and line FG.\\n3. Use the slopes and the coordinates of points E and G to compute the coordinates of point F.\\n4. Calculate the length of DF using the distance formula.\\n\\nLet's start by assigning coordinates:\\n\\n1. A(0, 0), B(0, 4), C(4, 4), D(4, 8), G(9.6569, 4), H(13.6569, 4), and K(13.6569, 0).\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Since the angles at points D and F are not right angles, we need to use some trigonometry to find points E and F. We already have the coordinates of points D and G.\\n\\nLet \\u03b1 be the angle between the horizontal line and the line DE, and \\u03b2 be the angle between the horizontal line and the line FG.\\n\\nLet P be the intersection point between the horizontal lines passing through points D and F. Therefore, the triangle DPF is a right triangle. \\n\\nSince we know the angles \\u03b1, \\u03b2, and the length DG, we can determine the length DF using the law of cosines:\\n\\nLet's calculate the length of DG first:\\n\\nDG = DH + HG = (13.6569 - 4) + (9.6569 - 4) = 9.6569 + 5.6569 = 15.3138\\n\\nNow, let \\u03b1 be the angle between line DH and DF, and \\u03b2 be the angle between line HG and DF. We can use the law of cosines to calculate the length of DF:\\n\\nDF^2 = DH^2 + HG^2 - 2 * DH * HG * cos(\\u03b1 + \\u03b2)\\n\\nWe also know angle DAB = \\u03b1, angle GHB = \\u03b2 and AB = FG = 4.\\n\\nWe can calculate cos(\\u03b1 + \\u03b2) using the cosine formula for the addition of angles:\\n\\ncos(\\u03b1 + \\u03b2) = cos(\\u03b1) * cos(\\u03b2) - sin(\\u03b1) * sin(\\u03b2)\\n\\nTo find sin(\\u03b1) and cos(\\u03b1), we can use right triangle with sides DA, AB, and DB. Since DB = BA = 4, we have:\\n\\ncos(\\u03b1) = AD / DB = 4 / 4 = 1\\nsin(\\u03b1) = BA / DB = 4 / 4 = 1\\n\\nSimilarly, for angle \\u03b2, we have right triangle with sides GH, HB, and GB. Since GB = BH = 4, we have:\\n\\ncos(\\u03b2) = HG / GB = 4 / 4 = 1\\nsin(\\u03b2) = HB / GB = 4 / 4 = 1\\n\\nNow, let's calculate cos(\\u03b1 + \\u03b2):\\n\\ncos(\\u03b1 + \\u03b2) = cos(\\u03b1) * cos(\\u03b2) - sin(\\u03b1) * sin(\\u03b2) = 1 * 1 - 1 * 1 = 0\\n\\nFinally, let's calculate DF using the law of cosines:\\n\\nDF^2 = DH^2 + HG^2 - 2 * DH * HG * cos(\\u03b1 + \\u03b2)\\nDF^2 = 9.6569^2 + 5.6569^2 - 2 * 9.6569 * 5.6569 * 0\\n\\nNow let's compute the square of DF.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Now let's calculate the square of DF using Python.\\n\\n```python\\nDH = 9.6569\\nHG = 5.6569\\ncos_alpha_beta = 0\\n\\nDF_squared = DH**2 + HG**2 - 2 * DH * HG * cos_alpha_beta\\nDF_squared\\n```\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 44.82826566696167,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'Simplify: $\\\\frac{\\\\sqrt{2.5^2-0.7^2}}{2.7-2.5}$.', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'We have \\\\begin{align*}\\n\\\\frac{\\\\sqrt{2.5^2 - 0.7^2}}{2.7-2.5} &= \\\\frac{\\\\sqrt{6.25 - 0.49}}{2.7-2.5} = \\\\frac{\\\\sqrt{5.76}}{0.2} = \\\\frac{\\\\sqrt{576/100}}{0.2}\\\\\\\\\\n&= \\\\frac{\\\\sqrt{576}/\\\\sqrt{100}}{0.2} = \\\\frac{24/10}{0.2} = \\\\frac{2.4}{0.2} = \\\\boxed{12}.\\\\end{align*}', 'problem_id': '2', 'response_with_ans': '```python\\nimport math\\n\\nnumerator = math.sqrt(2.5 ** 2 - 0.7 ** 2)\\ndenominator = 2.7 - 2.5\\n\\nresult = numerator / denominator\\nresult\\n```\\n', 'round': 1, 'messages': [{'content': 'Simplify: $\\\\frac{\\\\sqrt{2.5^2-0.7^2}}{2.7-2.5}$.', 'role': 'user'}, {'content': \"To simplify the given expression, let's break it down step by step and use Python to calculate the values:\\n\\nStep 1: Calculate the values inside the square root: $2.5^2 - 0.7^2$\\nStep 2: Take the square root of the result\\nStep 3: Calculate the denominator: $2.7 - 2.5$\\nStep 4: Divide the result of Step 2 by the result of Step 3\\n\\nNow, let's execute Python code to get the result.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '```python\\nimport math\\n\\nnumerator = math.sqrt(2.5 ** 2 - 0.7 ** 2)\\ndenominator = 2.7 - 2.5\\n\\nresult = numerator / denominator\\nresult\\n```\\n', 'role': 'assistant'}], 'time': 8.612567901611328, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", "{\n", @@ -3296,150 +2487,130 @@ " \"Mathematical Methodology\": \"completely effective\",\n", " \"Calculation Correctness\": \"completely correct\",\n", " \"Explanation Clarity\": \"very clear\",\n", - " \"Code Efficiency\": \"mostly efficient\",\n", - " \"Code Correctness\": \"mostly correct\"\n", + " \"Code Efficiency\": \"moderately efficient\",\n", + " \"Code Correctness\": \"completely correct\"\n", "}\n", "\n", "--------------------------------------------------------------------------------\n", - "actual label for this case: true\n", "\u001b[33mquantifier_user\u001b[0m (to quantifier):\n", "\n", "Task: Math problem solving.\n", - "Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", - "Task successful example: {\n", - " \"problem\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Number Theory\",\n", - " \"solution\": \"Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$\",\n", - " \"problem_id\": \"0\",\n", - " \"response_with_ans\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"round\": 0,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"What is the sum of all the distinct positive two-digit factors of 144?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere's a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\\\"The sum of all the distinct positive two-digit factors of 144 is:\\\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 11.140539407730103,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Task failed example: {\n", - " \"problem\": \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Algebra\",\n", - " \"solution\": \"We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 24.91333508491516,\n", - " \"trial\": -1\n", - "}\n", - "\n", - "Evaluation dictionary: {\n", - " \"Problem Interpretation\": {\n", - " \"description\": \"Ability to correctly interpret the problem.\",\n", - " \"accepted_values\": [\"completely off\", \"slightly relevant\", \"relevant\", \"mostly accurate\", \"completely accurate\"]\n", - " },\n", - " \"Mathematical Methodology\": {\n", - " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", - " \"accepted_values\": [\"inappropriate\", \"barely adequate\", \"adequate\", \"mostly effective\", \"completely effective\"]\n", - " },\n", - " \"Calculation Correctness\": {\n", - " \"description\": \"Accuracy of calculations made and solutions given\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"neither\", \"mostly correct\", \"completely correct\"]\n", - " },\n", - " \"Explanation Clarity\": {\n", - " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", - " \"accepted_values\": [\"not at all clear\", \"slightly clear\", \"moderately clear\", \"very clear\", \"completely clear\"]\n", - " },\n", - " \"Code Efficiency\": {\n", - " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", - " \"accepted_values\": [\"not at all efficient\", \"slightly efficient\", \"moderately efficient\", \"very efficient\", \"extremely efficient\"]\n", - " },\n", - " \"Code Correctness\": {\n", - " \"description\": \"Correctness of the provided code\",\n", - " \"accepted_values\": [\"completely incorrect\", \"mostly incorrect\", \"partly correct\", \"mostly correct\", \"completely correct\"]\n", - " }\n", + " Task description: Given any question, the system needs to solve the problem as consisely and accurately as possible\n", + " Task successful example: {'problem': 'What is the sum of all the distinct positive two-digit factors of 144?', 'level': 'Level 5', 'type': 'Number Theory', 'solution': 'Prime factorize $144=2^4\\\\cdot3^2$. The sum of the positive two-digit factors of 144 is $2^4+2\\\\cdot3^2+2^2\\\\cdot3+2^2\\\\cdot3^2+2^3\\\\cdot3+2^3\\\\cdot3^2+2^4\\\\cdot3=\\\\boxed{226}.$', 'problem_id': '0', 'response_with_ans': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'round': 0, 'messages': [{'content': 'What is the sum of all the distinct positive two-digit factors of 144?', 'role': 'user'}, {'content': 'To find the sum of all the distinct positive two-digit factors of 144, we need to first find all these factors. We can do this by iterating through the numbers from 10 to 99 and checking if they are factors of 144. Then, we can sum these factors and print their sum.\\n\\nHere\\'s a Python script to accomplish this:\\n\\n```python\\ntwo_digit_factors = []\\n\\nfor i in range(10, 100):\\n if 144 % i == 0:\\n two_digit_factors.append(i)\\n\\nsum_of_factors = sum(two_digit_factors)\\nprint(\"The sum of all the distinct positive two-digit factors of 144 is:\", sum_of_factors)\\n```\\n\\nPlease run this script to find the desired sum.', 'role': 'assistant'}], 'time': 11.140539407730103, 'trial': -1}\n", + " Task failed example: {'problem': 'Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.', 'level': 'Level 5', 'type': 'Algebra', 'solution': 'We have \\\\begin{align*} (2x+10)(x+3)&<(3x+9)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)&<3(x+3)(x+8) \\\\quad \\\\Rightarrow\\n\\\\\\\\ 2(x+5)(x+3)-3(x+3)(x+8)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (2x+10-(3x+24))(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (-x-14)(x+3)&<0 \\\\quad \\\\Rightarrow\\n\\\\\\\\ (x+14)(x+3)&>0.\\n\\\\end{align*} This inequality is satisfied if and only if $(x+14)$ and $(x+3)$ are either both positive or both negative. Both factors are positive for $x>-3$ and both factors are negative for $x<-14$. When $-14 0$ and then find intervals where the inequality holds true.\\n\\nLet's first find the factorized form of the quadratic:\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"I apologize for the confusion in my previous message. Let's find the factorized form of the quadratic using Python code.\\n\\n```python\\nfrom sympy import symbols, factor\\r\\nx = symbols('x')\\r\\nfactorized = factor(x**2 + 17*x + 42)\\r\\nfactorized\\r\\n```\\n\\nPlease run the Python code above to find the factorized form of the quadratic, and we can proceed to determine the intervals where the inequality holds true.\", 'role': 'assistant'}], 'time': 24.91333508491516, 'trial': -1}\n", + " Evaluation dictionary: [\n", + " {\n", + " \"name\": \"Problem Interpretation\",\n", + " \"description\": \"Ability to correctly interpret the problem.\",\n", + " \"accepted_values\": [\n", + " \"completely off\",\n", + " \"slightly relevant\",\n", + " \"relevant\",\n", + " \"mostly accurate\",\n", + " \"completely accurate\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Mathematical Methodology\",\n", + " \"description\": \"Adequacy of the chosen mathematical or algorithmic methodology for the question\",\n", + " \"accepted_values\": [\n", + " \"inappropriate\",\n", + " \"barely adequate\",\n", + " \"adequate\",\n", + " \"mostly effective\",\n", + " \"completely effective\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Calculation Correctness\",\n", + " \"description\": \"Accuracy of calculations made and solutions given\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"neither\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Explanation Clarity\",\n", + " \"description\": \"Clarity and comprehensibility of explanations, including language use and structure\",\n", + " \"accepted_values\": [\n", + " \"not at all clear\",\n", + " \"slightly clear\",\n", + " \"moderately clear\",\n", + " \"very clear\",\n", + " \"completely clear\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Efficiency\",\n", + " \"description\": \"Quality of code in terms of efficiency and elegance\",\n", + " \"accepted_values\": [\n", + " \"not at all efficient\",\n", + " \"slightly efficient\",\n", + " \"moderately efficient\",\n", + " \"very efficient\",\n", + " \"extremely efficient\"\n", + " ],\n", + " \"sub_criteria\": []\n", + " },\n", + " {\n", + " \"name\": \"Code Correctness\",\n", + " \"description\": \"Correctness of the provided code\",\n", + " \"accepted_values\": [\n", + " \"completely incorrect\",\n", + " \"mostly incorrect\",\n", + " \"partly correct\",\n", + " \"mostly correct\",\n", + " \"completely correct\"\n", + " ],\n", + " \"sub_criteria\": []\n", " }\n", - "actual test case to evaluate: {\n", - " \"problem\": \"How many integers $n$ satisfy both of the inequalities $4n + 3 < 25$ and $-7n + 5 < 24$?\",\n", - " \"level\": \"Level 5\",\n", - " \"type\": \"Prealgebra\",\n", - " \"solution\": \"Subtract 3 and divide by 4 on both sides of the first inequality to obtain \\\\begin{align*}\\n4n + 3 &< 25 \\\\\\\\\\n\\\\Rightarrow\\\\qquad 4n &< 22 \\\\\\\\\\n\\\\Rightarrow\\\\qquad n &< 5.5.\\n\\\\end{align*}Similarly, the second inequality yields \\\\begin{align*}\\n-7n + 5 &< 24 \\\\\\\\\\n\\\\Rightarrow\\\\qquad -7n &< 19 \\\\\\\\\\n\\\\Rightarrow\\\\qquad n &> -\\\\frac{19}{7}.\\n\\\\end{align*}Therefore, we are looking for all the integers between $-\\\\frac{19}{7}$ and $5.5$. Since $-\\\\frac{19}{7}$ is between $-3$ and $-2$ and the largest integer less than $5.5$ is 5, we need to count the number of integers between $-2$ and $5$, inclusive. There are $5$ positive integers, $2$ negative integers, and zero, so there are $\\\\boxed{8}$ integers that satisfy both $4n + 3 < 25$ and $-7n + 5 < 24$.\",\n", - " \"problem_id\": \"10\",\n", - " \"response_with_ans\": \"Here's the code to solve the inequalities and find the intersection of their solutions:\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\nn = symbols(\\\"n\\\")\\ninequality_1 = 4 * n + 3 < 25\\ninequality_2 = -7 * n + 5 < 24\\n\\nsolution_1 = solve(inequality_1, n)\\nsolution_2 = solve(inequality_2, n)\\n\\nintersection = (max(solution_1[0], solution_2[0]), min(solution_1[1], solution_2[1]))\\n\\nprint(f\\\"Solution to inequality 1: {solution_1}\\\")\\nprint(f\\\"Solution to inequality 2: {solution_2}\\\")\\nprint(f\\\"Intersection of solutions: {intersection}\\\")\\n```\\n\\nExecute this code, and let's see the solutions for both inequalities and their intersection.\",\n", - " \"round\": 1,\n", - " \"messages\": [\n", - " {\n", - " \"content\": \"How many integers $n$ satisfy both of the inequalities $4n + 3 < 25$ and $-7n + 5 < 24$?\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"To find the number of integers $n$ that satisfy both inequalities, we'll first solve each inequality individually, and then find the intersection of the solutions.\\n\\nStep 1: Solve the inequalities\\n1. $4n + 3 < 25$\\n2. $-7n + 5 < 24$\\n\\nStep 2: Find the intersection of the solutions\\n\\nStep 3: Count the number of integers in the intersection\\n\\nFirst, let's solve the inequalities using the python code.\",\n", - " \"role\": \"assistant\"\n", - " },\n", - " {\n", - " \"content\": \"\",\n", - " \"role\": \"user\"\n", - " },\n", - " {\n", - " \"content\": \"Here's the code to solve the inequalities and find the intersection of their solutions:\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\nn = symbols(\\\"n\\\")\\ninequality_1 = 4 * n + 3 < 25\\ninequality_2 = -7 * n + 5 < 24\\n\\nsolution_1 = solve(inequality_1, n)\\nsolution_2 = solve(inequality_2, n)\\n\\nintersection = (max(solution_1[0], solution_2[0]), min(solution_1[1], solution_2[1]))\\n\\nprint(f\\\"Solution to inequality 1: {solution_1}\\\")\\nprint(f\\\"Solution to inequality 2: {solution_2}\\\")\\nprint(f\\\"Intersection of solutions: {intersection}\\\")\\n```\\n\\nExecute this code, and let's see the solutions for both inequalities and their intersection.\",\n", - " \"role\": \"assistant\"\n", - " }\n", - " ],\n", - " \"time\": 19.949471950531006,\n", - " \"trial\": -1\n", - "}\n", + "]actual test case to evaluate: {'problem': 'Simplify $\\\\frac{2m+8}{3}-\\\\frac{2-m}{3}$.', 'level': 'Level 5', 'type': 'Prealgebra', 'solution': 'Both fractions have the same denominator, so we can subtract them: \\\\[\\\\frac{2m+8}{3}-\\\\frac{2-m}{3}=\\\\frac{(2m+8)-(2-m)}{3}\\\\] Distributing the negative sign across the parentheses, we get \\\\[\\\\frac{2m+8-2-(-m)}{3}=\\\\frac{2m+8-2+m}{3}=\\\\frac{3m+6}{3}\\\\] Notice that every number in the numerator has a common factor of 3. We can use the distributive law in reverse to get \\\\[\\\\frac{3m+6}{3}=\\\\frac{3(m+2)}{3}=\\\\frac{\\\\cancel{3}(m+2)}{\\\\cancel{3}}=\\\\boxed{m+2}.\\\\]', 'problem_id': '19', 'response_with_ans': '', 'round': 15, 'messages': [{'content': 'Simplify $\\\\frac{2m+8}{3}-\\\\frac{2-m}{3}$.', 'role': 'user'}, {'content': 'To simplify the expression, we can combine the fractions as they have the same denominator:\\n\\n$\\\\frac{2m + 8}{3} - \\\\frac{2 - m}{3} = \\\\frac{(2m + 8) - (2 - m)}{3}$\\n\\nNow, I will calculate the numerator and finish simplifying the expression.', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '\\n', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': \"My apologies for the confusion in the previous responses. Let me simplify the expression step by step now. \\n\\nWe have the expression:\\n\\n$\\\\frac{2m + 8}{3} - \\\\frac{2 - m}{3}$\\n\\nWe can combine the fractions as they have the same denominator:\\n\\n$\\\\frac{(2m + 8) - (2 - m)}{3}$\\n\\nLet's further simplify the expression using Python code.\", 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': '', 'role': 'user'}, {'content': '', 'role': 'assistant'}], 'time': 37.317880630493164, 'trial': -1}\n", "\n", "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[33mquantifier\u001b[0m (to quantifier_user):\n", "\n", + "```json\n", "{\n", " \"Problem Interpretation\": \"completely accurate\",\n", " \"Mathematical Methodology\": \"completely effective\",\n", " \"Calculation Correctness\": \"completely correct\",\n", " \"Explanation Clarity\": \"very clear\",\n", - " \"Code Efficiency\": \"moderately efficient\",\n", - " \"Code Correctness\": \"mostly correct\"\n", + " \"Code Efficiency\": \"not applicable\",\n", + " \"Code Correctness\": \"not applicable\"\n", "}\n", + "```\n", "\n", "--------------------------------------------------------------------------------\n" ] } ], "source": [ - "# log_path = \"../test/test_files/agenteval-in-out/agentchat_results/\"\n", "criteria_file = \"../test/test_files/agenteval-in-out/samples/sample_math_criteria.json\"\n", + "criteria = Criterion.parse_json_str(open(criteria_file, \"r\").read())\n", "outcome = {}\n", "\n", "for prefix in os.listdir(log_path):\n", " for file_name in os.listdir(log_path + \"/\" + prefix):\n", " gameid = prefix + \"_\" + file_name\n", " if file_name.split(\".\")[-1] == \"json\":\n", - " outcome[gameid] = get_quantifier(log_path + \"/\" + prefix + \"/\" + file_name, criteria_file)\n", + " test_case, ground_truth = remove_ground_truth(open(log_path + \"/\" + prefix + \"/\" + file_name, \"r\").read())\n", + " quantifier_output = quantify_criteria(\n", + " llm_config={\"config_list\": config_list},\n", + " criteria=criteria,\n", + " task=task,\n", + " test_case=test_case,\n", + " ground_truth=ground_truth,\n", + " )\n", + " outcome[gameid] = quantifier_output\n", "\n", "# store the evaluated problems\n", "with open(\"../test/test_files/agenteval-in-out/evaluated_problems.json\", \"w\") as file:\n", @@ -3464,7 +2635,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 18, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -3484,26 +2655,35 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/vscode/.local/lib/python3.10/site-packages/scipy/stats/_distn_infrastructure.py:2241: RuntimeWarning: invalid value encountered in multiply\n", + "/home/vscode/.local/lib/python3.10/site-packages/numpy/core/fromnumeric.py:3504: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/vscode/.local/lib/python3.10/site-packages/numpy/core/_methods.py:129: RuntimeWarning: invalid value encountered in scalar divide\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/vscode/.local/lib/python3.10/site-packages/scipy/stats/_distn_infrastructure.py:2244: RuntimeWarning: invalid value encountered in multiply\n", " lower_bound = _a * scale + loc\n", - "/home/vscode/.local/lib/python3.10/site-packages/scipy/stats/_distn_infrastructure.py:2242: RuntimeWarning: invalid value encountered in multiply\n", - " upper_bound = _b * scale + loc\n" + "/home/vscode/.local/lib/python3.10/site-packages/scipy/stats/_distn_infrastructure.py:2245: RuntimeWarning: invalid value encountered in multiply\n", + " upper_bound = _b * scale + loc\n", + "/home/vscode/.local/lib/python3.10/site-packages/numpy/core/_methods.py:206: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/vscode/.local/lib/python3.10/site-packages/numpy/core/_methods.py:163: RuntimeWarning: invalid value encountered in divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean,\n", + "/home/vscode/.local/lib/python3.10/site-packages/numpy/core/_methods.py:198: RuntimeWarning: invalid value encountered in scalar divide\n", + " ret = ret.dtype.type(ret / rcount)\n" ] } ], "source": [ "# computing average and 95% interval for failed and successful cases on all criteria\n", "try:\n", - " # convert the criteria to dict type if it is already not\n", - " dictionary_for_eval = eval(open(criteria_file, \"r\").read())\n", + " criteria = Criterion.parse_json_str(open(criteria_file, \"r\").read())\n", "except: # noqa: E722\n", " pass\n", "\n", - "criteria = list(dictionary_for_eval.keys())\n", + "\n", "nl2int = {}\n", - "for criterion in dictionary_for_eval:\n", + "for criterion in criteria:\n", " score = 0\n", - " for v in dictionary_for_eval[criterion][\"accepted_values\"]:\n", + " for v in criterion.accepted_values:\n", " nl2int[v] = score\n", " score += 1\n", "print(nl2int)\n", @@ -3521,17 +2701,17 @@ " try:\n", " tmp_dic = eval(outcome[game][\"estimated_performance\"])\n", " if outcome[game][\"actual_success\"] == \"false\":\n", - " task[\"f\"].append(nl2int[tmp_dic[criterion]])\n", + " task[\"f\"].append(nl2int[tmp_dic[criterion.name]])\n", " else:\n", - " task[\"s\"].append(nl2int[tmp_dic[criterion]])\n", + " task[\"s\"].append(nl2int[tmp_dic[criterion.name]])\n", " except: # noqa: E722\n", " pass\n", "\n", - " average_f[criterion] = np.mean(task[\"f\"])\n", - " average_s[criterion] = np.mean(task[\"s\"])\n", + " average_f[criterion.name] = np.mean(task[\"f\"])\n", + " average_s[criterion.name] = np.mean(task[\"s\"])\n", "\n", - " conf_interval_s[criterion] = stats.norm.interval(0.95, loc=np.mean(task[\"s\"]), scale=stats.sem(task[\"s\"]))\n", - " conf_interval_f[criterion] = stats.norm.interval(0.95, loc=np.mean(task[\"f\"]), scale=stats.sem(task[\"f\"]))" + " conf_interval_s[criterion.name] = stats.norm.interval(0.95, loc=np.mean(task[\"s\"]), scale=stats.sem(task[\"s\"]))\n", + " conf_interval_f[criterion.name] = stats.norm.interval(0.95, loc=np.mean(task[\"f\"]), scale=stats.sem(task[\"f\"]))" ] }, { @@ -3543,7 +2723,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 19, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -3553,9 +2733,17 @@ "outputId": "248cd0bc-0927-4d9f-b911-088bd76acf5d" }, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_394256/2108490914.py:34: UserWarning: Tight layout not applied. The left and right margins cannot be made large enough to accommodate all axes decorations.\n", + " plt.tight_layout() # Adjust subplot parameters to fit the labels\n" + ] + }, { "data": { - "image/png": "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ybEPp/j1euDAAX333Xf2OpOq/8KFC+rdu7fy588vHx8fFS1aVO+9957DmVO395M0btw4FSlSRD4+Pvrzzz8lufcaxdfxyy+/qE+fPgoJCVFAQIAef/xxnT592mE97d69Wz///LO9Vnf7Oxo7dqzCwsLk5+enunXrateuXQ7P//777+rcubMKFy4sX19f5c6dW127dtXZs2cd2l2+fFm9e/dWwYIF5ePjo5w5c+rhhx/W9u3bHdpt3rxZTZo0UVBQkPz9/VW3bl398ssvSdZ5Z58z8a/d119/rWHDhilfvnzy9fXVQw89pP379zuN78583V0GV44dO6Zu3brZ9+1ChQrpxRdf1I0bNyRJ586dU9++fVW2bFllzpxZgYGBeuSRR7Rz506naU2YMEGlS5eWv7+/smXLpkqVKjkdX9w9XrkzrdsZYxQcHOxwCXBcXJyyZs0qLy8vh33xvffeU8aMGe2Xvd7Zh5PNZlNUVJRmzZpl3y7vPAPvwoUL6ty5s7JmzaqgoCB16dJFV69eTXhFS9q1a5cuXLigdu3aOczvscceU+bMmTV37lyX4127dk3Xr19P8DlfX1/7DxrZs2eXJHstixYt0o4dOxQREZFobXdOc8SIEQoPD9eoUaNcHgOfeeYZValSxf44qWOr5P62X7BgQfsPQyEhIQ7ve676cHL3GCq5tz/Fbw/79+936zX+/PPPVaVKFfu2WqdOHaczsX744QfVrl1bAQEBypIli5o2bardu3e7rDEp7taX1Hbszr4Y/5rNnTtXb731lvLmzSt/f3/7ZfmzZs1yqm/58uWy2WxaunSpJOnQoUN66aWXVKJECfn5+SlHjhx64oknHPq6S8i+ffvUunVr5c6dW76+vsqXL5/at2+vixcverTuXOncubMyZ86sw4cP2/fFvHnzauLEiZKkP/74Qw0aNFBAQIDCwsKcjkPuHCPXrFmjypUrS5K6dOni8J59uz///FP169eXv7+/8ubNq/fff9+tZbj9c2eJEiXk6+urihUrau3atQ7t4redP//8U0899ZSyZcumWrVqSbr1w9K7775rf88vWLCgBg4cmOC+tGLFCpUvX16+vr4qVaqUFixY4FatydkH//77b3Xo0EFBQUEKCQnR22+/LWOMjhw5ohYtWigwMFC5c+fW6NGjneaT3PcQIF0zAFJNeHi46datmzHGmLVr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M2fO6OzZs2rcuLH27dunY8eOSbrVp8fu3bu1b9++ZM3j+++/V5UqVeynj0tS5syZ9dxzz+ngwYP2S8Y8UapUKa1cuVKLFi1S//79FRAQ4NZd6r7//nuFhoaqTZs29mH+/v567rnnPK4lId98841q166tbNmyOWwrDRs2VGxsrNOp961bt1ZISIj9cXJeo3jPPfecw+U4tWvXVmxsrA4dOnRXy9KyZUvlzZvX/rhKlSqqWrWqvv/+e/uw27fZ69ev68yZM/b+tG7fL7JmzarNmzfr+PHjLuf122+/ad++fXrqqad09uxZ+3JHRUXpoYce0tq1a506c3dHly5dHPqjqV27tqRblyYld75JLYMrcXFxWrRokZo1a6ZKlSo5PR//uvn4+Nj7aomNjdXZs2eVOXNmlShRwmk9Hj16VFu3bnU5v+Qcr5KaVkLit68NGzZIktatW6fatWurdu3aWrdunaT/XdYWv7499cILLzjN++zZs7p06VKC4wQHB6tt27aaNWuWRo8erX///Vfr1q2zH4elW5ezxfvll1/Uq1cvNW/eXC+88IJ+/fVXlSlTRgMHDnRo17dvXx07dkwbN27UsWPH9OSTT+r48eMaMWKExo0bp5s3b+qVV15RgQIFVKVKlSQvBY1fhixZsri1LpJ7bE1q208Od4+hnuzHSb3GixYtUlxcnN555x2n/ozi95+VK1fqwoULevLJJx22eS8vL1WtWtXps0NyeLINSp59dujUqZPDMVW69fkhJibG4VKqFStW2C8bjXf7eDExMTp79qyKFi2qrFmzJvoZJSgoSNKtS/SSulzVCs8++6z9/6xZs6pEiRIKCAhQ27Zt7cNLlCihrFmzOmyr7h4jk5I5c2aH/toyZcqkKlWquL1fVK9eXRUrVrQ/LlCggFq0aKHly5c7dX9w57YT/955512JX3vtNUlyujw2T548evzxx+2PAwMD1bFjR+3YsUMnTpxwWZ8n++Dtr4mXl5cqVaokY4y6detmHx7/Wt2+njx9DwHSKwInIBXExsZq7ty5ql+/vg4cOKD9+/dr//79qlq1qk6ePKnVq1dLutVZbOvWrbV48WL7deoLFixQTEyMwwemffv2affu3QoJCXH4K168uKT/dXAar1ChQi7rWrp0qapVqyZfX19lz55dISEhmjx5skN/BIcOHVKGDBmcpnHn3fVOnz6tCxcu6JNPPnGqK75fqjvrulOOHDnUuHFjLVy40N5HyBdffKGMGTM6fMjavXu3Hn/8cQUFBSkwMFAhISH2D0ZW9KWwf/9+GWP09ttvOy1LfF8e8csyZMgQXbhwQcWLF1fZsmXVr18//f7770nO49ChQ/ZOdG9XsmRJ+/OeCgwMVMOGDdWiRQu99957eu2119SiRQuX/dzcWVPRokWd+khxVefd2rdvn5YtW+a0fhs2bCgp6W04Oa9RvAIFCjg8zpYtm6RbYevdKFasmNOw4sWLO/QJcu7cOfXq1Uu5cuWSn5+fQkJC7Mt0+zb7/vvva9euXcqfP7+qVKmiwYMHO3xwjQ82O3Xq5LTcU6dOVXR0tEf7QFLrJjnzTWoZXDl9+rQuXbqkMmXKJNouLi5OY8eOVbFixeTj46Pg4GCFhITo999/d1ju119/XZkzZ1aVKlVUrFgx9ejRwyHYSM7xKqlpJaRChQry9/e3h0vxgVOdOnW0bds2Xb9+3f7c7eGIJzzdtqdMmaJHH31Uffv2VZEiRVSnTh2VLVtWzZo1k3TrS2dCMmXKpJdfflkXLlxwuuNcrly5VK1aNXsdr7/+uh566CE99NBDevfdd7V69Wp99dVXatmypZo2bZpo/3KBgYGSbv244I7kHlutPC64ewz1ZD9Oqs5//vlHGTJkSLQz9vj5NmjQwGm+K1asSPI9OjGerkdPPju4+kxTrlw5hYeHO/xg99VXXyk4ONj+Y5x0K0R955137H0Hxh9DLly4kOixs1ChQurTp4+mTp2q4OBgNW7cWBMnTkzyeHvlyhWdOHHC/nd7v4EJ8fX1dfiBRboVeOXLl89p2woKCnJYx+4eI5Pial7ZsmVze79I6H3x6tWrTuvgztcz/nPnnZ8zc+fOraxZszrtw672ufjPwwn1zWXFPhgUFCRfX18FBwc7Db99PXn6HgKkV9ylDkgFP/74oyIjIzV37lyXHbHOmTNHjRo1kiS1b99eU6ZM0Q8//KCWLVvq66+/Vnh4uMqVK2dvHxcXp7Jly2rMmDEu55c/f36Hx3f+Eijd+vLTvHlz1alTR5MmTVJoaKi8vb01Y8YMjzoyjP8lqEOHDurUqZPLNg888ECS0+nQoYOWLl2qpUuXqnnz5po/f74aNWpk//B14cIF1a1bV4GBgRoyZIiKFCkiX19fbd++Xa+//nqiZ3ck1OH2nb+2xU+jb9++aty4sctx4j8I1alTR//8848WL16sFStWaOrUqRo7dqw+/vhjh1/DUlOrVq30zDPPaO7cuQ7bUWqKi4vTww8/rP79+7t8Pv7DYrw7t+HkvEbx4m/LfidjjFs13422bdtqw4YN6tevn8qXL6/MmTMrLi5OTZo0cdhm27Ztq9q1a2vhwoVasWKFPvjgA7333ntasGCBHnnkEXvbDz74IMFbWScWEiQkqXWTnPkmtQx3Y/jw4Xr77bfVtWtXvfvuu8qePbsyZMig3r17O6zHkiVLau/evVq6dKmWLVum+fPna9KkSXrnnXcUERGRrONVUtNKiLe3t6pWraq1a9dq//79OnHihGrXrq1cuXIpJiZGmzdv1rp16xQeHu705TK5PN22g4KCtHjxYh0+fFgHDx5UWFiYwsLCVKNGDYWEhCR5d8n495pz584l2GbTpk2aN2+evSP9L7/8Um+//baqV6+u6tWra8qUKVq6dGmCd78LDw+XdKuz5JYtWyZajydS47jgyX5sRZ3x8509e7Zy587t9Pzd3OnP0/o8+ezg6jONdOssp2HDhunMmTPKkiWLlixZoieffNJhuV555RXNmDFDvXv3VvXq1RUUFCSbzab27dsneXbo6NGj1blzZ/v7fc+ePTVixAht2rRJ+fLlcznOqFGjHI4TYWFhSXZQntC6dGcdu3uMTMq93C8Sej09uUmKu6zaB91ZT56+hwDpFYETkArmzJmjnDlz2u8ycrsFCxZo4cKF+vjjj+Xn56c6deooNDRUX331lWrVqqUff/xRb775psM4RYoU0c6dO/XQQw95/IY8f/58+fr6avny5fLx8bEPnzFjhkO7sLAwxcXF6cCBAw6/WN15B6uQkBBlyZJFsbGx9rNUPNG8eXNlyZJFX3zxhby9vXX+/HmHy+nWrFmjs2fPasGCBapTp459+IEDB5Kcdvwvrnf+mn7nr2WFCxeWdOsLozvLEn93wS5duujKlSuqU6eOBg8enGjgFBYWpr179zoNj78kMCwsLMn5uis6OlpxcXFJ/roZFhamXbt2yRjjsF25qvNuFSlSRFeuXPF4W0nua+QuT/YnV5dT/v333/a7+J0/f16rV69WRESE3nnnnUTHk6TQ0FC99NJLeumll3Tq1ClVqFBBw4YN0yOPPKIiRYpI+t9ZbPdKcueb2DK4EhISosDAQKe7+91p3rx5ql+/vqZNm+Yw/MKFC06/MgcEBKhdu3Zq166dbty4oVatWmnYsGEaMGBAso9XiU0r/o5srtSuXVvvvfeeVq1apeDgYIWHh8tms6l06dJat26d1q1bp8ceeyzJ+afkFy/p1i/38b/ex5+x1Lp16yTHiz9zLaHAzBijnj17qlevXvZt6Pjx48qTJ4+9TZ48eZwuf71drVq1lC1bNn355ZcaOHBggl/w4t3LY6urebtzDE2J/bhIkSKKi4vTn3/+meAX6Pj55syZ854eP+K52o6t+uwg3QqcIiIiNH/+fOXKlUuXLl1S+/btHdrMmzdPnTp1criT2PXr1926i6sklS1bVmXLltVbb72lDRs2qGbNmvr44481dOhQl+07duzocAZjQuGKVdw9Rqb0MSWh90V/f/8kA/b4z5379u2zn5koSSdPntSFCxec9uH4M55vX6a///5b0v/u0nune/1e6ul7CJAecUkdcI9du3ZNCxYs0GOPPaY2bdo4/b388su6fPmy/VbuGTJkUJs2bfTtt99q9uzZunnzpsPldNKtMwiOHTumTz/91OX8oqKikqzLy8tLNpvN4eyegwcPatGiRQ7t4s8emTRpksPwCRMmOE2vdevWmj9/vssvje6cRi7d+jD2+OOP6/vvv9fkyZMVEBCgFi1aOMxHcvz16MaNG071uRIWFiYvLy+n/oHuHDdnzpyqV6+epkyZosjIyESX5c7b2mfOnFlFixZN8Na98R599FFt2bJFGzdutA+LiorSJ598ooIFCyZ6WURCLly4oJiYGKfhU6dOlSSXfePcWdPx48c1b948+7CrV6/qk08+SXYtSWnbtq02btyo5cuXOz134cIF3bx5M9Hxk/MaJUdAQIDbXzziLVq0yOEL85YtW7R582Z7uOJqm5WkcePGOTyOjY11CgVz5sypPHny2LenihUrqkiRIho1apTLfrk8Xe6kuDtfd5bBlQwZMqhly5b69ttvtW3bNqfn49edl5eX03r85ptvnAKLO/fLTJkyqVSpUjLGKCYmJlnHq6SmlZjatWsrOjpa48aNU61atexfiGrXrq3Zs2fr+PHjbvXf5Ml26akBAwbo5s2bevXVV+3DXG1Xly9f1rhx4xQcHOzQV8vtZs6cqSNHjjj8aJIrVy57+BMTE6P9+/e7PNsmnr+/v15//XX99ddfev31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JF27dk1vvPGG8ufPb2/76aefJnu5d+3apW3btql169aJ9i01bNgw7d69W9OnT/fo+OHufnH8+HENGjRI1apVU86cOeXj46OCBQvqpZde0qlTp5ym27lzZ9lsNv37778aNWqUihcvLj8/P5UqVcoeyty4cUNvvvmmChYsKF9fXz3wwAP64YcfnKYV37/W9evX9cYbb6hAgQLy9fVVyZIlNWHCBBlj3F7eU6dO6dVXX1XRokXl4+Oj4OBgtW7dWrt27XJqu2/fPnXp0kWFChWSj4+PsmfPrnLlyql3795O82zatKn8/f01c+ZMt2sBgLSES+oAAHdl8uTJeumllxQaGqrHH39cOXLk0IkTJ7RlyxYtXLhQrVu39njae/fuVf369RUZGalatWqpZcuWioqK0u7duzV8+HD17fu/SwLnz5+vp556SsYYNWvWTCVKlNCpU6e0efNmTZs2Tc2aNbO3HTBggEaOHKm8efOqVatWCgoK0rp169SvXz9t3rxZ33zzjUPb999/X0WKFNFTTz2lLFmy6NixY1q/fr1WrVpl73TaivWwevVqSVK1atU8Xmcpwd11cO7cOfXu3Vu1a9fWo48+qmzZsunff//VkiVL9MMPP2jt2rWqXLmy0/QXLlyoFStWqGXLlqpZs6a+++47DR06VMYYBQUFaejQoWrRooXq1aun+fPnq3///sqVK5c6duzoNK0xY8Zo9erVateunZo2bapVq1Zp3Lhx2rRpk9auXStvb+9ElzU6OlpNmjTRmjVrVL58eXXr1k0xMTH67rvv1KJFC02YMEEvv/yyJMkYo8aNG2vz5s2qWbOmmjRpogwZMujQoUNasmSJnnnmGYWFhTlMv3r16lq1apU2bNigRo0aefiK4G7Fb7Px/eP07t1bkpQ1a1ZJnm/L7krOMSguLk7NmzfXqlWrVLZsWT311FM6e/asXn311UTPUnLFnWPM9u3bNWzYMA0ZMkSlSpVK9rIlZ79Yu3atRo8erYceekhVq1aVt7e3duzYocmTJ2v58uXavn27goKCnObRp08fbd68Wc2aNZOXl5fmzp2rp556StmyZdOECRP0559/qmnTprp+/bq++OILtWjRQn/99ZfLM0fbtm2rHTt22I/R8+fPV8+ePXXw4EGNHj06yeX9559/VK9ePR09elSNGjVSy5YtderUKc2fP1/Lly/X6tWrVbVqVUm3ArYqVaooKipKTZs2Vbt27RQVFaV9+/Zp0qRJGjVqlDJm/N/Xs0yZMqlixYrauHGjoqKiuKwOQLpD4AQAuCtTp05VpkyZ9NtvvylnzpwOz509e/aupt2hQwdFRkbqk08+Uffu3R2eO3r0qP3/kydPqlOnTvL29ta6dev04IMPJth25cqVGjlypBo3bqz58+fbP8AbY/TSSy/p448/1vz58+1fPqZOnao8efLo999/l7+/v8N0z507Z//fivXwyy+/KEOGDCpfvrxb7e8Vd9dBtmzZdPjwYeXNm9ehze7du1WtWjUNHDhQK1eudJr+Dz/8oF9++cX+BT4iIkJFixbV2LFjFRgYqB07dqhw4cKSpL59+6po0aIaNWqUy8Bp+fLl2rp1qx544AFJt17XDh066IsvvtCHH36o1157LdFlHTJkiNasWaO3335bERER9rNALl++rAYNGui1115Tq1atlCdPHu3atUubN29Wy5YttXDhQofpREdHKyYmxmn6lSpVknTrtSZwSj316tVTvXr17GeO3HlmkqfbsjuSewz67LPPtGrVKjVp0kRLly61n3HUq1cv+/bkrl9++UWSVLFiRZfPR0dHq2PHjipfvrz69+/v0fIlZ79o0KCBTpw4ocyZMzu0++yzz9SpUyd99NFHevPNN53m8ddff+n3339XSEiIJKlLly6qWrWq2rdvrzJlyuiPP/6wr9fGjRurXbt2Gj9+vD788EOnaf3999/atWuXPdiKiIhQ1apVNXbsWD355JNJruOOHTsqMjJSy5YtU+PGje3D33rrLVWqVEndu3fX77//LulWmHXhwgWNGzdOvXr1cpjOuXPnHMKmeJUqVdK6deu0ZcuWZAeMAJDauKQOAHDXvL29XZ45kiNHDo+nuWXLFm3btk116tRxCpskKV++fPb/Z82apaioKL322mtOYdOdbT/66CNJ0ieffOLwa7HNZtPIkSNls9n05ZdfOoyfKVMml5eVZM+e3eHx3a6Ho0ePKmvWrPLx8XGr/b3kzjrw8fFx+oIuSaVLl1b9+vW1du1alyFMhw4dHM4WyZIlix577DFdvXpVL774oj1skqT8+fOrVq1a+vPPP3Xz5k2naXXs2NEeNkm3Xtfhw4fLy8sryctS4uLiNHnyZBUpUsQhbIqv6Z133tGNGze0YMECh/H8/PycpuXj4+P0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e998s2fP1pQpU1SnTh0VKVJEgYGB+vPPP/X9998re/bs6tKli8O0//nnHx04cEAvvvii9QsOAPcAgRMA4K6MGDFCy5Yt05YtW/Ttt98qICBARYoU0eTJk9WtWzeHtu+++64yZcqkadOm6eOPP1bBggX19ttvq1mzZpo/f77TtIsVK6bt27drxIgR+vbbbzVu3DhlzpxZxYoV01tvveXQ9vHHH9fmzZs1YsQI/fzzz1qyZImCg4NVvnx5de/e3aHtkCFDVKdOHX344YdavXq1Lly4oBw5cqhQoUIaPHiwnn76aUlSQECA3nvvPa1evVrr1q3TqVOnlC1bNpUoUUIjRoxQ+/btPVoPCSlYsKAaN26sefPmacKECfYgJ96yZcscfi2XZO8/KX782wOn/fv3O93W+59//rGHe2FhYUkGTslZB4899pjmzZun4cOH6/PPP5e/v78aNGighQsXasiQIW6tg7vVp08fNW/eXOPGjdP+/fuVPXt29erVy77tJcXHx0c//PCDpk2bps8++0zz589XdHS0cuXKpVKlSumFF16wX9pSqVIlvf7661qzZo2+++47XbhwQblz51bDhg3Vr18/VatWzWHaa9eu1eHDhzVu3LiUWHRYKKW3ZXePQdKtYGbx4sWKiIjQnDlzNH78eBUpUkRjx45VsWLFkhU4Zc6cWR06dNAnn3yiyMhIj+6yl5Tk7BcjRoxQ9uzZNXPmTE2aNEm5cuXSk08+edd3ykuOr7/+WoMGDdKXX36pkydPqlChQvrwww/d7pOpUKFC2rFjh8aMGaNFixZpxowZ8vLyUmhoqOrUqaM2bdrY2z755JO6fv26fvnlF23ZskXR0dHKly+fXnzxRfXr18+pL6nPP/9ckvT8889bt8AAcA/ZzJ3n//8HXLp0SUFBQbp48eJd35YbAJJy/fp1HThwQIUKFXLrsikgMatXr1bDhg31+eefO3zpROIGDx6siIgI/fTTT6pXr15ql+NShw4d9MMPP+iff/6xd3QP3Gt79+5VmTJlNHjwYL355pupXU6qqVevnn7++WenS6HTips3b6pYsWIqVKiQfvzxxyTb81kEwL3kbuZCH04AAKQhDz30kJo0aaKhQ4cqLi4utcuBRf7++2/NnTtXb731FmETUlWJEiX07LPPauzYsbp8+XJql4MEzJo1S4cOHUryLFQASMu4pA4A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AAAAAMA95lGn4QEBATp9+nSS7Q4cOKDs2bMne/o///yzevToocqVK+vmzZsaOHCgGjVqpD///FMBAQGelAwAANxw/tRJnT990mFY9PXr9v8P/LVLPr6+TuNlC8mlbDlzpXh9AAAASB88CpzKli2rX3/9VWfOnFFwcLDLNocOHdLOnTv18MMPJ3v6y5Ytc3g8c+ZM5cyZU7/++qvq1KnjSckAAMANK76ara8njknw+beebulyeNsefdTuFec+HQEAAHB/8ihw6tChg9auXatnn31WX3zxhfz9/R2ev3Hjhl566SXFxMSoQ4cOd13kxYsXJSnBs6Wio6MVHR1tf+zJZXwAAEBq1O4ZVW7QKNnjZQvh7CYAAAD8j0eBU5cuXTRnzhwtWbJE4eHhatKkiSRp586d6tmzp5YsWaLDhw+rYcOGateu3V0VGBcXp969e6tmzZoqU6aMyzYjRoxQRETEXc0HAABI2XJyaRwAAADunkedhnt5eenbb7/Vk08+qWPHjmnq1KmSpB07duijjz7S4cOH1bp1ay1YsOCuC+zRo4d27dqluXPnJthmwIABunjxov3vyJEjdz1fAAAAAAAAeMajM5wkKXPmzJozZ47efvttff/99/r3338VFxen/Pnz65FHHlH58uXvuriXX35ZS5cu1dq1a5UvX74E2/n4+MjHx+eu5wcAAAAAAIC753HgFC88PFzh4eFW1GJnjNErr7yihQsXas2aNSpUqJCl0wcAAAAAAEDK8eiSum+//VZxcXFW12LXo0cPff755/riiy+UJUsWnThxQidOnNC1a9dSbJ4AAAAAAACwhkeBU4sWLZQ/f369/vrr+uuvv6yuSZMnT9bFixdVr149hYaG2v+++uory+cFAAAAAAAAa3kUOFWoUEGRkZH64IMPVKZMGdWoUUOffvqpLl26ZElRxhiXf507d7Zk+gAAAAAAAEg5HgVO27Zt0++//67evXsrODhYmzZt0gsvvKDQ0FB17NhRP/74o9V1AgAAAAAAIJ3wKHCSpDJlymjMmDE6duyYFixYoMcee0wxMTH6/PPP9fDDD6tQoUIaMmSIDh06ZGW9AAAAAAAASOM8DpziZcyYUS1bttTixYt17NgxjRo1SqVKldKhQ4cUERGhokWLWlEnAAAAAAAA0om7DpxuFxISoj59+mjLli3q1auXjDEpejc7AAAA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nUgmcvPmTV599VXCw8Np2LAha9euJU+ePLZuloiIiIiIiMhfohFOIpmIh4cHixcvpk6dOjRv3lxhk4iIiIiIiGRJGuEkkgklJSXh7OwMYF2xTkRERERERCSrUOAkkokpbBIREREREZGsSFPqRDIxhU0iIiIiIiKSFSlwEhERERERERGRDKXASUREREREREREMpQCJxERERERERERyVAKnEREREREREREJEMpcBIRERERERERkQylwElERERERERERDKUAicREREREREREclQCpxEREQkW9q+fTu9evWiTJkyuLq64uzsjKenJ02aNGHGjBn88ssvf+l8ly5dwmQy4e3t/fc0+Cnt3r0bk8lEgwYNbN0UERERyUYUOImIiEi2cuPGDZo0aULTpk1ZsmQJKSkp+Pn5ERgYSPny5Tlw4ADDhg2jRIkSHDp0KEPe09vbG5PJxKVLlzLkfCIiIiKZnYOtGyAiIiLyrCQkJFCnTh3OnDlDuXLlmD9/PnXr1k23T1JSEkuXLuW9994jNjb2T5+7SJEiREdH4+jomNHN/p9Ur16d6OhocuXKZeumiIiISDZiMgzDsHUjRERERJ6FHj16EBYWhre3N0ePHsXDw+M397127Rq3bt2ibNmy//P7ent7c/nyZS5evJjpptyJiIiI/B00pU5ERESyhQsXLrB8+XIAQkNDfzdsAihUqJA1bBo/fjwmk4nx48fz73//m9dee42iRYvi6OjIq6++Cjy5htOSJUswmUxcvnwZAB8fH0wmk/W/3bt3p3vPmJgYhg0bRvny5cmVKxd58uThpZdeYs6cOaSmpj7WxldffRWTycSSJUuIioqic+fOeHp6Ym9vz/jx44Hfr+G0Y8cOBg0aRJUqVcifPz/Ozs54eXnRuXNnIiIi/sTfqoiIiMiTaUqdiIiIZAvh4eGkpaXh5uZG69atn+oc586do2rVqjg5OVG7dm0MwyB//vy/uX+pUqXo2bMnX331FXfv3iUwMBAXFxfr9sKFC1t/3rt3L23btiU+Ph5vb2+aNGlCUlIShw8fZtCgQWzYsIHw8PAnTtk7cOAAAwYMwNPTk3r16pGYmEiePHn+sD8DBgzgypUrVKxYkdq1a+Pg4MDp06dZtWoVa9as4csvvyQwMPAv/i2JiIiIKHASERGRbOLIkSMAvPDCC9jb2z/VOZYvX063bt1YuHAhzs7Of7h/nTp1qFOnDrt37+bu3btMmzbtiVPqrl69Svv27bl16xbz5s2jf//+2Nk9GIgeFxdHp06d2LZtG5MnT2bcuHGPHb9gwQJGjRrFpEmTrMf9GdOmTaN+/fq4u7une33dunV07NiR/v374+/vT86cOf/0OUVERERAU+pEREQkm/jll18AKFiw4FOfw8PDgzlz5vypsOmvmDlzJnFxcbz55pu8/vrr6UKjfPnysWzZMhwdHZkzZw5PKr9ZpkwZJk6c+JfCJoC2bds+FjZZXu/YsSNxcXHs2rXrr3dIREREsj2NcBIRERH5kxo3bkzevHkz/LwbN24EoHPnzk/cXqRIEUqXLs2PP/7IuXPnKFOmTLrtbdu2fepRWzExMWzcuJHTp0+TkJBgrRV16tQpAM6cOYO/v/9TnVtERESyLwVOIiIiki0UKFAAgOvXrz/1Of6uFeYuXLgAQN26df9w319++eWxwOlp2/X+++8zadIkUlJSfnOf27dvP9W5RUREJHtT4CQiIiLZQrVq1QgLC+PYsWOkpaU91Yigv6uWkdlsBqBDhw7kzp37d/fNly9fhrRrzZo1jB8/HhcXF+bMmUPDhg157rnnyJkzJyaTidGjRzN58uQnTuETERER+SMKnERERCRbaNmyJcOGDePWrVusX7+edu3a2bpJVkWLFuXcuXOMHDmSF1988Zm856pVqwCYNGkS/fr1e2z7uXPnnkk7RERE5J9JRcNFREQkWyhZsiRBQUEADB8+nJs3b/7u/tevX+fMmTMZ8t5OTk4A1vpIj2rRogXw3xDoWbD0v3jx4o9tu379Otu3b39mbREREZF/HgVOIiIikm3861//olSpUly8eJE6deqwb9++x/ZJTk5m0aJFVK1alejo6Ax5Xy8vL+C/hbgfFRwcjJubG6GhoUyfPp3k5OTH9rl48SKff/55hrQHoHz58gDMnz8/3fslJCTQs2dPEhISMuy9REREJPvRlDoRERHJNtzd3dm/fz+dO3dm9+7d1K1bFx8fHypVqkSuXLm4du0ahw8f5tdff8XV1ZXnnnsuQ943MDCQXbt20a1bN5o2bYq7uzvwIGgqW7YsXl5efPPNNwQGBjJixAimTJmCr68vnp6eJCQkEB0dzfnz53n55Zfp1q1bhrRp6NChLFu2jE2bNlGiRAlq1KhBSkoKe/bsIVeuXPTu3ZtFixZlyHuJiIhI9qPASURERLKVggULsmvXLrZs2cKKFSs4cOAAO3fuJCkpiXz58lGzZk0CAgLo3r07Hh4eGfKer7/+Onfu3OHzzz9n06ZN3L9/H4Bu3bpRtmxZAOrVq8epU6eYM2cOGzduJCIigqSkJAoWLEixYsXo1q0bgYGBGdIeAB8fH44fP86YMWP47rvvCA8Pp3DhwgQFBTF+/Hg+/vjjDHsvERERyX5MhpYeERERERERERGRDKQaTiIiIiIiIiIikqEUOImIiIiIiIiISIZS4CQiIiIiIiIiIhlKgZOIiIiIiIiIiGQoBU4iIiIiIiIiIpKhFDiJiIiIiIiIiEiGUuAkIiIiIiIiIiIZSoGTiIiIiIiIiIhkKAVOIiIiIiIiIiKSoRQ4iYiIiIiIiIhIhlLgJCIiIiIiIiIiGUqBk4iIiIiIiIiIZCgFTiIiIiIiIiIikqH+Dx3bsMPfNWoAAAAAAElFTkSuQmCC", + "image/png": "iVBORw0KGgoAAAANSUhEUgAACO8AAAmyCAYAAACFOzwGAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd5gV5f034M/SdpHeVFQERRQLib3SrGhU7L1r1ESNGoPGkgSwm1gwJmKLjRAbKiqJsaNgAXuNRqNYEBsKCCIizPuHL/tz3QUWFA+6931de117Zp6Z+c6cOc85cD77PGVFURQBAAAAAAAAAAC+d/VKXQAAAAAAAAAAANRVwjsAAAAAAAAAAFAiwjsAAAAAAAAAAFAiwjsAAAAAAAAAAFAiwjsAAAAAAAAAAFAiwjsAAAAAAAAAAFAiwjsAAAAAAAAAAFAiwjsAAAAAAAAAAFAiwjsAAAAAAAAAAFAiwjsAAAC1cOCBB6ZTp06lLuNbe//997PrrrumTZs2KSsry6BBg77X448cOTJlZWUZOXJk5bKaru3UqVPz85//PEsvvXTKyspy7LHHJil9/YvKuHHjUlZWlnPPPbfUpdTo6quvTllZWcaNG1e5rHfv3undu3fJamL+BgwYkLKysgVq+9FHHy3iqhgyZEi6du2ahg0bpmXLlklq/3qqqQ9l8VaXn7PevXtnjTXWKHUZtdKpU6dst912821Xl59PAABg0RLeAQCAH7CLL744ZWVl2WCDDUpdymLjqaeeSllZWX73u9/Ntc2rr76asrKyHHfccd9jZYuHX//617nrrrty0kknZciQIdl6663n2XbttddO69ats8QSS2TVVVfNgAEDMnXq1EVe55lnnpmrr746v/zlLzNkyJDst99+C1x/qV188cW5+uqrS10GVHHmmWdm+PDhi2Tf119/fdZee+1UVFSkXbt2OeSQQ2oMA5WVldX4c/bZZ1dp9/DDD2fttddOs2bN0rt377z88svV9nX00UenT58+C1zrrbfemm222SZt27ZNo0aNsswyy2T33XfP/fffv8D7WhAvv/xyDjzwwHTu3DmXX355LrvsskV6vMXVnADEsGHDFmr7RXkfs2DefffdDBgwIM8880ypSwEAAPhBa1DqAgAAgIU3dOjQdOrUKWPHjs1rr72WlVZaqdQlldzaa6+drl275rrrrsvpp59eY5t//OMfSZJ99933+yxtsXD//fdnhx12SL9+/ebb9vHHH0+PHj1y0EEHpaKiIk8//XTOPvvs3HvvvXnooYdSr9538/cgl19+eWbPnl2tzg033DD9+/df6PpL7eKLL07btm1z4IEHlrqUReLuu+8udQnMx+9+97uceOKJVZadeeaZ2XXXXbPjjjt+p8caPHhwjjjiiGy++eY5//zz88477+TCCy/ME088kTFjxqSioqJK+y233DL7779/lWVrrbVW5e+TJ0/ODjvskA033DCHHXZYrr766uyyyy557rnnUr9+/STJiy++mMsvvzxPPvlkressiiIHH3xwrr766qy11lo57rjjsvTSS2fChAm59dZbs/nmm+fhhx/Oxhtv/C2uxtyNHDkys2fPzoUXXljlPdvracEsqvuYBffuu+9m4MCB6dSpU9Zcc81SlwMAAPCDJbwDAAA/UG+88UYeeeSR3HLLLTn88MMzdOjQakGHRW327Nn54osvqn0pW2r77LNPfv/73+exxx7LhhtuWG39ddddl65du2bttdcuQXWl9cEHH1RO0zI/o0ePrrasc+fO6devX8aOHVvjtV0YDRs2rLbsgw8+yGqrrVbj8trWXxtffvllZs+enUaNGn1n+6wrXLPFX4MGDdKgwaL/r58vvvgiJ598cnr27Jl77rmncqqujTfeONtvv30uv/zy/OpXv6qyzcorrzzPAOWjjz6a6dOnZ9iwYamoqMjWW2+dFVZYIa+99lpWWWWVJMmxxx6bQw89tMa+Ym7OO++8XH311Tn22GNz/vnnV5lW7JRTTsmQIUMW6TX74IMPkqRaP+b1VHqff/55GjVq9J0FUymtadOmpUmTJqUuAwAAoNb8axQAAH6ghg4dmlatWmXbbbfNrrvumqFDh1aumzlzZlq3bp2DDjqo2nZTpkxJRUVFlZFLZsyYkf79+2ellVZKeXl5OnTokBNOOCEzZsyosm1ZWVmOOuqoDB06NKuvvnrKy8vz73//O0ly7rnnZuONN06bNm3SuHHjrLPOOjVOhzF9+vQcffTRadu2bZo1a5a+fftm/PjxKSsry4ABA6q0HT9+fA4++OAstdRSKS8vz+qrr54rr7xyvtdmn332SfJ/I+x83ZNPPplXXnmlss1tt92WbbfdNssss0zKy8vTuXPnnHbaaZk1a9Y8jzFnyo+RI0dWWT5u3LiUlZVVmy7p5Zdfzq677prWrVunoqIi6667bm6//fYqbWbOnJmBAwemS5cuqaioSJs2bdK9e/fcc8898z3n119/PbvttlvlFFcbbrhh/vnPf1auv/rqq1NWVpaiKPLXv/61cpqaBdWpU6ckyaRJk+bb9p133smOO+6YJk2aZMkll8yvf/3ravdUkhx44IGV+51zXd94443885//rKxzfvVPmjQpxx57bDp06JDy8vKstNJKOeecc6qM6DPnuTn33HMzaNCgdO7cOeXl5XnppZeS1O45mlPHww8/nOOOOy7t2rVLkyZNstNOO+XDDz+scp1efPHFPPjgg5W19u7du1bX+IILLkjHjh3TuHHj9OrVKy+88EKV9c8991wOPPDArLjiiqmoqMjSSy+dgw8+OBMnTqzS7tNPP82xxx6bTp06pby8PEsuuWS23HLLPPXUU1XajRkzJltvvXVatGiRJZZYIr169crDDz883zp79+5d5ZzmPHc33nhjzjjjjCy33HKpqKjI5ptvntdee63a9rU5bm3PoSbjx4/PIYccUvnaXmGFFfLLX/4yX3zxRZLk448/Tr9+/dKtW7c0bdo0zZs3zzbbbJNnn3222r4uuuiirL766lliiSXSqlWrrLvuutX6l9r2V7XZ19cVRZG2bdtWmeZv9uzZadmyZerXr1/ltXjOOeekQYMGlVPbDRgwoMrrpKysLNOmTcs111xTeV9+c2SoSZMm5cADD0zLli3TokWLHHTQQfnss8/mfqGTvPDCC5k0aVL22GOPKsfbbrvt0rRp01x//fU1bjd9+vR8/vnnc11XUVFRGQ5t3bp1klTWMnz48Dz99NMZOHDgPGv75j7POuusdO3aNeeee26NfeB+++2X9ddfv/Lx/PrWpPb3fqdOnSpDtu3atavyvvfN11NS+z40qd3rac798Nprr9XqOf773/+e9ddfv/Je7dmzZ7URgu6888706NEjTZo0SbNmzbLtttvmxRdfrLHG+altffO7j2vzWpzznF1//fX53e9+l2WXXTZLLLFE5dSb11xzTbX67rrrrpSVlWXEiBFJkjfffDNHHHFEVllllTRu3Dht2rTJbrvtlnHjxs33XF999dXssssuWXrppVNRUZHlllsue+65ZyZPnrxQ164mBx54YJo2bZq33nqr8rW47LLL5q9//WuS5Pnnn89mm22WJk2apGPHjtX6odr0kSNHjsx6662XJDnooIOqvGd/3UsvvZRNN900SyyxRJZddtn88Y9/rNU5fP1z5yqrrJKKioqss846eeihh6q0m3PvvPTSS9l7773TqlWrdO/ePclXId3TTjut8j2/U6dOOfnkk+f6Wrr77ruz5pprpqKiIquttlpuueWWWtW6IK/B//73v9l3333TokWLtGvXLr///e9TFEXefvvt7LDDDmnevHmWXnrpnHfeedWOs6DvIQAAwA+HkXcAAOAHaujQodl5553TqFGj7LXXXhk8eHAef/zxrLfeemnYsGF22mmn3HLLLbn00kur/EX/8OHDM2PGjOy5555JvvoSuG/fvhk9enQOO+ywrLrqqnn++edzwQUX5L///W+GDx9e5bj3339/brzxxhx11FFp27ZtZejiwgsvTN++fbPPPvvkiy++yPXXX5/ddtstI0aMyLbbblu5/YEHHpgbb7wx++23XzbccMM8+OCDVdbP8f7772fDDTes/OKmXbt2ufPOO3PIIYdkypQpOfbYY+d6bVZYYYVsvPHGufHGG3PBBRdUTrGS/F+gZ++9907yVRijadOmOe6449K0adPcf//9+cMf/pApU6bkT3/60wI9J3Pz4osvZpNNNsmyyy6bE088MU2aNMmNN96YHXfcMTfffHN22mmnJF99qXPWWWfl5z//edZff/1MmTIlTzzxRJ566qlsueWWc93/+++/n4033jifffZZjj766LRp0ybXXHNN+vbtm2HDhmWnnXZKz549M2TIkOy33341TlczN19++WUmTZqUL774Ii+88EJ+97vfpVmzZlW+3K7J9OnTs/nmm+ett97K0UcfnWWWWSZDhgzJ/fffP8/tVl111QwZMiS//vWvs9xyy+U3v/lNkq+m05lb/Z999ll69eqV8ePH5/DDD8/yyy+fRx55JCeddFImTJiQQYMGVTnGVVddlc8//zyHHXZYysvL07p161o/R3P86le/SqtWrdK/f/+MGzcugwYNylFHHZUbbrghSTJo0KD86le/StOmTXPKKackSZZaaqn5Xu9rr702n376aY488sh8/vnnufDCC7PZZpvl+eefr9z+nnvuyeuvv56DDjooSy+9dF588cVcdtllefHFF/PYY49VBhJ+8YtfZNiwYTnqqKOy2mqrZeLEiRk9enT+85//VI46df/992ebbbbJOuusk/79+6devXq56qqrstlmm2XUqFHzfZ5rcvbZZ6devXrp169fJk+enD/+8Y/ZZ599MmbMmMo2tT1ubc6hJu+++27WX3/9TJo0KYcddli6du2a8ePHZ9iwYfnss8/SqFGjvP766xk+fHh22223rLDCCnn//fdz6aWXplevXnnppZeyzDLLJPlqWrejjz46u+66a4455ph8/vnnee655zJmzJjKfqS2/VVt9vVNZWVl2WSTTap8Wf3cc89l8uTJqVevXh5++OHKPnTUqFFZa6210rRp0xr3NWTIkMr+5bDDDkvy1WhaX7f77rtnhRVWyFlnnZWnnnoqV1xxRZZccsmcc845c73ec74Eb9y4cbV1jRs3ztNPP53Zs2dXGdHk6quvzsUXX5yiKLLqqqvmd7/7XZVrsNZaa2Xy5Mk577zzsuuuu2bQoEFp0aJFVllllcyYMSO/+c1vMnDgwLRq1WqudX3T6NGj8/HHH+fYY4+t8r4wN7XpW79ufvf+oEGDcu211+bWW2/N4MGD07Rp0/zkJz+p8dgL0ocu6Ou4Ns/xwIEDM2DAgGy88cY59dRT06hRo4wZMyb3339/ttpqqyRf3U8HHHBA+vTpk3POOSefffZZBg8enO7du+fpp5+u/HywoOZX37zu4wX97HDaaaelUaNG6devX2bMmJHVVlstK664Ym688cYccMABVdrecMMNadWqVfr06ZPkq6klH3nkkey5555ZbrnlMm7cuAwePDi9e/fOSy+9lCWWWKLG8/viiy/Sp0+fzJgxI7/61a+y9NJLZ/z48RkxYkQmTZqUFi1aLNR1q8msWbOyzTbbpGfPnvnjH/+YoUOH5qijjkqTJk1yyimnZJ999snOO++cSy65JPvvv3822mijrLDCCklSqz5y1VVXzamnnpo//OEPOeyww9KjR48kqTLt3CeffJKtt946O++8c3bfffcMGzYsv/3tb9OtW7dss8028z2HBx98MDfccEOOPvrolJeX5+KLL87WW2+dsWPHZo011qjSdrfddkuXLl1y5plnpiiKJMnPf/7zXHPNNdl1113zm9/8JmPGjMlZZ52V//znP7n11lurbP/qq69mjz32yC9+8YsccMABueqqq7Lbbrvl3//+9zw/hy3oa3CPPfbIqquumrPPPjv//Oc/c/rpp6d169a59NJLs9lmm+Wcc87J0KFD069fv6y33nrp2bNnkoV7DwEAAH5ACgAA4AfniSeeKJIU99xzT1EURTF79uxiueWWK4455pjKNnfddVeRpLjjjjuqbPuzn/2sWHHFFSsfDxkypKhXr14xatSoKu0uueSSIknx8MMPVy5LUtSrV6948cUXq9X02WefVXn8xRdfFGussUax2WabVS578skniyTFscceW6XtgQceWCQp+vfvX7nskEMOKdq3b1989NFHVdruueeeRYsWLaod75v++te/FkmKu+66q3LZrFmzimWXXbbYaKON5lp3URTF4YcfXiyxxBLF559/XrnsgAMOKDp27Fj5+IEHHiiSFA888ECVbd94440iSXHVVVdVLtt8882Lbt26Vdnf7Nmzi4033rjo0qVL5bKf/vSnxbbbbjvP86rJscceWySp8hx++umnxQorrFB06tSpmDVrVuXyJMWRRx5Z630/+uijRZLKn1VWWaXaOddk0KBBRZLixhtvrFw2bdq0YqWVVqp23b55bYuiKDp27Fjjtaip/tNOO61o0qRJ8d///rfK8hNPPLGoX79+8dZbbxVF8X/PTfPmzYsPPvigStvaPkdXXXVVkaTYYostitmzZ1cu//Wvf13Ur1+/mDRpUuWy1VdfvejVq9dcrlBVc2pr3Lhx8c4771QuHzNmTJGk+PWvf125rKZ79rrrriuSFA899FDlshYtWszzuZ49e3bRpUuXok+fPlXO5bPPPitWWGGFYsstt6x23m+88Ublsl69elU5vzmviVVXXbWYMWNG5fILL7ywSFI8//zzC3zc+Z3D3Oy///5FvXr1iscff7zG8y6Kovj888+rvDaK4qvnoby8vDj11FMrl+2www7F6quvPs/j1ba/qs2+avKnP/2pqF+/fjFlypSiKIriz3/+c9GxY8di/fXXL377298WRfFV/9ayZcsq90r//v2Lb/7XT5MmTYoDDjig2jHmtD344IOrLN9pp52KNm3azLO+Dz/8sCgrKysOOeSQKstffvnlyr7j69dm4403LgYNGlTcdtttxeDBg4s11lijSFJcfPHFNZ73nNfGP/7xj6IoiuKMM84o1lhjjeLLL7+cZ13fNOdevPXWW2vVvrZ9a23v/aL4v+v84YcfVjnWN19Pte1DF+T1VNvn+NVXXy3q1atX7LTTTtVeI3OO8emnnxYtW7YsDj300Crr33vvvaJFixbVln/TnGt20003LXB9RTH3+7i2r8U5x19xxRWr9aknnXRS0bBhw+Ljjz+uXDZjxoyiZcuWVWqrqS+e85557bXXVjvXOc/Z008/Xe3cF4UDDjigSFKceeaZlcs++eSTonHjxkVZWVlx/fXXVy6f81r9+uew2vaRjz/+eLXPPXP06tWr2vWYMWNGsfTSSxe77LLLfM9hTv/xxBNPVC578803i4qKimKnnXaqXDbn3tlrr72qbP/MM88USYqf//znVZb369evSFLcf//9lcs6duxYJCluvvnmymWTJ08u2rdvX6y11lqVy775fC7Ma/Cwww6rXPbll18Wyy23XFFWVlacffbZlcvnPFdfv88X9j0EAAD4YTBtFgAA/AANHTo0Sy21VDbddNMkX43MsMcee+T666+vnO5ps802S9u2bStHAkm++uvne+65J3vssUflsptuuimrrrpqunbtmo8++qjyZ7PNNkuSPPDAA1WO3atXr6y22mrVavr6iAuffPJJJk+enB49elSZ3mbOFFtHHHFElW1/9atfVXlcFEVuvvnmbL/99imKokpdffr0yeTJk+c7bc4ee+yRhg0bVplK4MEHH8z48eMrp8z6Zt2ffvppPvroo/To0SOfffZZXn755XkeozY+/vjj3H///dl9990r9//RRx9l4sSJ6dOnT1599dWMHz8+SdKyZcu8+OKLefXVVxfoGP/617+y/vrrV04RkSRNmzbNYYcdlnHjxlVOC7UwVltttdxzzz0ZPnx4TjjhhDRp0qRySp751dS+ffvsuuuulcuWWGKJylESvks33XRTevTokVatWlW5V7bYYovMmjWr2vQau+yyS9q1a1f5eEGeozkOO+ywKlPu9OjRI7Nmzcqbb775rc5lxx13zLLLLlv5eP31188GG2yQf/3rX5XLvn7Pfv755/noo4+y4YYbJkmV10XLli0zZsyYvPvuuzUe65lnnsmrr76avffeOxMnTqw872nTpmXzzTfPQw89VGXasdo66KCDqoz2NWckhtdff32Bjzu/c6jJ7NmzM3z48Gy//fZZd911q62f87yVl5dXjgQza9asTJw4MU2bNs0qq6xS7Tq+8847efzxx2s83oL0V/Pb19zMub8eeeSRJF+NsNOjR4/06NEjo0aNSvJ/U1fNud4L6xe/+EW1Y0+cODFTpkyZ6zZt27bN7rvvnmuuuSbnnXdeXn/99YwaNaqyH06+GklmjocffjjHHHNM+vbtm1/84hd58skns8Yaa+Tkk0+u0q5fv34ZP358Hn300YwfPz577bVX3n333Zx11lkZNGhQvvzyy/zqV7/K8ssvn/XXX3++073NOYdmzZrV6losaN86v3t/QdS2D12Y1/H8nuPhw4dn9uzZ+cMf/lBltKTk/14/99xzTyZNmpS99tqryj1fv379bLDBBtU+OyyIhbkHk4X77HDAAQdUGzFqjz32yMyZM6tMl3T33XdXTg03x9e3mzlzZiZOnJiVVlopLVu2nOdnlDkj69x1113znZLuu/Dzn/+88veWLVtmlVVWSZMmTbL77rtXLl9llVXSsmXLKvdqbfvI+WnatGn23XffyseNGjXK+uuvX+vXxUYbbZR11lmn8vHyyy+fHXbYIXfddVe1KU6/ee/Mee/8+rSDSSpH9fvmFHjLLLNMlRG1mjdvnv333z9PP/103nvvvRrrW5jX4Nefk/r162fddddNURQ55JBDKpfPea6+fp0W9j0EAAD4YRDeAQCAH5hZs2bl+uuvz6abbpo33ngjr732Wl577bVssMEGef/993PfffclSRo0aJBddtklt912W+WUJrfccktmzpxZ5cunV199NS+++GLatWtX5WfllVdOknzwwQdVjj9nOoVvGjFiRDbccMNUVFSkdevWadeuXQYPHpzJkydXtnnzzTdTr169avtYaaWVqjz+8MMPM2nSpFx22WXV6jrooINqrOub2rRpkz59+uTWW2/N559/nuSrKbMaNGhQ5QurF198MTvttFNatGiR5s2bp127dpVfMn299oX12muvpSiK/P73v692Lv37969yLqeeemomTZqUlVdeOd26dcvxxx+f5557br7HePPNN7PKKqtUW77qqqtWrl9YzZs3zxZbbJEddtgh55xzTn7zm99khx12yLPPPjvfmlZaaaUqAZckNdb5bb366qv597//Xe36brHFFknmfw8vyHM0x/LLL1/l8Zypez755JNvdS5dunSptmzllVfOuHHjKh9//PHHOeaYY7LUUkulcePGadeuXeU5ff2e/eMf/5gXXnghHTp0yPrrr58BAwZU+RJwTkjsgAMOqHbeV1xxRWbMmLFQr4H5XZsFOe78zqEmH374YaZMmVJtOpVvmj17di644IJ06dIl5eXladu2bdq1a1c5JdUcv/3tb9O0adOsv/766dKlS4488sgqIZEF6a/mt6+5WXvttbPEEktUBnXmhHd69uyZJ554Ip9//nnluq8HTRbGwt7bl156aX72s5+lX79+6dy5c3r27Jlu3bpl++23T5K5TuWVfPVl/lFHHZVJkyblySefrLJuqaWWyoYbblhZx29/+9tsvvnm2XzzzXPaaaflvvvuyw033JAdd9wx2267bSZNmjTX4zRv3jzJV0HN2ljQvvW77Bdq24cuzOt4fnX+73//S7169WoM6n7zuJtttlm14959993zfY+el4W9jgvz2aGmzzQ//elP07Vr1yrh5xtuuCFt27atDDYnXwXS/vCHP6RDhw5V+pBJkybNs+9cYYUVctxxx+WKK65I27Zt06dPn/z1r3+db387derUvPfee5U/H3744TzbJ0lFRUWVsGryVXhoueWWq3ZvtWjRoso1rm0fOT81HatVq1a1fl3M7X3xs88+q3YNvvl8zvnc+c3PmUsvvXRatmxZ7TVc02tuzufhr78Pf9138Rps0aJFKioq0rZt22rLv36dFvY9BAAA+GFoUOoCAACABXP//fdnwoQJuf7663P99ddXWz906NBstdVWSZI999wzl156ae68887suOOOufHGG9O1a9f89Kc/rWw/e/bsdOvWLeeff36Nx+vQoUOVx9/8C/Xkqy+S+/btm549e+biiy9O+/bt07Bhw1x11VVVRr6prTl/obzvvvvmgAMOqLHNT37yk/nuZ999982IESMyYsSI9O3bNzfffHO22mqryi+yJk2alF69eqV58+Y59dRT07lz51RUVOSpp57Kb3/723mOOvLNL3fm+OZfgc/ZR79+/dKnT58at5nzpVLPnj3zv//9L7fddlvuvvvuXHHFFbngggtyySWXVPkr7VLaeeeds99+++X666+vch+V0uzZs7PlllvmhBNOqHH9nC/e5vjmPbwgz9Ec9evXr7FdURS1qvnb2H333fPII4/k+OOPz5prrpmmTZtm9uzZ2Xrrravcs7vvvnt69OiRW2+9NXfffXf+9Kc/5Zxzzsktt9ySbbbZprLtn/70p6y55po1HmtegYu5md+1WZDjzu8cvo0zzzwzv//973PwwQfntNNOS+vWrVOvXr0ce+yxVa7jqquumldeeSUjRozIv//979x88825+OKL84c//CEDBw5coP5qfvuam4YNG2aDDTbIQw89lNdeey3vvfdeevTokaWWWiozZ87MmDFjMmrUqHTt2rXaF/ULamHv7RYtWuS2227LW2+9lXHjxqVjx47p2LFjNt5447Rr1y4tW7ac5/Zz3ms+/vjjubZ57LHHMmzYsLzwwgtJkuuuuy6///3vs9FGG2WjjTbKpZdemhEjRlQZ5ePrunbtmiR5/vnns+OOO86znoVRin5hYV7H30Wdc447ZMiQLL300tXWN2iw8P/luLD1Lcxnh5o+0yRfjb5zxhln5KOPPkqzZs1y++23Z6+99qpyXr/61a9y1VVX5dhjj81GG22UFi1apKysLHvuued8Ry0777zzcuCBB1a+3x999NE566yz8thjj2W55ZarcZtzzz23Sj/RsWPHuQZK5pjbtazNNa5tHzk/3+frYm7P59w+s30XvqvXYG2u08K+hwAAAD8MwjsAAPADM3To0Cy55JL561//Wm3dLbfckltvvTWXXHJJGjdunJ49e6Z9+/a54YYb0r1799x///055ZRTqmzTuXPnPPvss9l8880X+suNm2++ORUVFbnrrrtSXl5eufyqq66q0q5jx46ZPXt23njjjSp/Sf3aa69VadeuXbs0a9Yss2bNqhw9ZWH07ds3zZo1yz/+8Y80bNgwn3zySZUps0aOHJmJEyfmlltuSc+ePSuXv/HGG/Pd95yRAL45ysM3/4p7xRVXTPLVl++1OZfWrVvnoIMOykEHHZSpU6emZ8+eGTBgwDzDOx07dswrr7xSbfmcab86duw43+PW1owZMzJ79uz5/tV9x44d88ILL6Qoiir3VU11fludO3fO1KlTF/peWdDnqLYW5vVU05Rp//3vf9OpU6ckX408cd9992XgwIH5wx/+MM/tkqR9+/Y54ogjcsQRR+SDDz7I2muvnTPOOCPbbLNNOnfunOT/Rlf6vizoced1DjVp165dmjdvXhnwmJthw4Zl0003zd/+9rcqyydNmlRt9IMmTZpkjz32yB577JEvvvgiO++8c84444ycdNJJC9xfzWtfFRUVc92uR48eOeecc3Lvvfembdu26dq1a8rKyrL66qtn1KhRGTVqVLbbbrv5Hn9RfomdfDWixJxRJeaMpLPLLrvMd7s5IyrNLXxUFEWOPvroHHPMMZX30Lvvvptlllmmss0yyyxTbYq7r+vevXtatWqV6667LieffPJcvyyf4/vsW2s6dm360EXxOu7cuXNmz56dl156aa5hhDnHXXLJJb/X/mOOmu7j7+qzQ/JVeGfgwIG5+eabs9RSS2XKlCnZc889q7QZNmxYDjjggJx33nmVyz7//PN5jv70dd26dUu3bt3yu9/9Lo888kg22WSTXHLJJTn99NNrbL///vtXGVlrbkGV70pt+8hF3afM7X1xiSWWmG9Ycc7nzldffbVyxKwkef/99zNp0qRqr+E5I/F9/Zz++9//Jknl+/A3fd/vpQv7HgIAACz+TJsFAAA/INOnT88tt9yS7bbbLrvuumu1n6OOOiqffvppbr/99iRJvXr1suuuu+aOO+7IkCFD8uWXX1aZMiv5amSL8ePH5/LLL6/xeNOmTZtvXfXr109ZWVmVUWfGjRuX4cOHV2k3Z1STiy++uMryiy66qNr+dtlll9x88801fgFfm6kikq++2Nppp53yr3/9K4MHD06TJk2yww47VDlOUvWvmr/44otq9dWkY8eOqV+/fh566KEqy7+57ZJLLpnevXvn0ksvzYQJE+Z5LhMnTqyyrmnTpllppZUqpz2bm5/97GcZO3ZsHn300cpl06ZNy2WXXZZOnTrNc+qTuZk0aVJmzpxZbfkVV1yRJFl33XXnW9O7776bYcOGVS777LPPctllly1wLfOz++6759FHH81dd91Vbd2kSZPy5ZdfznP7BXmOFkSTJk1q/SXuHMOHD68SPhg7dmzGjBlTGVSp6Z5NkkGDBlV5PGvWrGoBqyWXXDLLLLNM5f20zjrrpHPnzjn33HMzderUarUs7HnPT22PW5tzqEm9evWy44475o477sgTTzxRbf2ca1e/fv1q1/Gmm26qFv745uuyUaNGWW211VIURWbOnLlA/dX89jUvPXr0yIwZMzJo0KB079698svlHj16ZMiQIXn33XfTo0ePee4jWbj7cmGddNJJ+fLLL/PrX/+6cllN99Wnn36aQYMGpW3btllnnXVq3NfVV1+dt99+u0oAdamllqoM0sycOTOvvfZajaPAzLHEEkvkt7/9bf7zn//kt7/9bY0jf/z973/P2LFjkyyavrW2atuHLorX8Y477ph69erl1FNPrTbCypxr1qdPnzRv3jxnnnlmjffuouo/5qjpPv6uPjskX41w0q1bt9xwww254YYb0r59+yoh3znH++Y9dNFFF1Ubge+bpkyZUu19qVu3bqlXr948+7YVV1wxW2yxReXPJptsUuvzWRi17SObNGmSpHqY+bvy6KOP5qmnnqp8/Pbbb+e2227LVlttNd8A3s9+9rMk1d8j54w2ue2221ZZ/u677+bWW2+tfDxlypRce+21WXPNNefat3yf76Xf5j0EAABY/Bl5BwAAfkBuv/32fPrpp+nbt2+N6zfccMO0a9cuQ4cOrQzp7LHHHrnooovSv3//dOvWrcpfHifJfvvtlxtvvDG/+MUv8sADD2STTTbJrFmz8vLLL+fGG2/MXXfdNd+gxrbbbpvzzz8/W2+9dfbee+988MEH+etf/5qVVlopzz33XGW7ddZZJ7vssksGDRqUiRMnZsMNN8yDDz5Y+VfNX/9L57PPPjsPPPBANthggxx66KFZbbXV8vHHH+epp57KvffeO8+pVb5u3333zbXXXpu77ror++yzT+WXTEmy8cYbp1WrVjnggANy9NFHp6ysLEOGDKnVVA4tWrTIbrvtlosuuihlZWXp3LlzRowYkQ8++KBa27/+9a/p3r17unXrlkMPPTQrrrhi3n///Tz66KN555138uyzzyZJVltttfTu3TvrrLNOWrdunSeeeCLDhg3LUUcdNc9aTjzxxFx33XXZZpttcvTRR6d169a55ppr8sYbb+Tmm29OvXoL/ncbI0eOzNFHH51dd901Xbp0yRdffJFRo0bllltuybrrrjvXaWnmOPTQQ/OXv/wl+++/f5588sm0b98+Q4YMyRJLLLHAtczP8ccfn9tvvz3bbbddDjzwwKyzzjqZNm1ann/++QwbNizjxo2rNpLKN9X2OVoQ66yzTgYPHpzTTz89K620UpZccslsttlm89xmpZVWSvfu3fPLX/6yMqjRpk2byinBmjdvnp49e+aPf/xjZs6cmWWXXTZ33313tdGiPv300yy33HLZdddd89Of/jRNmzbNvffem8cff7xylIh69erliiuuyDbbbJPVV189Bx10UJZddtmMHz8+DzzwQJo3b5477rhjgc97fmp73Nqcw9yceeaZufvuu9OrV68cdthhWXXVVTNhwoTcdNNNGT16dFq2bJntttsup556ag466KBsvPHGef755zN06NDKkZjm2GqrrbL00ktnk002yVJLLZX//Oc/+ctf/pJtt902zZo1S1L7/qo2+5qbjTbaKA0aNMgrr7ySww47rHJ5z549M3jw4CSpVXhnnXXWyb333pvzzz8/yyyzTFZYYYVssMEG891ufs4+++y88MIL2WCDDdKgQYMMHz48d999d04//fSst956le3++te/Zvjw4dl+++2z/PLLZ8KECbnyyivz1ltvZciQIWnUqFG1fX/66ac5+eSTc+aZZ1a5TrvuumtlwOThhx/O559/Xvll/dwcf/zxefHFF3PeeeflgQceyK677pqll1467733XoYPH56xY8fmkUceSbJo+tbaqm0fuihexyuttFJOOeWUnHbaaenRo0d23nnnlJeX5/HHH88yyyyTs846K82bN8/gwYOz3377Ze21186ee+6Zdu3a5a233so///nPbLLJJvnLX/7yXV6SKuZ2H39Xnx2Srz4//eEPf0hFRUUOOeSQas/3dtttlyFDhqRFixZZbbXV8uijj+bee+9NmzZt5rnf+++/P0cddVR22223rLzyyvnyyy8zZMiQyvDR4qK2fWTnzp3TsmXLXHLJJWnWrFmaNGmSDTbYICussMJ3Uscaa6yRPn365Oijj055eXllSLo200T99Kc/zQEHHJDLLruscqrUsWPH5pprrsmOO+6YTTfdtEr7lVdeOYccckgef/zxLLXUUrnyyivz/vvvVxtJ8uu+z/fSb/MeAgAA/AAUAADAD8b2229fVFRUFNOmTZtrmwMPPLBo2LBh8dFHHxVFURSzZ88uOnToUCQpTj/99Bq3+eKLL4pzzjmnWH311Yvy8vKiVatWxTrrrFMMHDiwmDx5cmW7JMWRRx5Z4z7+9re/FV26dCnKy8uLrl27FldddVXRv3//4pv/7Jg2bVpx5JFHFq1bty6aNm1a7LjjjsUrr7xSJCnOPvvsKm3ff//94sgjjyw6dOhQNGzYsFh66aWLzTffvLjssstqdb2Koii+/PLLon379kWS4l//+le19Q8//HCx4YYbFo0bNy6WWWaZ4oQTTijuuuuuIknxwAMPVLY74IADio4dO1bZ9sMPPyx22WWXYokllihatWpVHH744cULL7xQJCmuuuqqKm3/97//Ffvvv3+x9NJLFw0bNiyWXXbZYrvttiuGDRtW2eb0008v1l9//aJly5ZF48aNi65duxZnnHFG8cUXX8z3PP/3v/8Vu+66a9GyZcuioqKiWH/99YsRI0ZUazev5/DrXnvttWL//fcvVlxxxaJx48ZFRUVFsfrqqxf9+/cvpk6dOt/ti6Io3nzzzaJv377FEkssUbRt27Y45phjin//+9+1urYdO3Ystt1221rX/+mnnxYnnXRSsdJKKxWNGjUq2rZtW2y88cbFueeeW3n93njjjSJJ8ac//anGemvzHF111VVFkuLxxx+vsu0DDzxQ7bzee++9Ytttty2aNWtWJCl69eo112v19drOO++8okOHDkV5eXnRo0eP4tlnn63S9p133il22mmnomXLlkWLFi2K3XbbrXj33XeLJEX//v2LoiiKGTNmFMcff3zx05/+tGjWrFnRpEmT4qc//Wlx8cUXVzv2008/Xey8885FmzZtivLy8qJjx47F7rvvXtx3333VzvuNN96oXNarV68q5zTnGtx00001nts3XxPzO+6CnENN3nzzzWL//fcv2rVrV5SXlxcrrrhiceSRRxYzZswoiqIoPv/88+I3v/lN0b59+6Jx48bFJptsUjz66KPVzuvSSy8tevbsWVln586di+OPP75K31gUteuvaruvuVlvvfWKJMWYMWMql73zzjtFkqJDhw7V2tfUB7/88stFz549i8aNGxdJigMOOKBK2w8//LBK+5qe+5qMGDGiWH/99YtmzZoVSyyxRLHhhhsWN954Y7V2d999d7HllltWvs5atmxZbLXVVlXut286/vjji3XXXbeYPXt2leVTp04t9t9//6Jly5ZF165di3//+9/zrPHrhg0bVmy11VZF69atiwYNGhTt27cv9thjj2LkyJFV2tWmb12Qe39u1/mb911R1L4PLYravY4X9Dm+8sori7XWWqvyc0GvXr2Ke+65p9q59+nTp2jRokVRUVFRdO7cuTjwwAOLJ554opiXmq7ZgtQ3t/u4KGr3Wpzbc/Z1r776apGkSFKMHj262vpPPvmkOOigg4q2bdsWTZs2Lfr06VO8/PLLRceOHavU8833h9dff704+OCDi86dOxcVFRVF69ati0033bS4995753nNFtQBBxxQNGnSpNryXr16Fauvvnq15d98361tH1kURXHbbbcVq622WtGgQYMq9/zcjlXT+35N5rzn//3vf6/8jLnWWmtVu//ndu8URVHMnDmzGDhwYLHCCisUDRs2LDp06FCcdNJJxeeff17j+d91113FT37yk8rPs9+8R2p6vy+Kb/carO1z9W3fQwAAgMVbWVHU4k9KAQAAFqFnnnkma621Vv7+979nn332KXU5AACUWFlZWY488shFOooTAADA4mLRje8LAABQg+nTp1dbNmjQoNSrVy89e/YsQUUAAAAAAFA6DUpdAAAAULf88Y9/zJNPPplNN900DRo0yJ133pk777wzhx12WDp06FDq8gAAAAAA4HslvAMAAHyvNt5449xzzz057bTTMnXq1Cy//PIZMGBATjnllFKXBgAAAAAA37uyoiiKUhcBAAAAAAAAAAB1Ub1SFwAAAAAAAAAAAHWV8A4AAAAAAAAAAJSI8A4AAAAAAAAAAJSI8A4AAAAAAAAAAJSI8A4AAN/agAEDUlZWVuWna9eupS4LAAAAAABgsdeg1AUAAPDjsPrqq+fee++tfNyggY+aAAAAAAAA8+MbFQAAvhMNGjTI0ksvXeoyAAAAAAAAflBMmwUAwHfi1VdfzTLLLJMVV1wx++yzT956661SlwQAAAAAALDYKyuKoih1EQAA/LDdeeedmTp1alZZZZVMmDAhAwcOzPjx4/PCCy+kWbNmNW4zY8aMzJgxo/Lx7Nmz8/HHH6dNmzYpKyv7vkoHAACgjiqKIp9++mmWWWaZ1Kvnb50BACgd4R0AAL5zkyZNSseOHXP++efnkEMOqbHNgAEDMnDgwO+5MgAAAKjq7bffznLLLVfqMgAAqMOEdwAAWCTWW2+9bLHFFjnrrLNqXP/NkXcmT56c5ZdfPm+//XaaN2/+fZUJAABAHTVlypR06NAhkyZNSosWLUpdDgAAdViDUhcAAMCPz9SpU/O///0v++2331zblJeXp7y8vNry5s2bC+8AAADwvTF1MwAApWYSVwAAvrV+/frlwQcfzLhx4/LII49kp512Sv369bPXXnuVujQAAAAAAIDFmpF3AAD41t55553stddemThxYtq1a5fu3bvnscceS7t27UpdGgAAAAAAwGJNeAcAgG/t+uuvL3UJAAAAAAAAP0imzQIAAAAAAAAAgBIR3gEAAAAAAAAAgBIxbRYAAMAPwMyZMzNr1qxSlwEA/MDUr18/DRs2LHUZAAAAzIPwDgAAwGJsypQp+eijjzJjxoxSlwIA/ECVl5enbdu2ad68ealLAQAAoAbCOwAAAIupKVOmZPz48WnatGnatm2bhg0bpqysrNRlAQA/EEVRZObMmZk8eXLGjx+fJAI8AAAAiyHhHQAAgMXURx99lKZNm2a55ZYT2gEAFkrjxo3TrFmzvPPOO/noo4+EdwAAABZD9UpdAAAAANXNnDkzM2bMSIsWLQR3AIBvpaysLC1atMiMGTMyc+bMUpcDAADANwjvAAAALIZmzZqVJGnYsGGJKwEAfgzmfKaY8xkDAACAxYfwDgAAwGLMqDsAwHfBZwoAAIDFl/AOAAAAAAAAAACUiPAOAAAAAAAAAACUiPAOAAAA1AGDBg1Ko0aNMm7cuFKX8oNx9dVXp6ysLFdfffX3crwePXpkgw02+F6ORe3NnDkzAwYMSJcuXVJeXp6ysrIMHz58gfczcuTIlJWVZcCAAVWW9+7de5FOZzS3487P8OHDU1ZWlkceeWTRFPYjpM8AAABgYQnvAAAAwI/cJ598ktNOOy0HH3xwOnXqVGXdl19+mSuvvDIbbbRR2rVrl2bNmmW11VbLCSeckPfee680BddRAwYMyNixY3P99deXuhS+5rzzzsvAgQOzzDLLpF+/funfv3+6du1a6rIWqZkzZ+aEE05Inz59svHGG1db//jjj+dnP/tZWrZsmSZNmmTDDTfMjTfeWIJK6zZ9BgAAwI9Hg1IXAAAAwMIpKzu31CXMVVH0K3UJfM0FF1yQjz/+OMcff3y1dXvssUduueWWrLTSStlzzz1TXl6exx57LH/605/y97//PU899VSWXnrpElRd92y++eZZe+21079//+yxxx6LdDSW78otr0wodQlztfMq7b+T/YwYMSJNmzbNPffck0aNGi30ftZff/385z//Sdu2bb+TuhalIUOG5NVXX80ll1xSbd0DDzyQPn36pKKiInvuuWeaNWuWm2++OXvssUfefvvt/OY3vylBxXXTD7HPAAAAoGZG3gEAAIAfsS+//DJXXHFFNtlkk3Tu3LnKurFjx+aWW27J+uuvn5deeikXXXRRzj333IwePTpHH310JkyYkMsuu6xElddN++67b/773//m/vvvL3Up/H/vvvtu2rRp862CO0myxBJLpGvXrj+I8M7gwYPToUOHbLrpplWWf/nllzn00ENTr169PPTQQ7nsssty3nnn5dlnn83KK6+ck08+OW+++WaJqq6b9BkAAAA/DsI7AAAALJZuvvnm9OrVK0suuWQqKiqyzDLLZIsttsjNN99c2WbkyJEpKyvLgAEDqm0/bty4lJWV5cADD6y27oMPPshvfvObrLLKKmncuHFat26dDTbYIOeeW300o2effTb77LNPlltuuZSXl6d9+/bZeuutc8cdd1Rre9ttt2XzzTdPq1atUlFRkTXWWCPnnntuZs2aVaXd7Nmzc8UVV2T99ddP69at07hx4yy33HLZfvvtM3LkyAW+DvPy73//OxMmTMhuu+1Wbd3rr7+eJNliiy3SsGHDKuu22267JMmHH35Yq+NMmDAhxxxzTLp06ZLGjRunZcuWWXXVVfOLX/wikydPrmz33//+NyeccELWXnvttGnTJhUVFVl55ZVz4oknZurUqdX227t375SVlWXGjBk5+eSTs/zyy6dx48ZZZ511cu+99yZJJk+enCOPPDLLLLNMKioqstFGG2Xs2LHV9tWpU6d06tQpkyZNyuGHH56ll146FRUVWWuttXLdddfV6jzneOONN/Lzn/88yy+/fOV9ceCBB9YYXHjqqaey6667VrZt165d1ltvvZxxxhnV2s55nq6++uoFqofv3oABA1JWVpY33ngjb775ZsrKylJWVlY59dwXX3yRiy66KH369EmHDh1SXl6eJZdcMjvvvHOefvrpavubV381N7XtU5Jk+vTpOfHEE9OhQ4fKtpdffvkCn/cLL7yQJ554Irvssku1kVzuv//+/O9//8vee++dNddcs3J5ixYtcvLJJ+eLL77INddcU6vj6DP0GQAAAPwf02YBAACw2Bk8eHCOOOKItG/fPjvttFPatGmT9957L2PHjs2tt96aXXbZZaH3/corr2TTTTfNhAkT0r179+y4446ZNm1aXnzxxZx55pnp1+//pvy6+eabs/fee6coimy//fZZZZVV8sEHH2TMmDH529/+lu23376y7UknnZSzzz47yy67bHbeeee0aNEio0aNyvHHH58xY8bkpptuqtL2j3/8Yzp37py99947zZo1y/jx4zN69Ojce++96d2793d2He67774kyYYbblht3eqrr54kuffeezNgwIAqAZ4RI0Yk+Wpalvn57LPPsskmm2TcuHHZaqutstNOO+WLL77IG2+8kSFDhqRfv35p0aJFkuSWW27J3/72t2y66abp3bt3Zs+encceeyznnHNOHnzwwTz00EPVgkTJV9N7Pf/88+nbt2+mT5+eoUOHZrvttsvDDz+cww47LF988UV22223fPjhh7nhhhuy9dZb54033qg87hxffPFFtthii0ydOjX77bdfpk2blhtvvDF77713Pvroo/zqV7+a7/mOGTMmffr0ybRp07LddtulS5cuGTduXIYOHZo777wzjz76aFZcccUkyTPPPJONN9449evXzw477JCOHTtm0qRJeemll3LZZZfllFNOqbLv5ZZbLh06dKh83iidOa/DQYMGJUmOPfbYJEnLli2TJB9//HGOPfbY9OjRIz/72c/SqlWrvP7667n99ttz55135qGHHsp666230MdfkD5l9uzZ6du3b+69995069Yte++9dyZOnJhf//rX1UbPmZ959RlzwoVbbbVVtXV9+vRJkjz44IPzPYY+Q58BAABAVcI7AAAALHauuOKKNGrUKM8880yWXHLJKusmTpz4rfa97777Vk4Hdeihh1ZZ984771T+/v777+eAAw5Iw4YNM2rUqKy11lpzbXvPPffk7LPPTp8+fXLzzTenSZMmSZKiKHLEEUfkkksuyc0331wZtrniiiuyzDLL5LnnnssSSyxRZb8ff/xx5e/fxXV4+OGHU69evSqjZMzRrVu3HHPMMbnwwguz2mqrZZtttkl5eXkeffTRPPnkkxk4cGB23HHH+R7jvvvuyxtvvJFjjz02F1xwQZV1U6dOrfLF+n777Zfjjjuu2hREp556avr3758bb7wx++yzT7VjTJw4Mc8991zlte3Tp0/22GOPbLHFFtlyyy3zj3/8Iw0afPXfHGuuuWZ++9vf5m9/+1uOO+64KvuZMGFCunTpkkceeaSyhpNPPjlrrbVWjj/++Oy8885Zdtll53quM2fOzJ577pnZs2dn7NixVe6L0aNHp3fv3jnmmGMqR2YaMmRIZsyYkeHDh2eHHXaodk41WXfddXPrrbfmjTfeyAorrDDXWli0evfund69e1eOaPLNEXNatWqVt956q9r98uKLL2bDDTfMySefnHvuuWehjr2gfcq1116be++9N1tvvXVGjBiR+vXrJ0mOOeaYrLvuugt07IcffjhJss4661Rb9+qrryZJunTpUm3d0ksvnaZNm1a2mRd9xlf0GQAAAMxh2iwAAAAWSw0bNqxxNIU2bdos9D7Hjh2bJ554Ij179qwW3Em+GsFgjmuuuSbTpk3Lb37zm2rBnW+2/ctf/pIkueyyyyq/KE6SsrKynH322SkrK6s2xUqjRo0qv2D/utatW1d5/G2vwzvvvJOWLVumvLy8xvWDBg3KoEGD8uabb+aiiy7Kueeem4cffjhbbrlldt5551odY47GjRtXW9a0adMqx1522WWrfQmfJEcddVSSVE5r801nnHFGlWu76667pmHDhpk0aVLOPffcyi/hk2SvvfZK8tWUZzU588wzq9Sw3HLL5ZhjjsmMGTNy/fXXz+sUM2LEiIwbNy7HH398tfuie/fu2WGHHfKvf/0rU6ZMqbKupmszt+dwqaWWSlI1IMbip7y8vMbQxuqrr55NN900Dz30UGbOnLlQ+17QPuXaa69N8tXr5Ov9Srdu3bLffvst0LHn3Hdz7sOvmzOd1TdHp5mjefPmVaa8mh99hj4DAACArxh5BwAAgMXOnnvumRNOOCFrrLFG9t5772y66abp3r17mjdv/q32O3bs2CQ1T/nybdo+9thjadKkSa688soa1zdu3Dgvv/xy5eM999wzF198cdZYY43sueee2XTTTbPRRhtV+7L2u7gOEydOrBI0+rrZs2fnF7/4Ra677rpcdNFF2WGHHbLEEkvk4YcfztFHH50NN9wwDzzwwHyn/unZs2fat2+fs88+O88++2y222679OrVK6uuumrKysqqtC2KIldddVWuvvrqvPDCC5k8eXJmz55duf7dd9+t8RjfHDmoXr16WXLJJfPZZ59l+eWXr7Kuffv2c91XgwYNstFGG1Vb3qNHjyTJ008/Pc9zfeyxx5J8Nf3aN0diSZL33nsvs2fPzn//+9+su+662X333TNo0KDstNNO2WOPPbLlllumZ8+e8xypY06A66OPPppnLZTeM888kz/+8Y8ZPXp03nvvvWphnY8++qjyflwQC9qnPPvss2nSpEnWXnvtam179OiRv/3tb7U+9sSJE1O/fv00a9ZsgeuuLX3G/9FnAAAAkAjvAAAAsBjq169f2rRpk8GDB+e8886rHCVh2223zQUXXLDQ04LMGRFiXl+CLkzbjz/+OF9++WUGDhw41zbTpk2r/P3CCy/MCiuskKuuuiqnn356Tj/99FRUVGT33XfPeeedl7Zt2yb5bq5D48aN8/nnn9e47sorr8zll1+eCy+8MIcffnjl8m222SbDhg3LmmuuWaupf1q0aJHHHnssf/jDH3LHHXfkX//6V5KkQ4cOOfHEE3PEEUdUtj366KPzl7/8JR06dEjfvn3Tvn37ylE2Bg4cmBkzZtR4jJoCSw0aNJjr8iQ1jnrStm3b1KtXfSDiOSNXzG/UkDnTmg0dOnSe7eY83xtssEFGjhyZM888M//4xz9y1VVXJUnWW2+9nHPOOdl0002rbTt9+vQkqTalGouXRx55JJtttlmSr0J+Xbp0SdOmTVNWVpbhw4fn2Wefnev9PD8L2qdMnjw5HTp0qLFdTSPozEvjxo0za9aszJw5s9qoX3NG3Jnb62TKlClp1arVfI+hz6hOnwEAAFC3Ce8AAACw2CkrK8vBBx+cgw8+OBMnTsyoUaNy3XXX5cYbb8yrr76a5557LvXr16/8QvXLL7+sto+avlBt2bJlkmT8+PHzreHrbTt16jTPts2bN09ZWVmtRz1o0KBB+vXrl379+uXdd9/Ngw8+mKuuuirXXntt3nvvvdx1111Jan8d5qVdu3ZznUrlzjvvTJIavwz+6U9/mlatWs13VIk5ll9++Vx99dWZPXt2nnvuudx9993585//nCOPPDKtWrXKXnvtlQ8++CB//etf85Of/CSPPvpolS+a33vvvXkGFb4rH330UWbPnl3ty/j3338/ydynA5pjzhf/d9xxR7bbbrtaHbNHjx658847M3369IwZMyZ33HFHLr744my77bZ54YUXsuKKK1ZpP+fL/nbt2tVq/5TGGWeckRkzZmTUqFHp3r17lXWPPfbYXKdgqo0F7VNatGiRDz/8sMZ1c+7t2ppz33388cfVgj9dunRJkrz66qtZZ511qqx77733MnXq1Ky//vq1Oo4+Y+70GQAAAHVP9T8bAQAAgMVImzZtsuOOO+aGG27IZpttlpdeeimvvfZaklSO8FBTGKem0MmcL5Xvvvvu+R53QdpusMEGmThxYl599dX5tv2mZZZZJnvttVf+/e9/Z6WVVsq9995bOYrC183rOsxLt27d8vnnn+ett96qtu6LL75Ikhq/9J8xY0Y+/fTTyhEuaqtevXpZc801c8IJJ+S6665Lktx+++1Jktdffz1FUWSLLbaoNkLEqFGjFug4C+vLL7/Mo48+Wm35nOOvtdZa89x+gw02SJIa9zE/jRs3Tu/evXPeeefl5JNPzvTp02sc1eiVV15Jw4YN07Vr1wU+Bt+f//3vf2ndunW14M5nn32Wp5566lvte0H7lJ/+9KeZNm1ajcdd0NdWt27dknx1H35Tr169ktTcL84JHc5pU1v6jLnTZwAAANQdwjsAAAAsdkaOHJmiKKosmzlzZuXoAhUVFUmSVVZZJc2aNcvtt99euS75akSE008/vdp+11tvvay33np56KGHcvnll1db//UQ0AEHHJCmTZvmvPPOyzPPPDPPtkcffXSSVI6Q803vvfde/vOf/yT5KhTzyCOPVGszbdq0TJ06NQ0bNqwc4aG212Fe5nyRPmbMmGrrNtlkkyTJmWeeWW3qmQEDBuTLL7+scVSeb3rxxRdrHN1jzrI5dXbs2DHJV9MNzZ49u7LdO++8k5NOOmm+x/munHzyyZXBpTnHv/DCC1NeXp4999xzntvusMMOWX755XP++efnoYceqrZ+5syZGT16dOXjRx99tMZpy755beb44osv8vTTT2fdddc1Bc5irmPHjvnkk0/y4osvVi6bNWtW+vXrN9dRcGprQfqUJNlvv/2SJKecckpmzZpVufz555/PkCFDFujY8+ozNt9886y44or5xz/+UaVfnDx5cs4888w0atQo+++//3yPoc/4P/oMAAAAEtNmAQAAsBjacccd07x582y44Ybp2LFjZs6cmXvuuScvvfRSdt1118ovdBs1apRf/epXOfPMM7P22mtnhx12yKeffpo77rgjvXr1yv/+979q+x46dGh69+6dww47LEOGDMlGG22Uzz//PC+++GKefvrpyi/Kl1xyyVx77bXZc889s/7666dv375ZZZVV8tFHH2XMmDHp1KlThg8fniTZeuut8/vf/z6nnXZaVlpppWy99dbp2LFjJk6cmNdeey2jRo3K6aefnlVXXTXTp0/PJptskpVXXjnrrLNOll9++UydOjUjRozIe++9l379+lWOdlPb6zAvO+ywQ4477rjcc8892W233aqsO+KII3LNNdfkvvvuS9euXbP11luncePGefjhhzN27Ni0a9cup5566nyPcc899+T444+vPK82bdrk9ddfz+23356KiooceeSRSZL27dtnl112yc0335x11103m2++ed5///2MGDEim2++eY3P13etffv2mTZtWn7yk59k++23z7Rp03LjjTdm4sSJ+fOf/5xll112ntuXl5dn2LBh2WabbdKrV69sttlm6datW8rKyvLmm29m1KhRadOmTV5++eUkyTnnnJMHHnggPXv2zAorrJCKioo89dRTue+++7Liiitmp512qrL/UaNGZcaMGdlxxx0X1SXgO/KrX/0qd999d7p3757dd989FRUVGTlyZMaPH5/evXtn5MiRC73vBelTkq/Chv/4xz/y73//O2uttVa22WabfPzxx7nuuuuy1VZbZcSIEbU+9uabb55mzZpVvq6/rkGDBrniiivSp0+f9OzZM3vuuWeaNWuWm2++OW+++WbOPffc+U4zmOgz9BkAAAB8k/AOAAAAi52zzjor//73vzN27NjccccdadKkSTp37pzBgwfnkEMOqdL2tNNOS6NGjfK3v/0tl1xySTp16pTf//732X777XPzzTdX23eXLl3y1FNP5ayzzsodd9yRQYMGpWnTpunSpUt+97vfVWm70047ZcyYMTnrrLPy4IMP5vbbb0/btm2z5ppr5tBDD63S9tRTT03Pnj3z5z//Offdd18mTZqUNm3aZIUVVsiAAQOyzz77JEmaNGmSc845J/fdd19GjRqVDz74IK1atcoqq6ySs846q8ooDgtyHeamU6dO6dOnT4YNG5aLLrqoyjRYzZs3z2OPPZZzzjknt912W66++urMmjUryy23XH7xi1/klFNOyXLLLTffY/Tp0yfjxo3LQw89lFtuuSVTp07Nsssumz322CMnnHBCVltttcq2V199dTp16pSbb745F110UZZffvkcd9xx+e1vf5thw4bV6py+jUaNGuWee+7JiSeemCFDhmTSpEnp2rVrLrroouy111612sd6662XZ599Nn/605/yr3/9Kw8//HDKy8uz7LLLZscdd6yyn1/+8pdp0aJFxowZkwcffDBFUWT55ZfPySefnF//+tdp3rx5lX3//e9/T6NGjXLQQQd9p+fNd2+77bbLsGHDcuaZZ+bvf/97llhiiWy22Wa59dZbaxV6m5/a9inJV1NP3XbbbRk4cGCGDh2aCy+8MJ07d84FF1yQLl26LFB4p2nTptl3331z2WWXZcKECWnfvn2V9ZtuumlGjx6d/v3754YbbsjMmTPTrVu3nHPOOdljjz1qdQx9hj4DAACAqsqKb46/DQAAJTBlypS0aNEikydPrvalBNRFn3/+ed54443Kv7qHb+O+++7LFltskb///e9VvvCva+aMCDJu3LiS1jE3n3zySTp27Jhdd901V155ZanLoQ575ZVXssYaa2TAgAE55ZRTSl1O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AAAAAMAPnZF3AABIkvTu3TtrrbVWzj///O9kf7Nnz86MGTO+k30BAAAAAAD8WBl5BwCASiuuuGLuvPPOBd7upJNOyjbbbJPll18+n376af7xj39k5MiRueuuuxZBlQAAAAAAAD8ewjsAAFR69dVXUxTFAm/3wQcfZP/998+ECRPSokWL/OQnP8ldd92VLbfcchFUCQAAAAAA8OMhvAMAQL788succ845eeaZZ9K9e/cF3v5vf/vbIqgKAAAAAADgx094BwCgDthss83muu7TTz/N66+/nkmTJqVevXo5+eSTv8fKAAAAAAAA6jbhHQCAOmDkyJHzbdOlS5ecffbZ2XrrrRd9QQAAAAAAACQR3gEAqBMeeOCBua5r1KhRll122Sy//PLfY0UAAAAAAAAkwjsAAHVCr169Sl0CAAAAAAAANahX6gIAAAAAAAAAAKCuEt4BAAAAAAAAAIASMW0WAMCP0GabbbbQ25aVleW+++77DqsBAAAAAABgboR3AAB+hEaOHLnQ25aVlX13hQAAAAAAADBPwjsAAD9CDzzwQKlLAAAAAAAAoBaEdwAAfoR69epV6hIAAAAAAACohXqlLgAAAAAAAAAAAOoq4R0AAAAAAAAAACgR02YBANQhEyZMyG233ZZXXnklU6ZMSVEU1dqUlZXlb3/7WwmqAwAAAAAAqHuEdwAA6oiLLrooxx9/fGbOnFm5bE54p6ysrPKx8A4AAAAAAMD3x7RZAAB1wH333ZdjjjkmFRUVOfHEE7PRRhslSS699NL85je/SadOnZIkxx57bK688soSVgoAAAAAAFC3CO8AANQBF154YcrKynLXXXfljDPOSJcuXZIkhx56aP70pz/lpZdeygEHHJArr7wyPXr0KHG1AAAAAAAAdYfwDgBAHTB27Nisvfba2WCDDWpcX15ensGDB6eioiKnnnrq91wdAAAAAABA3SW8AwBQB3zyySfp3Llz5eOGDRsmSaZPn165rLy8PD169Mh99933vdcHAAAAAABQVwnvAADUAa1bt860adMqH7dq1SpJ8tZbb1VpN2vWrEycOPF7rQ0AAAAAAKAuE94BAKgDll9++bz99tuVj9dYY40URZERI0ZULps6dWpGjRqV5ZZbrhQlAgAAAAAA1EkNSl0AAACLXq9evXLBBRfk/fffz1JLLZVtt902TZo0ycknn5z33nsvyy+/fK655pp8/PHH2XPPPUtdLgAAAAAAQJ0hvAMAUAfstttuefrpp/PMM8+kT58+ad26dc4///z84he/yPnnn58kKYoinTp1ysCBA0tcLQAAAAAAQN0hvAMA8CM0bNiw7LjjjmnQ4KuPe+utt17uueeeKm0OPfTQrLPOOrnpppvy8ccfZ9VVV81BBx2UFi1alKJkAAAAAACAOkl4BwDgR2j33XdPu3btsu++++aggw7KGmusUWO7tddeO2uvvfb3XB0AAAAAAABz1Ct1AQAAfPfatGmTDz/8MIMGDcpPf/rTbLjhhrn88svz6aeflro0AAAAAAAAvkZ4BwDgR2jChAkZNmxYtt5669SrVy9jx47NL37xi7Rv3z4HHnhgHnrooVKXCAAAAAAAQIR3AAB+lBo0aJCdd945//znP/P222/nzDPPTJcuXfLZZ5/l2muvzaabbpqVV14555xzTiZMmFDqcgEAAAAAAOos4R0AgB+5pZdeOieeeGJefvnljB49OgcddFCaNGmS1157LSeffHI6duyYvn375rbbbsusWbNKXS4APzpTkrzzjZ/xX1s/vob17/z/7QAAAADgx69BqQsAAOD7s/HGG2fjjTfORRddlBtuuCFXXXVVRo8enREjRuSf//xn2rVrl/333z9//OMfS10qAD8ajyW5Zx7rL57L8i0XQS0wx5RUD4jN/Nrv45M0rGG75ousIgAAAKDuEt4BAKiDllhiiRx00EE56KCD8r///S9XXnllLrvssnzwwQc577zzhHcA+A5tmGS1hdhOSIJFSagMAAAAWHwI7wAA1GEzZszI2LFjM3bs2HzyySelLgeAH6XmEcRh8SNUBgAAACw+hHcAAOqgJ554IldeeWWuv/76TJ48OUVRpH79+vnZz36WQw45pNTlAQAsYkJlAAAAwOJDeAcAoI746KOP8ve//z1XXXVVXnjhhSRJURRZccUVc/DBB+fAAw/MMsssU+IqAQAAAAAA6hbhHQCAH7HZs2fnzjvvzJVXXpl//vOfmTlzZoqiSEVFRXbeeecccsgh2XTTTUtdJgAAAAAAQJ0lvAMA8CP03//+N1deeWWGDBmS9957L0VRJEnWXHPNHHLIIdl3333TokWLElcJAAAAAACA8A4AwI/QqquumuSrabFatmyZvffeO4ccckjWWmutElcGAAAAAADA1wnvAAD8CBVFkd69e+eQQw7JLrvskoqKilKXBAAAAAAAQA2EdwAAfoRee+21rLjiiqUuAwAAAAAAgPmoV+oCAAD47gnuAAAAAAAA/DAI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIk0KHUBAAB8/1577bV8+OGHadOmTVZeeeVSlwMAAAAAAFBnGXkHAKCOmDVrVk4//fQsvfTSWWWVVdK9e/ecffbZleuHDh2ajTfeOC+++GIJqwQAAAAAAKhbhHcAAOqAWbNmZbvttkv//v3zySefZNVVV01RFFXabLLJJnnsscdyyy23lKhKAAAAAACAukd4BwCgDrjkkkty1113ZdNNN80bb7yRF154oVqbTp06pXPnzrn77rtLUCEAAAAAAEDdJLwDAFAHXHPNNWndunVuuummLLPMMnNtt+qqq+att976HisDAAAAAACo24R3AADqgJdffjnrr79+WrVqNc92LVq0yAcffPA9VQUAAAAAAIDwDgBAHTBr1qyUl5fPt92ECRNq1Q4AAAAAAIDvhvAOAEAd0LFjxzz33HPzbDNz5sy88MIL6dKly/dUFQAAAAAAAMI7AAB1wNZbb51x48blsssum2ubiy66KB9++GG23Xbb77EyAAAAAACAuq1BqQsAAGDRO/7443P11VfniCOOyEsvvZTdd989STJt2rQ89dRTufHGG3P++eenbdu2Oeqoo0pcLQAAAAAAQN1h5B0AgDqgffv2GT58eFq2bJk///nP6dGjR8rKyjJs2LCst956+eMf/5imTZvm5ptvTtu2bUtdLgAAAAAAQJ0hvAMAUEf07NkzL774Yk444YSsvvrqady4ccrLy7PSSivl6KOPzvPPP5/u3buXukwAAAAAAIA6xbRZAAB1yFJLLZWzzz47Z599dqlLAQAAAAAAIEbeAQAAAAAAAACAkhHeAQAAAAAAAACAEjFtFgBAHbDZZpvVql2jRo3Stm3brLvuutlrr72y1FJLLeLKAAAAAAAA6jbhHQCAOmDkyJFJkrKysiRJURTV2pSVlVUuv+6663LKKadk8ODB2X///b+3OgEAAAAAAOoa4R0AgDrggQceyIgRI3LeeedlvfXWy957751OnTqlrKws48aNyz/+8Y+MHTs2xx13XNZcc83cf//9ueaaa/Lzn/88Xbt2zfrrr1/qUwAAAAAAAPhREt4BAKgDGjVqlAsvvDDnn39+jj322Grrjz766Fx44YU5/vjjM3LkyOy7777ZaKONcvjhh+fCCy/M0KFDv/+iAQAAAAAA6oB6pS4AAIBF77TTTkvXrl1rDO7Mccwxx6Rr1645/fTTkyQ///nP06lTp4wePfp7qhIAAAAAAKDuEd4BAKgDxo4dm27dus23Xbdu3TJmzJgkSVlZWVZbbbV88MEHi7o8AAAAAACAOkt4BwCgDpg+fXomTJgw33YTJkzI559/Xvm4SZMmadDATKsAAAAAAACLivAOAEAdsOqqq2bUqFGVo+rUZMyYMRk1alRWW221ymXjx49P27Ztv48SAQAAAAAA6iThHQCAOuCII47IrFmzstVWW+X3v/99/vOf/2T69OmZPn16Xn755fzhD39Inz59Mnv27Pzyl79Mknz22Wd5+umns84665S4egAAAAAAgB8vcyAAANQBBx98cJ544olccsklOfPMM3PmmWdWa1MURQ4//PAcfPDBSZJx48Zl9913z5577vl9lwsAAAAAAFBnCO8AANQRF198cbbeeutceOGFefTRR/P5558nScrLy7PRRhvl6KOPzo477ljZfrXVVstVV11VomoBAKDumDBhQiZMmLDA27Vv3z7t27dfBBUBAADwfRLeAQCoQ/r27Zu+fftm1qxZ+eijj5Ikbdq0SYMGPhYCAECpXHrppRk4cOACb9e/f/8MGDDguy8IAACA75VvaQAA6qD69etnqaWWKnUZAABAksMPPzx9+/atsmz69Onp3r17kmT06NFp3Lhxte2MugMAAPDjILwDAAAAAFBCNU1/NW3atMrf11xzzTRp0uT7LgsAAIDvifAOAEAdMmHChNx222155ZVXMmXKlBRFUa1NWVlZ/va3v5WgOgAAAAAAgLpHeAcAoI646KKLcvzxx2fmzJmVy+aEd8rKyiofC+8AAAAAAAB8f+qVugAAABa9++67L8ccc0wqKipy4oknZqONNkqSXHrppfnNb36TTp06JUmOPfbYXHnllSWsFAAAAAAAoG4R3gEAqAMuvPDClJWV5a677soZZ5yRLl26JEkOPfTQ/OlPf8pLL72UAw44IFdeeWV69OhR4moBAAAAAADqDuEdAIA6YOzYsVl77bWzwQYb1Li+vLw8gwcPTkVFRU499dTvuToAAAAAAIC6S3gHAKAO+OSTT9K5c+fKxw0bNkySTJ8+vXJZeXl5evTokfvuu+97rw8AAAAAAKCuEt4BAKgDWrdunWnTplU+btWqVZLkrbfeqtJu1qxZmThx4vdaGwAAAAAAQF0mvAMAUAcsv/zyefvttysfr7HGGimKIiNGjKhcNnXq1IwaNSrLLbdcKUoEAAAAAACokxqUugAAABa9Xr165YILLsj777+fpZZaKttuu22aNPl/7N15vJZz/j/w12nXLkuWKIpJGMY2hDC2sY8xyhj7ngpJ2pSsSdbIILuyNtbsIjExdrIvY0+2lJS2c+7fH37n/p6UGYO6W57Px6NHdV/XffW+ruv+fM7p+rzO51MvvXv3zoQJE7Lqqqvm2muvzcSJE7PPPvuUulwAAAAAAIAlhvAOAMASYO+9984LL7yQF198MTvuuGOaNGmS8847L0cddVTOO++8JEmhUEiLFi1yyimnlLhaAAAAAACAJYfwDgDAEmDjjTfOQw89NMdrhx9+eDbccMPceuutmThxYtZaa60cfPDBadSoUYmqBAAAAAAAWPII7wAALME22GCDbLDBBqUuAwAAAAAAYIlVrdQFAAAw/62++ur54x//WOoyAAAAAAAA+AHhHQCAJcBnn32WJk2alLoMAAAAAAAAfkB4BwBgCdC8efN88803pS4DAAAAAACAHxDeAQBYAvzlL3/JmDFj8sUXX5S6FAAAAAAAAKoQ3gEAWAL06tUra621VnbYYYeMHTu21OUAAAAAAADw/9UodQEAAMx/u+yyS6pXr56XXnopW265ZZZffvm0aNEiSy211Fz7lpWVZdSoUSWoEgAAAAAAYMkjvAMAsAQYPXp08c+FQiGfffZZPvvss3nuW1ZWtoCqAgAAAAAAQHgHAGAJ8Oijj5a6BAAAAAAAAOZBeAcAYAmw1VZblboEAAAAAAAA5qFaqQsAAAAAAAAAAIAllZl3AACWIIVCIffdd1/Gjh2bL774Ir///e9zyCGHJEm++OKLfP3112nZsmWqV69e4koBAAAAAACWDMI7AABLiJdeeikdOnTI22+/nUKhkLKyssyaNasY3nnooYey//7754477shuu+1W4moBAAAAAACWDJbNAgBYAnz88cfZbrvt8tZbb2WnnXbK2WefnUKhMMc+f/rTn1KzZs3ceeedJaoSAAAAAABgySO8AwCwBDjzzDPz1Vdf5YILLsjIkSNzwgknzLVP3bp1s9566+WZZ54pQYUAAAAAAABLJuEdAIAlwP3335/WrVvnmGOO+Y/7tWjRIp9++ukCqgoAAAAAAIAapS4AAID5b/z48dljjz3+635lZWX55ptvFkBFAABAkpSVnfMjW2YW/1S//oVJav3oMQqFuWfWBAAAYNFh5h0AgCVAvXr18sUXX/zX/d577700adJkAVQEAAAAAABAIrwDALBEWHfddfPcc8/lyy+//NF9Pvjgg7z00kvZcMMNF2BlAAAAAAAASzbhHQCAJcB+++2XKVOm5LDDDsu0adPm2j5z5swcffTRmTVrVvbbb78SVAgAAAAAALBkqlHqAgAAmP8OPvjgDB8+PHfddVdat26dP/7xj0mSl156Kcccc0zuuuuufPjhh9luu+3SoUOHElcLAAAAAACw5DDzDgDAEqB69eq5++6789e//jWffPJJrrjiiiTJCy+8kIsvvjgffvhh9tprr9x2220lrhQAAAAAAGDJYuYdAIAlRP369TN8+PD07ds39957b/7973+noqIiq6yySnbaaaesv/76pS4RAAAAAABgiSO8AwCwhGndunVat25d6jIAAAAAAACIZbMAAJYId999dyoqKkpdBgAAAAAAAD8gvAMAsATYY489ssoqq6RHjx55/fXXS10OAAAAAAAA/5/wDgDAEmCDDTbIp59+mkGDBmWdddZJ27ZtM3To0HzzzTelLg0AAAAAAGCJJrwDALAEePbZZ/Pyyy/nuOOOy7LLLpunnnoqRx11VFZcccUccMABeeSRR37R8QcMGJCNN944DRo0yPLLL58//elPefPNN3+l6gEAAAAAABZfwjsAAEuIddZZJ+edd14++eST3Hbbbdl1110za9asDBs2LNtvv31WW221nHrqqfnggw/+52M/9thj6dSpU5566qk89NBDmTVrVnbYYYdMnTp1PpwJAAAAAADA4kN4BwBgCVOjRo386U9/yp133plPPvkk55xzTtq0aZMPPvggp5xySlq1avU/H/P+++/PQQcdlLXXXjvrrbderrnmmnz44Yd57rnn5sMZAAAAAAAALD6EdwAAlmDLLbdcjj/++Dz99NM59thjUygUUlFR8YuPO3ny5CRJkyZNfnSfGTNm5JtvvpnjFwAAAAAAwJJGeAcAYAn21FNP5cgjj8xKK62UwYMHJ/nPgZufoqKiIscdd1w233zzrLPOOj+634ABA9KoUaPir1VWWeUX/bsAAAAAAACLIuEdAIAlzKeffpqBAwdmrbXWyuabb56hQ4dmypQp2WGHHXLTTTflk08++UXH79SpU1555ZXcdNNN/3G/Xr16ZfLkycVfH3300S/6dwEAAAAAABZFNUpdAAAA89/MmTNzxx135JprrslDDz2UioqKFAqFtGzZMgcddFAOOuigrLzyyr/43+ncuXNGjhyZMWPGpFmzZv9x39q1a6d27dq/+N8EAAAAAABYlAnvAAAsAVZcccVMmjQphUIhdevWzV/+8pcccsghadeu3a9y/EKhkC5duuT222/P6NGjs9pqq/0qxwUAAAAAAFjcCe8AACwBvv7662y22WY55JBD0qFDh9SvX/9XPX6nTp1yww035M4770yDBg0yYcKEJEmjRo2y1FJL/ar/FgAAAAAAwOJEeAcAYAnw+uuv5ze/+c1/3Oerr77Kddddl6uuuirjxo37n47/97//PUmy9dZbz/H61VdfnYMOOuh/OhYAAAAAAMCSRHgHAGAJ8GPBnUKhkPvvvz9XXnllRo4cmVmzZv2s4xcKhV9SHgAAAAAAwBJLeAcAYAn03nvv5aqrrso111yT8ePHF8M3G2ywQQ444IASVwcAAAAAALDkEN4BAFhCzJgxIyNGjMiVV16ZMWPGpFAopFAopKysLCeeeGIOOOCAtGnTptRlAgAAAAAALFGEdwAAFnPPPfdcrrzyytx0002ZPHlyCoVCatSokZ133jkvv/xyPvjgg5x11lmlLhMAAAAAAGCJJLwDALAY+vrrrzNs2LBceeWVGTduXJKkUCikdevWOeSQQ3LAAQdk+eWXz5ZbbpkPPvigxNUCAAAAAAAsuYR3AAAWQyuuuGJmzZqVQqGQ+vXrp0OHDjnkkEOy2Wablbo0AAAAAAAAqhDeAQBYDM2cOTNlZWVp1qxZrr/++my11ValLgkAAAAAAIB5qFbqAgAA+PWtu+66KRQK+fjjj/OHP/wh66+/fgYPHpyvvvqq1KUBAAAAAABQhfAOAMBi6KWXXsrTTz+dI444Ig0aNMjLL7+crl27ZuWVV06HDh3ywAMPpFAolLpMAAAgSfJNko9/8OuTKts/mcf2j///+wAAAFjUWTYLAGAxtdFGG2WjjTbK+eefn1tuuSVXXnllnnjiidx6660ZMWJEVl555Xz33XelLhMAAMhTSR76D9sv+ZHXt0+yw69fDgAAAAuU8A4AwGJuqaWWyoEHHpgDDzwwb7/9dq688spcd911+fjjj5MkZWVl2XzzzXPggQemQ4cOadSoUYkrBgCAJc2mSdr8jPc1/LULAQAAoAQsmwUAsARZY401ctZZZ+Wjjz7KHXfckV133TXVqlXLk08+mY4dO2bFFVfMPvvsU+oyAQBgCdMwSbOf8Ut4BwAAYHEgvAMAsASqXr16dt9999x111356KOPcsYZZ6Rly5aZPn16br311lKXBwAAAAAAsMQQ3gEAWMKtsMIK6dWrV9566608+uij2W+//UpdEgAAAAAAwBKjRqkLAABg4bHVVltlq622KnUZAAAAAAAASwwz7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAAAAAAAAAAQIkI7wAA8KsYM2ZMdtttt6y00kopKyvLHXfcUeqSAAAAAAAAFnrCOwAA/CqmTp2a9dZbL0OGDCl1KQAAAAAAAIuMGqUuAACAxcNOO+2UnXbaqdRlAAAAAAAALFLMvAMAAAAAAAAAACVi5h0AAEpixowZmTFjRvHv33zzTQmrAQAAAAAAKA0z7wAAUBIDBgxIo0aNir9WWWWVUpcEAAAAAACwwAnvAABQEr169crkyZOLvz766KNSlwQAAAAAALDAWTYLAICSqF27dmrXrl3qMgAAAAAAAEpKeAcAgF/Ft99+m3feeaf49/feey8vvvhimjRpklVXXbWElQEAAAAAACy8hHcAAPhVPPvss9lmm22Kfz/++OOTJAceeGCuueaaElUFAAAAAACwcBPeAQDgV7H11lunUCiUugwAAAAAAIBFSrVSFwAAAAAAAAAAAEsq4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAAAAAAACgR4R0AAAAA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4us/rFmABxZ+//jHP1JRUZFJkyalfv36Sb5fHuiHqlevXgwlCPD8MoVCIbNmzUqnTp3y3XffZdVVV80mm2ySZN7XXoBn/ps2bVpOPfXUVFRUpF69etl4442TJLNmzZpr3/kd4BHeAQAAAABgvqhXr1422WSTTJ06NYMGDUry/YDuvAYnBHiAJVGLFi2y2WabZerUqRk8eHBmzZo1x1JZVS0MAZ4DDzww1113XWbNmpWDDjooN910U5I5wzqVBHhg4XbxxRfnmGOOyZtvvpk//elPeeONN4rLA/1Q1VCCAM/PV1ZWlpo1a+bxxx/PGmuskeuuuy49evTIzJkz51imrCoBnvmrbt26ueCCC7LDDjvkrrvuymGHHZYkqVmz5jzvx/wM8AjvAAAAAADwqysUCmnZsmWuvfba7Lzzzrn55pvTvn37JP83CPFDAjzAkqS8vDyNGjXKvffem+233z533HFH9t5770yfPv1H+7tSBngqByP322+/XHrppSkvL8/hhx+em2++OclPD/Ast9xy6datWy644IL5Wi/w4yr7kQsuuCCdOnXKuHHj0r59+7z11ls/Gj6oVq3aPJfQ+tvf/pbHH398gda/KJs9e3ZWXHHFPPbYY2nRokUGDRqUk046KeXl5T/a9wvwzD+FQiHrrLNOLrzwwvzhD3/IVVddlUMOOSRJfvR+zK8AT1lhXnPvAQAAAADAL1QoFFJWVpb33nsvXbp0yb333pu99967ONA7e/bs1KhRY673VVRUpFq1apk0aVKOPfbYXH/99Wnbtm1Gjx6dGjVqFI8LsKirHKydPHly2rdvn4ceeih77LFHbrzxxtSpU6e4/cfelyQHHHBAhg0bls022yw33XRTVllllflWb2X/nCTXX399jjzyyFSvXj1Dhw7NPvvskyTz7KOrvjZmzJjsu+++GT9+fC6++OIcffTR861e4MdV7UeOOeaYXHzxxVlnnXVy66235je/+c0c7b2qqq+fe+656d69exo3bpyRI0embdu2C/QcFlWV3wNPmDAhbdu2zfvvv58TTjghAwYMKAZG5tX3V75v+vTp2WGHHfLEE09k7733zqWXXpqll166BGeyeKj8GvXGG2+kc+fOeeSRR3LQQQflqquuSpIfvR+VbWH8+PHp3r17brzxxmy55ZZ57LHHflYdZt4BAAAAAGC+qJxxYbXVVstFF12UnXfeObfeems6dOiQ5KfNwHPppZfmyCOPzNixY/PnP/9ZcAdYrFQO0jZq1Ci33HJLtt9++9x5553561//+pNn4LnuuutyxBFH5Mknn8yRRx6Zb775Zr7VW3U2gf333z+XXXZZcQae/7aEVuX72rVrl9/97ndZZpllMmHChHz77bfzrV7gx1XtRwYPHpzOnTvnlVdeyd57750333zzP87AM2vWrCRJt27dstFGG6WsrCxPPPFEZs6cuUDPYVFV+T3wCiuskLFjx6ZFixY555xz0qtXr/86A8+MGTNSp06djBkzJo0aNcqzzz6bhx9+eK5+l5+u8utW69atc/HFF+cPf/hDrrnmmp80A8/MmTOz0kor5dprr82qq66axx9/PNdcc83PqkN4BwAAAACA+aZywPbnBHiSZKmllsrMmTPTpEmTtGjRIl9++eUCrR9gfvslAZ6qll566fz2t7/N9OnT52u9PzfAU9mvd+nSJffcc086duyYI444IvXr15+v9QI/7ucGeGrWrJkkOfTQQ/Pss8+mU6dO+etf/5patWot0PoXZT83wFO7du0kyRFHHJHJkydnn332Sdu2bYXbf6GqAZ4hQ4b85ABP5Wf+8MMPz4QJE9KnT59ss802P68Gy2YBAAAAADC//dwltM4+++z07Nkzffr0SceOHbPSSist6NIBFojK5Tf+1yW0/v73v6dTp07p2bNnOnfuPN/6yaozn/2wz/5fltAaMWJExo0blyOPPFKfDiVStW3+sJ3+1CW0Zs+enX79+mXGjBk54YQTsuKKKy7Qc1hU/fB6V17b/2UJrffeey8HHnhgNtxww5x44omu/S/wY7N6/i9LaE2bNi2bb755tthii/Tp0ycrrLDCz6pFeAcAAAAAgF9d1QfhkydPTqNGjYrb/pcAz7Rp0zJs2LDssssuWXnllRfcCQDMZ1X7ySlTpqRBgwbFbf9LgOezzz7LqFGjsvXWWy+Q4M4TTzyR8ePHZ9ttt80yyyxT3GdeAZ55DfgnyYwZM4qzRwALVtX2/Oyzz+a7777L73//+9SoUaPYXucV4Jk5c+ZcM+uUl5fnu+++M4PWT1T12r/99ttZeumls+yyyxb79h8L8MyaNas421Hy/XUfP3586tWrlyZNmpTqdBZ5Ve/Hq6++moqKiqy77rrF7W+++WY6deo0V4BnXm1h+vTp+fbbb7Psssv+7HqEdwAAAAAA+FVVfRD+yCOP5IEHHshuu+2WLbbYojiQ+2MBHoAlQdV+8qGHHsqoUaOy1157ZeONNy4GGX8swPPfjjc/a33kkUfSpUuXTJs2LU899VSaNm06x/aqAZ5LL700f/vb35IkY8aMyZZbbmlZFyixebXnZZddNnfeeWcaN248R0CwaoDnxhtvzNprr50k+fe//53VV1+9ZOewqKp67R9++OEcf/zx2XHHHTNgwIDiElo1atSYK8Bz6qmn/mjfz8/3w/tx0kknZc0118zf//731K1bN8n3S2n92Aw8yffh2aZNm/5qNc0ddQUAAAAAgJ/ph4NC3bp1y0UXXVR8sF35E92rrbZaLrroouy888659dZbs/feexeP8fe//z3jxo1b8MUDLAA/7Cd79OiRs88+O0svvXSSpEaNGikUCmnUqFFuueWWbL/99rnzzjvToUOHVP5M/nnnnZcPPvigeMwFEdwZNWpUevbsmQ8//DDDhw9P06ZNM3PmzMyYMaO4//7775/LLrss5eXl6dixY+6555506tQpW2+9dZ555pn5UiPw0/yw7+nVq1fee++9DBgwII0bN87s2bPn6EsGDx6czp0755VXXsm+++6bzz//PEceeWS22WabfPjhh6U6jUXSD699375989prr+XPf/5zcdbJGjVqpKKiIiussELGjh2bFi1a5Jxzzskpp5ySJDnggAPSvn37kp3D4uSH9+Okk07KM888k86dO6devXpzbG/dunUuvvji/OEPf8g111yTww8/PEly+OGHp2vXrvnyyy9/tbrmXkAYAAAAAAB+hh8+CO/Zs2feeeedPPLII1ljjTUybdq0TJo0qbisS2WAp1Ao5B//+EcOPPDANGjQIJdcckmuv/76tGnTZq7lYQAWZfPqJ99+++08+eSTadWqVaZNm5YpU6YUA4+VAZ727dvn7rvvzl//+tc0aNAgV155ZRo3bpyDDjponstSzY9ae/XqlVdeeSWjR4/OJptskkmTJuW2227LzJkzi3VVq1Yt+++/f5KkY8eO2W233VK/fv0cf/zxWWWVVeZLncB/N6++54ft+eGHH069evWy3XbbpVq1aqlevXoGDx6csrKyXHTRRVl99dUze/bsHHXUUVlqqaVKfEaLjh+79mPHji1e+3HjxqVVq1ZZccUVM3v27GKAZ/PNN8/AgQNz7733Zty4cenUqVMmTZqUxo0bl/akFmE/dj+efPLJbLLJJpk8eXL+9a9/pXHjxtlkk01SKBSKAZ7OnTvnyiuvzKhRo/L++++na9eucyxn9kuZeQcAAAAAgF/sxx6EP/zww9l0000zadKkXHXVVenevXteeumlJElFRUVWW221DBkyJLvsskuuv/76DBs2LD169Mg222wjuAMsVuY1i01lP/n73/8+kyZNyuWXX55u3brltddeS5KUl5cXAzw77rhjbrnlltx+++3p1atXdtxxxwUW3Kms9dFHHy0Obg4fPjyHHXZYHnjggTRq1CjVqlUrzgy0//77Z/DgwWnRokU6d+6cbt26ZcUVV5wvtQL/2U9pz8OGDUv79u1z1113pWbNmqlevXrKy8uTJBdeeGGOOOKI1K1bNyeccEJ69OiR5ZZbrpSntMj4Kdf+2muvzdZbb50RI0YkSXEJrRVWWCH//Oc/s8IKK2TcuHHp27dv+vTpI7jzC/yU+3HNNdekffv2ue+++5J8P7NdZYBnyJAhadu2bcrLy9O7d++ccMIJadSo0a9WX1mh8qsoAAAAAAD8DP/pQfjvf//74qDQ8ccfn0033TSPPfbYXO9955130rdv36y55po54ogjsvLKK5fqdAB+dT+1nzzhhBOy3nrr5amnniq+t7y8PNWrV8+kSZNyxBFHpGXLluncufN86yd/GDKqnHGnaq3XX399evfunQ033DCPPvroHO+rqKgohopefPHFLLfccvp0KJGf0vdcf/316dOnT9Zff/05vkdL/q//SZKRI0dm/fXXT7NmzRb4eSyKfuq179u3b9Zaa62MHTt2jvfPnj07NWrUyBdffJHrrrsuf/nLX9K8efNSnMpi4afej5NOOilt2rSZ635Uvv/NN9/MP//5z+ywww6/elsQ3gEAAAAA4Gf7qQ/Ce/XqlY022qg4yFt1cLfyzxMnTkxFRUWWXXbZkp0PwK/t1+gnKwfQZ8yYkSlTpsy3fvJ/qXWDDTYoDvRXDjLP6zhAafxa7blqgIef5te+9vrUX+bX/tr2w9d/LcI7AAAAAAD8LL/Wg3CAxdWv2U8uyMHbylpfffXVPPLII/p0WIT5Hq10XPuFy6hRo9K7d++MGzduobwfwjsAAAAAAPwio0aNSs+ePfP6669n1KhRC92DcIBSqxwwfOWVVxb6MMwzzzyTI444Im+//bY+HRZxY8aMSdeuXfPmm2/mkUceySabbKI9LyD33XdfTj755Lzxxht56KGH9KUlNmbMmHTr1i2vvvpqRo8evVC2BZ8AAAAAAAB+tvfeey9nnXVWXnnllYwePdrABMAPfPDBB+nTp0/GjRu30A4YVtWsWbPUrVs3Dz/8sD4dFnGFQiETJkzIqFGjFvq+Z3Hzyiuv5Nlnn82TTz6pL10IvPTSSxk/fnweffTRhbYtmHkHAAAAAIBf5NRTT82uu+6aDTbYIJMnT86wYcPSs2fPheZBOECpnXPOOWnXrt1CMWBYUVGRatWqzXNbZQ0zZ85MrVq1MmnSpAwfPlyfDgupH2vP5eXlqV69epJk0qRJady4cbE99+rVK7/73e+051/ox5YyrHrtP/jggzRv3jyTJ0/O8OHD06NHD33pfPJT7sdHH32UVVZZpfh1uHfv3gtVWxDeAQAAAADgR/2nQd6qD8OT5Jtvvsl1112Xnj17ZqONNsro0aOTlP5BOMD89J/6ycoQTKWqA4YbbLDBAu8nq/bbL7/8ct5///0UCoW0bNky66yzzhz7VatWLbfeemv233//bLbZZvp0WMj8MCQyYcKE1KxZM2uuuWbq169f7JvKy8tTKBRyzTXXpEuXLvn973+vPf9CVa/9d999l6+//joNGzZM/fr1kyQzZsxI7dq1UygUMmPGjFx44YU5/fTTs/HGG+eRRx5J4tr/mqrejy+++CJTp07N7Nmz06pVq7n2nT59eoYNG5bjjjsuG2644UIT3EksmwUAAAAAwI+o+iD8vvvuy4svvpjPP/88rVq1ypFHHll8wF05OPTGG2/knHPOKcmANEApVO0n77333rzwwgv54osv0rx583Tu3HmO4E6SvPvuuznppJPyu9/9boH3kxUVFcVa+/fvn0suuSRffvllkqRmzZo566yzcvjhh6d+/frF/TbddNMccMABGTp06AKtFfjPqrbnM888M1deeWXee++9JMmaa66ZoUOHZvPNN0+S4n4rrLBCtt1224wcOTKJ9vxzVe33L7rooowYMSL/+te/suaaa2bLLbfMkCFDUrt27SRJWVlZ6tSpk88//zzNmzcX3JkPqt6P888/PzfddFNee+211KpVK3vttVe6d++eli1bFkO2derUyQcffJCtt956oWsLZt4BAAAAAGAuVWeS6NWrVy644ILMmDEjNWrUyOzZs7PNNtvk5JNPTtu2bed42H3VVVflkEMOSbLwPAgHmB+q9pM9e/bM4MGDM3369GI/ueGGG+byyy/PeuutN8fMPMOHD8/f/va3JAuun6y6nEiPHj0yaNCg7LrrrjnooINSu3btDBo0KGPGjMlJJ52UY489Nssss8xcS5Do02HhULVtnnDCCTn//POz+eabp0OHDvn6669z2WWXZebMmRk8eHD+9Kc/FYMkVWnPP0/Vfr9bt2658MIL06pVq2y55ZZ5++23M2bMmGy33Xa5/vrr07Rp03nOzOba/3rm1RZat26dP/zhD5kwYULuvvvubLPNNjnxxBOz1VZbzXOWvIXpfgjvAAAAAADwo0466aQMGDAg+++/f4466qist956Oe+889K3b99sueWW6dOnT/7whz/M9dD7h0tqASxOqg4Y9u3bN2eccUYOOuigHHbYYWnbtm369++fU089Nb/73e9ywQUXZPPNN18oBnCvuOKK9OjRI3/729/SpUuXrLHGGikvL0/r1q3z73//O4VCISeeeGK6d++eZZZZZq5zBRYeQ4YMSe/evXPQQQelY8eOad26db755pust956+eCDD9K4ceNcdNFF+ctf/lIM8GjPv45Bgwbl5JNPzmGHHZYjjjgi66yzTt59991suumm+eqrr7LlllvmpptuyoorrjjH98T/aZlFfr7K/5scfvjhOeyww7LOOuvk3//+d9q2bZvPP/88bdu2zWmnnVYM8FS2g4WtPfhkAAAAAACQ5PsBnapGjBiRq666KkcccUROOumkbLrppqlRo0aGDRuWhg0b5l//+ld69eqVRx55JLNnz57jGII7wOKscrBvxIgRufrqq3P44YenT58+adu2bSoqKnLzzTencePGeeWVV3LUUUdl7NixKS8vn+MY8yu48+9//7u4HFZVn376aYYNG5YNNtgghx9+eNZYY41Mnjw566yzTr799tucfPLJ2XLLLXP22Wdn8ODB+eKLL+Y4V2DB++yzzzJjxoy5Xn/nnXdy/fXXp127djniiCPSunXrTJo0KZtuummmT5+eLl26pEGDBjn22GNz++2357vvvkuiPf8vZs6cOc/Xn3322Vx33XXZfffdc/TRR2edddbJpEmTsscee6R69erZfvvt8/jjj2e//fbLp59+Osf3xII7P9/kyZPn+jqaJC+//HKGDRuWHXbYIR07dsw666yTKVOmZJdddkm1atWy77775vnnn0+/fv0yevTolJeXF9vBwtYefDoAAAAAAJZgzz33XO67777MnDlzjgfY3377be6+++4ss8wyOfzww9OqVatMmTIlv/3tbzNp0qQMHDgwffv2zcsvv5zTTz89Dz/8cGbPnr3QPQQH+KWeeeaZjB49OhUVFXO8PnXq1Nx8881Zeuml07Fjx7Rs2TLffvtt1l577WI/2aNHj7z++uvp2rVrxo4dO9cxfm2vv/56WrVqleOOOy5TpkyZY1udOnXy73//O/vuu2/WXXfdTJs2LTvssEMmTpyYQYMGpV+/funRo0eS5LTTTsuFF144zxAQsGA8//zzWWONNXLZZZcVQ9KVpk2blqeffjqHHHJI1l577UybNi3bbrttvvrqq5x33nkZOHBgjjrqqEycODFdu3bNnXfeOc8QEPM2ZsyYbLXVVnn//ffn2vb222/n1VdfzVFHHZXWrVtn6tSpadeuXb788stcfPHFufzyy7P11lvn0UcfzQEHHJAJEyYs+BNYzIwdOzbbbrtt7r333rm+jr799tt55513cswxx+Q3v/lNpk6dmi222CITJ07MueeemzPOOCP77rtv/vnPf+bss8/OmDFj5hkCWhgI7wAAAAAALKE+++yz7LLLLmnfvn1effXVObbVr18/DRo0yNFHH50NNtgg3333XXbbbbd8+eWXGTBgQI488sgceeSRWX311fPEE0/kjDPOyH333bfQPgwH+DkmTJiQ3XffPdtvv31ef/31ObbVq1cva6yxRg488MCsv/76+e6777Lrrrvmyy+/zJlnnpnDDz88ffv2zVprrZXnnnsuxxxzTEaPHj3XLGe/phkzZmSLLbbId999N9fMPksvvXRefvnlHHzwwamoqMjJJ5+cV199Nd27d8+f//znJMmOO+6YTTbZJK1bt86ZZ56ZSy+9dL7WC/y4Tz75JDVq1MjTTz89Vzv87W9/m1deeSV77rlnZs+ena5du+btt99Onz59sscee6ROnTrZb7/90qxZsxQKhey777655557SnQmi5by8vKMHDky//rXv3LZZZfNde07dOiQkSNHZuutt87MmTNz8MEH5+OPP07//v2zyy67pHnz5unSpUtq166d0aNHZ5tttsnnn39eorNZ9JWXl2fcuHF5/vnn8/jjj891P/baa69cf/312WabbTJz5swcddRRef/999O/f//stddead68efbYY48kyaOPPpojjjgiTz/9dClO5b8qK/iKCwAAAACwRPr2229z1VVX5aWXXsr555+fhg0bpqKiYo4p/WfPnp0aNWpk8ODB6dWrV0444YT07NkzSy21VJLkmGOOyVNPPZVnn302f/jDH3LPPfekdu3apTolgF/VtGnTctFFF+Xtt9/Oeeedl4YNG/7ovhdffHF69OiRE044Ib179y72hUcffXSeffbZvPDCC9lss83y8MMPp1atWvOt5vfffz/LL7986tatm5EjR2aTTTbJ8ssvP8c+hUIhbdu2zdSpUzN27NjUr18/STJ9+vQ0b9487du3z6xZs9K7d++suuqq861W4D979tln85vf/CYNGjTIc889l7XXXjt16tSZY59vvvkmm2yySVZcccU8+OCDqVmzZpLvwz9t2rTJgQcemDfeeCNXXXVVmjVrVorTWOR8+eWXue+++7LbbrulcePG+eabb+b4Prny91dffTVbbbVVdtxxx1x99dXFvv3hhx9Ohw4d0q5duzzzzDN57rnn0rRp0xKf1aLr66+/zrPPPptNNtkkjRo1yrvvvpsWLVrMtUzvv//972y11VbZZJNN8o9//KP4+rhx47LLLrvkz3/+cx566KGMGjUqK6ywwoI+jf/KzDsAAAAAAEuo+vXr54gjjsill16ahg0b5vzzz89dd92VWbNmFfepnLnh6aefTu3atXPYYYcVgztJ8sQTT+Q3v/lNbr755lx77bWCO8Bio1AopG7duunatWsuueSSNGzYMIMGDcqDDz44z+Wvnn766dSsWTOHHHLIHH3hc889l1atWuWqq67KjTfeON+CO5U/r9+iRYvUrVs3w4YNy+67754zzzwzX3311Rz7fvXVV3nzzTfTtGnTYnAnSa655prUrVs3nTp1yiWXXJJVV13VjGpQApV9zEYbbZQGDRrkggsuyMYbb5wbb7xxruWvPvroo7z11ltZe+21i8GdJLnpppuy4oorpkePHhk5cmSaNWumPf8EFRUVWXbZZbP//vuncePG6dGjRzbccMN8/PHHxYB75e9vvfVWJk6cmD333HOOvv3+++/P2muvncsvvzyvvvpqmjZtOt+XTVycLb300tl+++3TqFGj9O/fP9tuu20effTR4ue58uvfa6+9lk8++SQbb7zxHO8fMWJE6tevn65du+aZZ57JCiussFDeD+EdAAAAAIAlVEVFRerUqZOaNWvmueeeS7du3dK7d++MGjWqGOCpfBg+efLkOX5PkuHDh2fixInZY489svfee2fllVc2KAQsNsrKylJeXp5atWqlVq1aefrpp9OjR4907Ngxjz/++FwDf7Nnz57j9yQZNmxYxo8fnz333DP777//fO0nq9ZTUVGRDTfcMHvuuWcuvvjinH766fnyyy+L2xs1apQddtgho0aNyvnnn5933303Q4YMyQUXXJBlllkmK6ywQnFw+oczGwDz3w8Xz1l99dXTpk2bHH/88XMFeFZeeeVsuOGGuf766zN27NhMnDgx1157bYYOHZqmTZumcePGxWCJ9vzfVb32M2fOzJQpU/Luu++mQ4cO+fjjj+fYZ6WVVkq1atUydOjQ4ntuueWW3HnnnVlxxRXTpEmTNGrUKIVCYY6ZLfnpfvg1s2HDhpk4cWJ69+6d0aNHp7y8PGVlZUmSddddN02bNs2DDz6Y8ePH57vvvsuNN96Ym266Ka1bt86KK66YunXrLrT3w7JZAAAAAABLoPLy8uIAzoQJE7LCCivk6quvTp8+fdKkSZMMGjQo2223XfEnuK+88socffTRadu2bQ499NC89tpruemmm1KrVq089thjlgIAFjs/7CeXX375XHHFFTnllFNSt27dDB06NO3atSsOAF5yySXp3Llz1ltvvfTu3TvPP/98br311tSoUWO+95OFQqE4eHnmmWemRo0aOf744/P666/n9NNPz6233ppjjz02ffr0ybLLLpskeeCBB4r7VGrVqlUeeuihNG/efK5lFIEFo2rbGzJkSJo1a5Y99tgj99xzT0466aS88847ueiii/LXv/61OMvXBRdckJNOOimzZ89OgwYNMmnSpKyyyip59NFH07x58zn6CH5c1Wt/zz33ZOONN85SSy2VM844I4MGDcrGG2+cESNGFJcfmzJlSv7617/m3nvvTZs2bdKoUaOMGzcuTZo0yeOPP55VVlmllKezyKt6P6677rpsuOGGad26da688sr07Nkzq622Ws4+++xsvfXWqV69er7++uucfPLJufjii7Pmmmumdu3aee+997LMMsvksccey6qrrrpQtwVfcQEAAAAAljCFQqE4IN2jR4/st99+efvtt7PPPvvkzDPPzBdffJHu3bvn4YcfLs7As/POO6dz58559dVXc8ABB+Sss85Kw4YN88ADD1gKAFjsVO0nTzjhhBx++OF5++23c8ABB6Rv376ZMmVKDj/88IwZM6Y4K8ABBxyQzp0758MPP0yHDh0ycODA1KtXb4H0k5UDkQMHDsxJJ52Uzz77LF9//XXWXXfd9O7dO3vvvXcuvPDCnH766fn888+TJDvuuGOuuuqqDB48OIceemjOPvvsjBkzJs2bN095ebngDpRIZdvr169funTpkmeeeSZTp07NLrvsklNPPTWtWrVKly5dcuONN2batGlJkuOOOy5Dhw7NgQcemPXXXz+dO3fOE088UWzPC2tYYWFTee27deuW3XbbLXfeeWcaNGiQE088Md26dcvTTz+dv/zlL8UZeBo0aJBrrrkmRxxxRCoqKjJhwoRst912xeCOGSl/mcr7ceKJJ+bwww/PiBEjkiTt27fPgAED8t577+XEE0/M6NGjM3v27Cy99NI54YQTctZZZ6V27dqZNWtWdtpppzz++OPFZSAX5rZg5h0AAAAAgCXU0KFD07Fjx3Ts2DHHH398VltttXz77bcZMWJEevTokeWWWy5nn312tt1229SuXTtff/11xo8fXxwM2mijjbLsssvOMTsFwKKu6k/lX3755Tn66KNz5JFH5sQTT0zz5s0zbdq0XH/99Tn55JPToEGDDB06NG3btk2tWrXy3Xff5Y033sjLL7+cFVdcMRtuuGGWWWaZ+dZPVj3urFmzsvnmm6dVq1Y544wzstpqqxX3e+mll3LmmWfm1ltvzTHHHJNevXr96ExA+nQojcq2VygU8sknn2T77bdP27Zt07dv37Ro0aK43913351+/foVZ+DZe++9U69eveL22bNnp1q1aqlWrZr2/BNVvU6PP/54/vrXv2bnnXdOz549s/rqqydJJk6cmLPOOivnnHNONtlkkzlm4Jk1a1amTJmS8vLyNGjQIHXq1HHtf4Gq1+7ZZ59Nhw4dsuOOO6Z79+7Fr22TJ0/OTTfdlF69emW11VbLwIEDs9VWWxVnDZ01a1bxODVr1lwk7ofwDgAAAADAEuKHS6Dsu++++eyzz3LFFVdktdVWKw5YzyvAs91226VWrVr/9ZgAi7If9mkHH3xw3n333Vx11VVp1apVcfu8Ajybb755cdDwPx1zfjj77LPTtm3bdO3aNQMGDMh2222XQqGQQqFQ/Ld/GODp06dPlltuuflaF/C/GzFiRNZbb73stNNOGTZsWDbddNMkc/YlPwzwdOjQIUsttVQpy14svPnmm/n8889z4IEH5p577slaa62V5P+u/X8K8FS1MC/NtCh544038vHHH6d9+/Z5+OGHs8EGG8yxfV4Bnm222WahD+n8mBqlLgAAAAAAgAWjcsCne/fuWWqppfL5559n3333zWqrrTbHgFD9+vXzl7/8Jcn3y2qdeOKJOeecc7LtttvONTAtuAMsTir7tM6dO6esrCxff/11DjjggLRq1aoYhCkUCqlbt27233//JMnJJ5+cww8/PFdccUW22GKLuQYN53c/+cADD6Rnz56pU6dO6tWrl4YNGyb5fimtsrKy4iDyeuutl969eydJhgwZkilTpmTQoEFp0qTJfK0P+Omuu+66HHTQQWnWrFnq1q2bNddcs7itsv8pKyvLbrvtluT7pbWOP/74fPfddzn00EPnGbTmpzn55JNz2mmn5Xe/+11WX331YnAn+b9r36RJk/Ts2TNJcu6552afffbJ8OHD07x58zmOJbjzy51//vnp1q1b2rVrl4022qgY3KkajGrUqFH22WefJEmvXr3Sp0+f9O/fPzvuuOMi+X+URa9iAAAAAAB+tnfeeSe33XZbTj/99DzyyCP58ssvk8w9uFwZ4Bk4cGAmTZqUgw8+OE888UQpSgZYoD744IPccccdGTJkSO69995MmDBhju2VgZjKAM8pp5yS6dOn509/+lOefvrpBV7vDjvskG7duqV+/fqZPHlyXnvttSTfLztStd4kWW+99dKnT59su+22uf/++xfJwU1YnG255ZbZeeed8/XXX+fTTz/Nq6++mmTe7Xm33XbL6aefngYNGuSCCy7IrFmzSlb34mCNNdZIo0aN8tprr+Xrr7/Od999l+T7WXeS/7v2lQGe7t27Z+zYsencuXNxH349q6yySjbYYIM8+eSTefHFFzNu3LgkcwejKgM8AwcOzDPPPJPBgwcvsm3BV2QAAAAAgCVIq1atMmTIkGyzzTapVq1aXnjhhUycOHGe+1YGePr06ZOmTZumdevWC7hagAWvefPmGT58eDbeeOPMnj0748aNS0VFRcrKyuYaQK8M8Bx//PFp0aLFXLMvzG+zZ89OWVlZzj777Bx88MEpLy9Pr1698uabb6Z69erzHPD/7W9/m/POOy8vv/xyGjduXHwdKK2Kioqsttpqufjii7P11ltnypQpOe2005Ik1atXz+zZs5PM2Z532WWXXHHFFXnsscdSr169ktW+ONhvv/1yxRVXpFatWnnhhRdyySWXJPk+4D6vAE/37t1z6qmnZsiQIYKQv6LKa/2Xv/wlffv2zYYbbpgvv/wyt912249+vWrUqFH23nvvXHfddRk6dGhq1669IEv+1ZQVfEUGAAAAAFgsVZ1WPskcS2Pdd999Oe200/LUU0/lzDPPLC4BMC9Tp05NktSrVy/l5eVzLQkDsKj6T/3kY489luOOOy4vvfRSevfundNPP32ufSrf/91336WiomK+9pNV/9151V8oFNKrV6+cffbZadasWf75z39mlVVWmaOe/3S+wILz39re+++/n2OOOSYjR47Mn/70p9x2221Jvg/s1ahRI8nc7dn3aD/Nf7v2t912Ww444IBMmzYtV1xxRQ455JC53lf558p74Nr/fP/tftx5553p379/XnrppVx66aU54ogjfnTfRf1+CO8AAAAAACyGqj60njlzZqZPn56Kioo0bty4uM/999+ffv365dlnn815552X4447rjTFApRA1X5y9uzZ+fbbb1OtWrU0bNiwuM9jjz2WY445JuPGjUv//v3Tr1+/JPMO8Pzwz/Or1n/84x954YUX8t5776VJkyY5/PDDs+qqqxZn0endu3cGDhyYlVdeOWPHjp0rwAOUVtX2+Mgjj+TNN9/Ml19+meWXXz777rtvateunVq1av2kAA//m6rX/o033sj48eNTKBSyzDLLZP311y/uN2LEiBx66KGZMWNGLrnkknkGePjlqt6PF154IR988EG++eabtGnTJuuss07q1KmT5PsAT79+/TJu3Lj/GuBZlAnvAAAAAAAsZqo+CL/00kvz4IMP5tVXX02tWrXyt7/9Ldtvv3023HDDJMkDDzyQvn375tlnn825556brl27lrJ0gAWiaj/597//PQ888EBeeOGF1K9fP3vuuWf23XfftGnTJkkyZsyYdOnSZa4Az4IKxFQdLO7evXsGDx6c6tWrp169evnqq6/SqFGjdOvWLXvttVfWWmutFAqF9OnTJ2eddVaaNWuWsWPHplmzZgI8sBCo2p579uyZiy++ONOmTStuX3/99XPCCSdkp512ytJLL50PP/wwXbp0yd133z1HgEd7/t9Vvfb9+/fP5ZdfngkTJhS3H3fccTnqqKOy5pprJvk+KHnIIYfMFeCZXyHNJU3V+9G3b99cdtll+fLLL4vb99133xx88MHZdtttkyQjR45Mnz59FusAj1gYAAAAAMBipFAoFAdzunXrls6dO+fFF19My5YtU7t27Zx00kk54YQTct111yVJdtxxx5xxxhnZaKON0q1btwwePLiU5QPMdz/sJyuDORtuuGGWWmqpDBo0KMccc0xuuOGGFAqFtGvXLpdccknWXXfd9O/fv7h81oIaOK8c3BwwYEDOPffcHHLIIRk7dmy++OKLPPDAA1lttdXSr1+/3HnnnZk1a1bKyspyxhlnpGfPnvn444+zxhprZPz48Qb6YSFQ2Z5POeWUnH322Wnfvn3Gjh2bCRMm5NJLL8348ePTuXPnjBw5MuXl5Vl11VVz0UUXZbfddssdd9xRDDJoz/+7qqGpU089NZtuumlGjBiRf/zjH9l7770zePDgdOnSJS+++GKSZK+99spVV12V2rVr59hjj82QIUOSRHDnV1J5P/r06ZMzzjgjW221Ve6999489NBDOf7443PrrbemS5cuefDBB5Mku+66a84444ysu+666dSpUy688MJSlj9fmE8LAAAAAGAxUjmgMGTIkFx44YU5+uijc/TRR6d169b5/PPP06dPn1x55ZVZfvnl0759+9SpUyfbb799kuTkk0/Occcdl7p16+awww4r5WkAzDeV/eQll1ySwYMHp2PHjunYsWPatGmTDz/8MKeffnquuOKKrLDCCtlrr71Su3btbL755hkyZEiOPfbY9OvXL/Xq1VugM5W99dZbufbaa7PtttumW7duadWqVZJk4sSJmTBhQpo1a5YjjjgiNWvWLC6pc8YZZ+Tbb7/NzTffHAtxwMLj6aefzhVXXJE99tgjvXv3Lrbn+vXrZ+bMmWnQoEF22223VK9ePRUVFcUAz6RJk/LUU0/lyy+/zLLLLlvis1g0jRw5MpdddlkOOuig9OnTJy1btkySTJgwIbfeemveeuut4v1Ivg/wVKtWLXvttVfOPffcHHTQQalXr16pyl/sPPzww/n73/+efffdNyeffHLWWGONVFRU5Isvvkh5eXmSZNNNNy3uv+uuu6asrCxHHnlkzj777Bx22GGL1f0w8w4AAAAAwGKkUChk8uTJ+cc//pG11lornTt3TuvWrVNRUZFHH300o0aNSvPmzXPJJZekTp06mTlzZpJk++23T+/evbPTTjsVwzwAi6NCoZCpU6fm1ltvzVprrZUuXbqkTZs2KS8vz9NPP50HHnggzZs3zwUXXJDatWsXBxC32GKLnHPOOdluu+2y5557LtCax48fn7feeivt27dPq1atMnv27Nx0003p0aNHateuneeffz5NmjTJrFmzikvwlJWV5cILL8xbb72VlVdeuXgeQGn9+9//zieffJIDDzxwjvbcu3fvLL300nnuuefSuHHjzJo1K7Nnz06SrLrqqrnhhhvy/vvvZ9lll01FRUWJz2LR9Nxzz2X69Ok59NBD07Jly8yaNSs333xzBg4cmJYtW+aZZ55J/fr1M2vWrOJ79txzz4wcOTJjxoxZrIIiC4MXX3wx3333XTp27Jg11lgjs2bNyq233poePXqkefPmGTNmTBo2bJhZs2Zl6tSpSZJddtkl11xzTZ577rnF7n4I7wAAAAAALEbKysry9ddf5+mnn87WW2+dNddcMzNnzsytt96aE088MYVCIc8880yWWWaZJMknn3ySyZMnJ/n+p1lvu+22NG/evDhYBLC4KSsry1dffZWxY8dm2223zZprrpkZM2bk1ltvTbdu3VKtWrU888wzxQHy9957r/jebbbZJiNHjkyLFi3mWz9Zedyqs+V8+eWXSZIWLVokSW655Zb06NEjZWVlefrpp4uzcHzyySfZZZdd8uGHHxbPtVGjRnMsFQYsOJWhuUKhUAzcvPHGG0mS9ddfP8mc7fmpp54qtufXX389Xbp0KQZJVl555Sy33HKpqKgoLjnEj6vah5aXl6dQKOSpp57KCiuskM033zwVFRUZMWJETjzxxJSVlWXs2LHFaz927NgMHz68+P6dd945zZo1E4L8BX54P5LkySefTP369bPhhhtm5syZxftRrVq1udrCTTfdlBkzZiRJtttuu6ywwgqL3f3QqgEAAAAAFiE/XPqk6k8GV6pWrVrKyspSp06dJMltt91WfBBedZB3ypQp2XHHHXPXXXcV31u7du0kSY0aNebXKQDMV/+tnywUCllqqaVSs2bN4r533nlnevToMVc/OXPmzLRr1y6XX3558f21atVK8uv0k1Vnz6icCa3yuE899VRx23LLLZckueGGGzJs2LD06tWrWGvltiQ599xz89xzz+Xzzz+f49+pXCoMmH/mNRtOZWjus88+KwZuVl999STJiBEjcvvtt/9oex40aFBuuummYhivkuDO3H547SsqKor9XmV4saysLCuuuGK++OKLPP7447nrrrvSs2fPeV77/v37p3///pk0adIcxxWC/Gl+eD/Ky8uL92PGjBnF67jeeutl8uTJefrpp/Poo4/+6P3o1q1bBg4cmG+//XaO4y5u90PLBgAAAABYRBQKheKD748//jhJUrNmzSTJ0KFDM3HixCTfB3BWWWWVDB8+POecc05xQPpf//rXHA/CzznnnEyYMCENGjRYwGcCMH9U7SfHjx+f5P/6yXPPPTfTp09PWVlZKioqsuKKK+aOO+7ImWeemRNOOGGufrJQKGTAgAGZOnXqHH3nr6lyEH6//fbL8OHDiwOeRx11VDbffPM8/fTTSZItt9wym266aa699tp069YtZWVleeWVV+aoddiwYbnzzjuz1157pU2bNvOlXuDHVbbnHj16ZPTo0cXXDznkkKy55pr57LPPkny/BF/Dhg1z9tlnp3PnzqlWrVpefPHFOfqZq666Ko8++mj+9re/pVmzZgv0PBZFldf+5ptvzsSJE4t/P/roo9OhQ4diUHPbbbfNtGnTcsopp+TYY48tzrRW9doPGTIkr7/+evbbbz/fI/9Mldf/oosuymuvvVYM2Rx88ME54IADMn369CTJWmutldmzZ6dTp0459NBDU7169bn+vzJkyJC89tpr+fOf/5yGDRsu+JNZgIR3AAAAAAAWEZUD0ttvv326dOlSXMrl2GOPzZFHHpm77747SdK0adPsv//+mTBhQvr375+Kioq88cYbWX755ZN8/9OwN998c6677rq0a9cu22yzTWlOCOBXVtlP7rDDDunSpUtxxorjjz8+3bt3z9ChQ5N8308ef/zx+fDDD3PqqaemUCjk5ZdfnqOfvPXWWzNs2LBsscUW+cMf/jDfan788cdz22235fTTT8/DDz+co48+OkOHDs2xxx6bFVdcMcn3A6EDBw7MmmuumS+++CKHHHJI6tWrVzzG0KFDc+qpp2appZbKwIEDU7du3blmIALmvxtvvDGDBg3KwIED8/bbb+eYY47Jddddl/32268Yzlt99dVz9tlnZ9q0afn0009zyimnpFGjRsVjXHvttRk4cGAaN26cvn37pnbt2trzT3DiiSfmr3/9a66++uok339/fOmll2aVVVYpBtx333337L333nnkkUfy1Vdf5ZZbbinOtJZ8P7vZhRdemFVWWSUdO3Zc7GZ2WZDOPPPMHHvssbnssssyadKknHDCCbn22muzzDLLZNq0aUmSvffeOwcddFBeffXVfP3117nmmmuKX4eT79vT4MGDs8IKK6Rr167FMO7iyrynAAAAAACLkK+++ipLLbVU7r777jRr1iyTJk3K8OHD071792y99dbF/U444YS8+eabue6667LJJpvk448/TosWLZIk559/fv7+97+nUCjk0ksvTaNGjVJRUWEZBmCR9cMZdxo0aFDsJ7/++usMGzYsPXr0yO677158T/v27fPSSy/l8ssvT5s2bfLuu+9mnXXWSXl5eS6++OJccsklKS8vz2WXXTZf+8nf/e53uemmm9K9e/e0b98+33zzTY477rj07t17jkHlDTbYIP369Uvfvn3Tv3//3Hfffdloo43y0ksv5YUXXkjTpk3z8MMPZ6WVVkp5eblBZyiBdu3aZeDAgenfv3+23377fPjhhznhhBNywgknzBFK2G233TJhwoQMGDAgPXv2zOjRo/Pb3/42o0ePzmOPPZbGjRvngQceyIorrqg9/xeVffO2226bl19+Ob169cptt92WJ598Mt27d8+xxx6bZZZZJknSoEGDHH744Zk8eXJGjRqVIUOGZKuttkrr1q0zbNiw/OMf/0itWrXy0EMPZfnll/f98S/Qvn37vP7667n44oszevTojBs3Lr17907Hjh3TpEmT4ue6f//+mTJlSv7xj3/kxBNPzAEHHJDll18+9957b+6+++4stdRSefDBB9O0adPF/n6UFcT0AAAAAAAWalOnTi3OsFAoFPLVV1/l5JNPzqWXXppCoZBDDjkkZ599dpo0aTLHAPYHH3yQk08+Odddd12qV6+eddddN1999VU+++yzrLnmmrnrrrvSokULg0LAIu/bb79N/fr1i33gBx98kPPPPz8XXXRRCoVCDj300Jx++ulzDf69+uqrGTx4cIYOHVrsJydOnJjPP/88a6yxRu666640b978V+0nR40alWHDhhVnh6i066675t57702jRo0yYMCAHHXUUUkyR73Tp0/PO++8k969e+f555/PhAkTsu6662arrbZKjx49DPTDAvboo4/m/fffz8EHH1x8bdasWdlyyy3z9NNPp3nz5hk8eHB22223JJmjfX755ZcZNWpUevbsmQkTJmTGjBlZbbXVsuWWW+aMM87IyiuvrD3/B88//3zq16+fNddcs/jauHHjsvPOO2f8+PHZYostcvnll+c3v/lNkjmv/eOPP54rr7wy1113XfG9jRs3zmabbVacrce1/98888wzqVu3btZee+3ia1OnTs0666yTjz/+OOuuu24uuuiibL755knm/No2YcKEnHrqqbnsssuSfP//neWXXz6bbbZZLrroojRr1myJuB9m3gEAAAAAWIj985//zN/+9rfccMMNadu2bcrKyrLsssumZs2axUHqqVOn5ttvv02TJk1SVlZWfL158+a55ppr0rZt24waNSpvvvlmNtxww7Rr1y5/+9vfstxyyy0RD8KBxdvjjz+eP/7xj7n//vuz5ZZbJkmaN2+e2bNnF/vDadOmZfbs2Um+X4KqctBw7bXXzpAhQ7LNNtvkxhtvzMcff5zf/e532WqrreZLPzl9+vScddZZGTVqVDbaaKN06tQpFRUVefPNN/PNN99kjz32yHPPPZeBAwemSZMm2XXXXYtLYJWVlaVOnTpZZ511ctddd2XixImZOHFiWrZsmdmzZ6dmzZr6dFiAJk6cmIMOOigfffRRmjdvXlxe78knn8zEiRPTtm3bPPvssxkyZEiWX375bLLJJqlevXqx/1l22WXToUOHbLPNNpk0aVI+/fTTrLvuuqlbt27q1KmjPf8H7777bjbaaKM0bNgwr7/+enGJwWeeeSaff/55Vl555fzzn//MPffckxVXXDENGzac49pvueWW2XLLLXPQQQfl008/zTfffJNNN900q6++eho0aODa/48++uijbL311pk1a1befffdrLLKKkm+D6tOnjw5zZs3z4svvphbbrklTZs2TatWrVKtWrXi17YVVlghl1xySfbff/9MmjQpX3zxRTbeeOOsuuqqqVev3hJzP8y8AwAAAACwEDvnnHNy4okn5ogjjsgll1ySJJkyZUoGDRqUmTNn5v3338+IESNy8MEHp3v37mndunWS739itaKiYo4H3TNnzkytWrWKf1/cp54Hlgznn39+unXrln322SfDhg1LRUVFZs6cmZNOOinJ9z/Rf/PNN+eQQw7JiSeemDXWWCPJ3H3gvPrE+dFPjhs3Lrfffns6d+6cJk2aFF9//fXX06BBg7z44os55phjUlFRkUGDBmXXXXfNUkstVRy8rFrT7NmzU6NGjTlmXQMWnOHDh+fxxx/PgAEDsvTSSxdf/+c//5nGjRvnvvvuS9++fbP55pvnjDPOyO9///sk/9d2q7bnqu1Ym/7vOnTokO+++y7Dhw9PgwYNknw/6+Tjjz+eunXr5rLLLsuoUaNy+umnp1OnTsV9Kq/9j3Ht/3fTp09Pnz598umnn+bSSy9Nw4YNkyTvv/9+Xn/99Sy33HK58MILM3z48Bx11FHp1q1bWrZsmST/NZizJN0P4R0AAAAAgIXYrFmzirNJNG7cOB9//HGaNWuWb775JmVlZZk9e3a6deuWa665Zq4AT/L9A+/y8vLUqFFjngO/AIuDkSNHZosttkjjxo3z0UcfZZVVVsm3336bJJk0aVJOOeWUXHXVVTnkkEPSo0ePtGrVqvjeQqGQQqGQatWqFUOO86ufrByErPy9c+fO+eijj3LnnXcW9/nuu+9yzz335MQTTywGeHbeeefUq1cvhUIhd911V2bPnp299trrV68P+GmqBgoqwyBdu3ZNixYtcuyxxxb3+/zzz3PFFVfktNNOyxZbbJHTTz89G220UfH7sYcffjgtW7YsBhn476qGPb777rsstdRSueCCC7Lttttm3XXXLfbfY8aMySmnnJLHHntsrgBPoVDIiy++mPXWW8/3xL9Q5fWeNWtWKioqUrt27QwaNCjbb7991l9//eL2Dz/8MD169MjNN9+cjh075vjjjy9+7ivvxxprrJH69euX+IxKxycRAAAAAGAhVVFRkZo1a2a33XZL48aN069fv7Rs2TLPPvtsGjZsmAYNGmTppZfOaaedloMOOihXX311Bg0alNdee614jDvuuCPHHXdcvv766+JAh0EKYHFRXl6eJNl1113TuHHj9O7dO82bN8+//vWv1K9fP/Xr10+zZs1y4okn5tBDD81VV12VgQMH5q233ioe4/bbb8++++6bGTNmFGcnm1/9ZEVFRZKkrKwsX331VcaOHZu77747hx56aHGfpZZaKrvuumsGDRqUatWqpXv37rn77rszceLEPPDAA+nWrVv222+/fPvtt/Ez+lAalQHqJKlRo0Zef/31XHLJJenatWuuvvrq4n7LL798DjvssPTr1y9PPPFE+vTpk2eeeSYVFRV56KGHcvDBB+ePf/xjcZk//rvq1asXr/1SSy2VO++8M8cff3z23XffvP3228X+u127dunfv3+23nrrnHTSSbn44oszderUJMn999+fffbZJ0cddVTJzmNxUa1atZSXl6dmzZqpXbt2Ro8enR49emSvvfbKa6+9Vrwfq666agYOHJgOHTrk73//e84999y8//77SZJ77703O++8c7p167ZEt4Mfnw8KAAAAAICS+uHD69q1a6dGjRrZe++9M2LEiGy44YZJkpVXXjmnn356kuTqq6/OrFmzctRRR2XChAnp27dvXn/99fTt23eB1w+woNWvXz9lZWXZfvvtM2rUqGy88cZJkjXWWCMnnnhiCoVCrrrqqpSXl+eQQw7JhAkTctppp2XcuHE577zzstJKK8232qouZfjiiy9m/fXXzy233JJOnTrl6quvTkVFRXHQv06dOtlll11SVlaWnj175qijjkrLli0zfvz41KxZM6+88soSPTsBlFpFRUVx6aXx48dnrbXWyogRI3LMMcfk0EMPTaFQyCGHHJLk+wDPoYcemrKyspx++uk57LDDsvrqq+fll19OtWrVct999/3HZZyYU+WMkpX22GOPdOzYMX//+9/zl7/8JbfeemvWXHPNJMmWW26Zk08+OWVlZTn55JPz0UcfpV69ehk5cmQmT56cnj17luo0FhtVZ0KaPHlyNt9885x++uk566yz8qc//Sm333571l577ST/F+ApKyvLpZdemvfffz+rr756Hn744ZSXl6d79+5LzBJZ82LZLAAAAACAhVDVJVtuuumm/O53v8saa6yRCy+8MP3790+jRo1y++23FwM8SfLpp5+mf//+GTp0aDHos+yyy+aRRx7JaqutZrksYLFStU8bNmxYtt566zRr1iwXXnhhTjzxxNSoUSOjR48uBniS5N133825556bSy+9NNWrV0+tWrWy7LLLZvTo0Qusn+zcuXOuvfba3Hbbbdl+++3z5ptvpnPnzhk1alQOPPDAOWbtmDFjRv71r3+lf//+ef/999OmTZtccsklWXXVVYtL9QCl07Fjxzz11FMZPnx42rRpkzvvvDNdunTJxx9/nCuuuKIY4EmSr776Krfffnt69OiRQqGQNm3aZPjw4WnevLn2/BNVXa6sY8eOqVatWoYMGZLk+771kksuybrrrjtHgCdJxo4dm8GDB+eWW25JtWrV0qZNm9x1111p0aKFa/8LVL0fxx9/fL766qsMGDAgyyyzTM4555yceeaZWXnllecI8CTf/5/ltNNOy6WXXpo6derkN7/5Te64444lvi0I7wAAAAAALMR69OiRQYMG5dxzz03Xrl0za9asDB48OKeeeuo8AzzTp0/PZZddllGjRqV58+bp2bNnVl555Tl+KhZgcdK7d++cddZZOfvss3PCCSckSc4///z07NlzngGeL774IiNHjsztt9+eVq1apVu3bvO1n6waCLrjjjty5JFHZqeddspJJ52UVq1aJcl/DPBU+uyzz9KoUaPUqVNHnw4lUjWscM0116Rr167Zeeedc/rpp2e11VZLkv8Y4EmSL7/8MhMnTsxKK62U+vXra88/w/nnn59u3bpl9913z5AhQ7Lyyisn+c8BnilTpuTZZ5/N1KlT07Zt2zRp0sS1/wWqtoUhQ4akS5cuOfTQQ9OvX7+sssoqmT59es4999wfDfAkyZgxY1KrVq2sueaa7keEdwAAAAAAFipVH1q//PLL2X333bPjjjumR48eWX311ZMks2fPzoUXXvijAZ7KfapVq5Zq1aot8Q/CgcVL1T7t9ddfz0477ZQ//vGPOf744+cYqJ1XgKdyWKysrCwzZ85MjRo15ms/WXVwc+LEibn33nszcODA3HHHHWnZsmUKhUIKhUKqVav2owGeGTNmpHbt2vM8JrDg/HBmrgEDBuTuu+/O9ddfn5YtW86x/ccCPLNmzUrNmjWLx9Cef5of9tH77rtvZs6cmYEDB6Zly5ZzzNbSpUuXDBkyZI4Az7yusxkpf74fXrv9998/n332WS655JK0atWqeL9+LMAzr2vvfiRL5nxDAAAAAAALqcqBibFjxxYHlbt06VIM7pSXl6dGjRo59thjkySnnnpq9txzz2KAp/LBd9Xp5gV3gMVJZZ82evTozJgxI9WqVcsxxxxTDO5UDhp27do1SdKzZ89svfXWxQBP5SBvrVq15jrmr61ysLhLly654447suyyy+a3v/1tMbhTVlaWsrKyFAqF/OY3v8mQIUPSqVOnXHvttalWrVquvPLKOYI7VY8JLFiVwYIuXbrktddey4QJE7LHHnukZcuWxe2V7XqPPfZIWVlZunTpksMOOyxlZWU5+OCD5wjuJNrzT1XZR59++umZPn16HnrooZx77rnFa1+jRo1i33/RRRelUCjkkksuyd57750RI0ZkjTXWmCsAtKQHRX6JymvXvXv3zJo1K2+//XY6duxYnE2uevXqqaioSJ06ddKtW7ckyZlnnpk999wzd9xxR9q0afOjx1ySuQIAAAAAAAuZs88+O1tssUUOO+ywNGnSJOuss05xW+XD8MoAT79+/TJ58uS0b98+Tz31lAffwBJhwIAB+cMf/pB+/fqlQYMGWWuttVJeXp7k//rJJOnatWvOOuuszJ49O9tvv30xGLmg1apVK5988kneeeedYm1J5pgJqFAoZM0118yQIUOy44475uqrry4GkICFxz//+c88+uij+eKLL4phkFmzZiX5v7acJLvvvnsuuuiirL766jn00ENz0003lazmxcG7776bfv365corr8xSSy2VFVZYIcn/Xfvq1asXvw5cfPHF6dSpU8aNG5dtttkm//73v4XZf2XvvfdeLrzwwgwdOjSvv/56pk2bluT72T+T78M4VQM8vXv3zmeffZYtttgib731VilLX2j5XxwAAAAAwEKmbdu2WXXVVfPGG29k0qRJ+eKLL5KkOCBR+TC8MsDTv3//vPfeeznmmGOKAxgAi7N99tknTZs2zTPPPJNvvvkmkydPnmPgtrKfTL4P8Jx99tn55ptvcuCBB2bWrFnFwfX5oWo4p9K5556bU089NVOnTs3NN9+cBx54YI5Zd5I5AzznnntuOnTokGOOOWa+1Qn8d/Nqz88//3x23HHHfPnllxk2bFjGjx+fmjVrFvf9YYBnwIAB2XjjjbPpppsu0NoXdT/sp1u2bJkHH3wwSfLxxx/nxhtvTJI5rn3VrwMXXXRR9t9//0yfPj1169ZdgJUvnn54P1ZbbbXcd999WW655TJlypT861//SvL9LEiV9+OHAZ5OnTplmWWWSf369Rd4/YuCssL8/O4EAAAAAID/qHKZqx96+umns88+++T999/PUUcdlUsuuSRJ5pjyv/K9s2bNylVXXZVddtklzZo1W6D1A8xvP9ZPfvTRR2nXrl0++OCDHHDAAbnqqqtSrVq1efaTSTJ06ND88Y9/zCqrrDLfaq36b//rX/9KoVCYY8B+wIAB6dOnT1q1apWrr746m2++eZIUl9qp+ufKY1Uu8wUsWFXb8+uvv54mTZqkadOmxe277LJL7rvvvuy2224ZOnRoll9++Tn6nKrtevr06alTp472/BNVvY4TJkwozrKTJI899ljat2+fL774IhdccEEx5Fj1PVXv3ddff52ll156rmWz+OmqXrtPPvkkTZo0yVJLLZUkGTNmTP7617/m008/Tf/+/dOvX78kc96Pyj/PmDEjM2bMSMOGDd2PeRDeAQAAAAAokaoPre+555688847ad++fZo2bZpq1arl2WefTfv27fP++++nb9++OeWUU+Z63w8HtT0IBxYnVfu0kSNH5uWXX85xxx1XnEXh448/zuabb56PPvoovXr1yumnnz5H8OWHx5jX338tVfvj008/PUOHDs1HH32UV199NWussUZxwP7UU09N//7989vf/jZDhgyZZ4AHKK2q/cTZZ5+doUOHZsaMGXnhhRfSsGHD1KxZM0myww475OGHH06HDh1y4YUX/scADz9N1Ws/ZMiQDB8+PC1atMgNN9xQ3OfRRx/NXnvtldq1a+eUU07JEUcckeTHAzzuw89X9ToOHjw4V199dX73u9/lvPPOS6NGjVJWVpbHH388++yzTz799NMMGDAgPXr0SJIfbQvux7xZNgsAAAAAoAQqKiqKD8JPOumkHHLIIenatWvGjRuX8vLyFAqFbLTRRrn55pvTokWLnHbaacWfZP3h0jBVCe4Ai4sf9pOHH354TjrppDzwwANJvh9QbNasWZ544omsvPLKGTBgQE466aQUCoVUr159jmVUqpof/WShUCj2xyeccEJOPfXUtGvXLvfdd1/WWmutOZYR6devX04++eS8/PLL6dSpU/75z38miYFMWEhU9iHJ9+25b9++WW+99XLOOedkmWWWSc2aNTN79uwkyYMPPphtt902N998c4455ph8/vnncyzbp13/b6r2+927d0+PHj1Ss2bNbLPNNnPst8022+TWW2/NjBkz0q9fv1x++eVJ5lwysWpf7z78PD9sC7169UqjRo2yzTbbpHHjxsUl4rbccsvccMMNWWmllfL/2LvvwJru/4/jz5sdkZAQM5LYq1rULEqtGjVqFlV7b2IFsfferb2J2Zq1YyulLS2t1dq1R4ise8/vD797v7m2lgR9Pf7BPecen3NOzvvcnM/rfj49e/Zk+PDhALbR8MD+HOh8PJlG3hERERERERERERFJQMHBwYwYMYKmTZvStGlT8uXLZ1tm/VbqwYMHqV279mMj8DxtKhkRkXdJz549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ERERkQRgfRh+6NAhmjZtyi+//EKhQoXo0KED3t7ebNy4keXLlwOwa9cu/P397UbkERH5r9i1axfdu3fnhx9+IH/+/LRu3ZoUKVKwadMmW53cvXv3G1Mnr1y5wqJFiwgJCeH+/fskS5aMmJgYIiIiyJo1K2vWrCEwMNB2HxCRN9dvv/3G2LFjmTVrFgA5c+bk3r17XLx4kcDAQDZt2qTr+TWIjIxk8eLFjBw5kt9//51MmTJRrFgxTp06xc8//4y3tzdhYWEEBga+EXX/v2DPnj20adOGI0eOkCpVKrJnz86tW7c4duwYqVOnZvv27boW/iWFd0REREREREREREQS2PHjx+nYsSObN2+2vZYoUSLy5s3L/PnzCQgI0INwEfnPMpvN/PTTT4waNYqlS5faXk+cODF58uRh3rx5b2Sd/PXXX5k2bRoXL14kUaJEFC5cmJo1a+Lr6/vGtVVEni46OpqlS5eyaNEizp49i5+fH4ULF6ZVq1akTJlS1/MrZh19Jyoqil9++YUpU6bw3XffcefOHbJmzUqRIkUYMGAAadKk0bGPZzdv3qRbt2788ssv/P7777z33nsUKFCAHj16kDp1ap2Pf0nhHREREREREREREZE3xKpVq/j777+5ffs2+fPnJ1++fCRNmlQPwkVE/t93333HjRs3uHbtGgULFiR37txvZJ181kgQGiVC5O1ksVgwDMOu1rxptedd8egUZFeuXOHBgwekTZsWwzBwcXHRsY9nce9d0dHRXL58GX9/f2JjY3F2dtb5eAUU3hERERERERERERFJYM962K1OXhGRxzty43qT62Tcdj9rH0TkzWexWDCZTJhMJl3P8cRa3x893jr+CSPucX/auZF/TuEdEREREREREREREREREREREZEE8mbGkEVERERERERERETecvrepIjI28lisbzS7T16P9D9QST+vOrrWV7c0469zknC0Pl482nkHREREREREREREZGX8OjQ8HGnvDIMA8Mw/tX0LY9u/02eDkZE5EU8OjWgxWLBMIynThf4PK9r+pRH27VlyxZOnTpFREQEqVKlolq1ari4uLzUNCFx17tx4wbJkiX71+0UkeczDAOLxWK7ng8dOsSlS5cwDIN06dKRJ08eu3Vf9nqOiIggUaJEr6fxbzHrMYpb9y9fvszNmzfx9PQkefLk/+i4PWkKQk3X9HzW8xD3fJw4cYI7d+4QExND7ty5//X5sNLvLP+ewjsiIiIiIiIiIiIiLyHuQ3CTyWR7SD1v3jz27t3L33//jbe3Nw0bNiRLliykTp36hbcd90H4iRMnyJw5szolROStExkZiZub22NhmDlz5rB//37Onz9P0qRJadKkCe+99x4pUqQAXqwDPe46J0+eJFWqVHh6ev7jtu7cuZMjR47Qtm1bu9e7devG6NGj7UbJKVq0KE2aNKFWrVq4u7s/t71xl2/evJmlS5dSs2ZNypYt+4/bKyJPd+DAASIiIihRooTd9RccHMyECROIiIiwrdusWTPq1q1L8eLFgefXn7jLt23bxrZt22jQoAGZM2d+jXv09jh58iReXl6kTJmS2NhYnJycABgwYABz5szhr7/+wsnJiUyZMjFgwAA+/vhjW+1/nrjHft++fVy6dImKFSvi5ub22vbnbXf06FEyZsxIokSJiI6OxsXFBYB+/foxffp0Ll++DEDWrFlp0KABX3zxBYGBgS99Hw4LC+PYsWO0bt369e7Qf4TCOyIiIiIiIiIiIiIvaMuWLQwfPpzQ0FB8fHxsr3fp0oWxY8fi5OSEm5sb9+7dw9PTk0qVKtG9e3dy5cr13G3HfRC+adMmBg8eTJEiRRgyZMhr2x8RkVdt06ZNjBs3jvHjx9t1alvrpIuLC4kTJ+bmzZu4ublRp04dWrVqRb58+Z677bh1cuPGjYwePZqCBQvSv3//l/62v2EYhIeH4+vrS0xMDJMmTbJ1Po4YMYK+fftSq1Yt6tati6OjI4sXL+a7777D0dGRbt260aZNm2d2HD8a3OnSpQvnzp3j0KFDZMyY8aXaKiLPZhgG58+fJzAwkLRp07JgwQJbKGfAgAEMHDiQzz77jBo1avD333+zevVq9uzZQ548eejduzdVqlR57vbjXs+dO3fmxo0b7N+/H39//9e+f2+6n3/+mbx589K4cWMGDx5MypQpgYehqWHDhlGgQAGKFSvGH3/8webNmzGZTLRu3ZrWrVuTIUOGZ2770c/HHTt2xNPTkw0bNth9Fpf/OXLkCLlz56ZEiRKsXbvWNrJOnz59GDx4MB999BEVKlTgl19+4fDhw5w+fZoqVaowbNgwsmbN+sxtP3o+unfvzs2bN/nhhx9IlSrVa9+3d54hIiIiIiIiIiIiIs9lsViM6tWrGyaTyShXrpxx48YNwzAMY+bMmUaSJEmMjh07Gr/88osRHh5uLF682ChXrpxhMpmMMmXKGL/99ttzt221adMmI3fu3IaHh4dx9OjR17pPIiKvWt26dQ2TyWR89tlnxqlTpwzDMIzZs2cbXl5eRseOHY3Dhw8bUVFRxrx584wKFSrY1v3pp5+eud1H62SePHkMV1fX59bX5wkLCzM8PDwMd3d3Y8KECYZhGEbNmjWNqlWrGmfPnrWtd+PGDWPp0qWGv7+/4efnZ3z77beGYRiG2Wx+obYmSZLkufsoIv9Ov379DJPJZOTIkcPYunWrYRiGUahQIaNBgwZ21/OJEyeM/v37G87OzkaRIkWMH3/88anbfPR6zps3r+Hl5aXrOY6rV68a+fPnN5ycnIy2bdsaly9fNi5fvmwEBgYa7dq1M/766y/buqGhocYnn3xiODs7G7169TLCw8Ofut1Hj/2HH35oJE6c2Dh8+PBr3Z+33d27d42PP/7YMJlMRqVKlYzw8HDj5s2bRoYMGYz27dsbf/75p2EYhhEREWH8+OOPRuXKlQ2TyWR88cUXxoULF5663SddC4kTJzZ+/vnn171L/xkK74iIiIiIiIiIiIi8oMjISKNWrVqGyWQySpUqZcTGxhojR440PvzwQ+PMmTO29SwWi3HlyhWjXr16hslkMtq0afPUzokndfJ6eXnpQbiIvLXq169vCzpeuXLFGDNmjFGwYEFbh6HVyZMnjSZNmhgmk8no3LmzER0d/cJhmFdRJ63/1+7duw0XFxfD1dXV6N+/v5EpUyZjwYIFtv/b+v9HRkYas2bNMhInTmxUqVLlidt8Uls9PT1V00Veo7h1Y9iwYYbJZDKyZ89uzJ4920ifPr2xbds2wzAMIzY21rbejRs3jG7duhkODg5G3759n7hdXc8v7urVq8Ynn3ximEwmo0OHDsaSJUsMPz8/WxA9JibGtu6uXbuMggULGp6ensbOnTsNw7A/1o/+O+6x/+WXX+Jhb95e1p/xu3fvGuXLlzdMJpNRrVo1Y+/evUaWLFlsobO418zVq1eNTz/91HB3dzeWLFliGIbOR0LRtFkiIiIiIiIiIiIiLyA2NhYnJyeioqKoW7cuq1atonjx4sTGxlK0aFGGDh0K2A8n/+eff/LVV1/xxx9/cODAAQIDA+22aTwyDUP37t05deoUu3bt4oMPPojX/RMR+besdRKgbt26LFmyhDJlyvD3339TvHhxJkyYAIDFYrFNc3X06FFatmzJL7/8wqFDhx6bsuN110lrW/bs2UPJkiXx8vLCwcGBkSNH8tVXXxETE4Ozs7Nt/cuXL1OnTh127tzJoUOHyJMnT7y1VUSeLm5dGT58OD179iRr1qzcvn2b5cuXU6RIEcxmM46Ojrb3HD58mKpVqxIeHs7x48ftpv3R9fzyrl27Ru3atdmxYweffPIJV65cYd++fbi7u9vOjfWYfv3117Ru3Zry5cuzZs0au6kPdez/HevPeXh4OLVr1+b7778nS5Ys3Lt3jx9++IG0adM+9p5NmzZRuXJlihYtapvazErnI/683ASgIiIiIiIiIiIiIv9RTk5OmM1mXF1dWbRoEVWrVmX37t0cOnSIc+fOYbFYiImJsXvYnS5dOsqVK8f169dZt26d3fb0IFxE3jVOTk7ExsYCsGjRImrXrs3mzZs5f/48kZGRwMOAT9xO2ly5cvHZZ58RERFBWFiY3fZeZ520WCzA/zqSixQpwpYtW7h79y7Xrl1jy5YtADg7O9vWNZvNpE6dmqpVqwJw//59u21at7Vp0yZ69Oihmi4STwzDwMHBAbPZDED37t0ZPHgwf/zxB1euXOHQoUMAODo6Endci7x581KqVCnCw8OJiIiw26Y+o708X19fQkND+fjjj9m2bRsnTpzg1KlTtsCUyWSyHf9GjRqRLVs2Lly4YDtvoM/Hr4KjoyOxsbF4enoSGhpKmTJlOHHiBHfu3OHUqVMAtnu1VbFixciUKRNnzpzh2rVrdst0PuKPwjsiIiIiIiIiIiIiz2HtVLB2Pri6urJ48WIqVapEZGQku3fv5vLlyzg7O9vWtY5AUaFCBdu/47I+CN+4cSPBwcF6EC4ibzVr7bOOvAOwePFi6taty+3bt1myZAlHjx7FycnJ1nkbExMDwGeffQbAvXv37LZprZPff/89PXv2fGV10trRD3DhwgXb68WKFWPnzp24urqyYMEChg0bBoCDgwPR0dG2e8Bvv/2Gs7MzSZMmfWzbe/fupWPHjpw8eZLdu3erpou8ZhaLxVYr4taQnj17MmbMGACCgoL4/vvvgYd1Je5nsps3b+Lj44Obm9tj2w4LC6NDhw6cOXNGn9GewBpshP/dA6wBnooVKxITE0PXrl25cOGCLbjz6KRAcUc2A/ugSJcuXXTsX0LcEJT1Xuzp6cny5cspX7489+/fp2PHjkRERNjCttZz6O7ujpubG15eXk+8Fr7//nu6dOnCn3/+qfPxGim8IyIiIiIiIiIiIvIc1g7bXbt22TqbXV1dWbJkCTVq1OD8+fNUq1aNmzdv4ujoSExMjG2knrVr1wKQOnXqx7b722+/ERQUxNGjR9XJKyJvNWud3Lt3L3fv3rW9vmDBAho0aMC9e/do2bIlJ0+exGQyER0dbRvVZvXq1QCPTS0I8Ouvv9KnTx+OHDnyyuqktXO4R48efPHFF+zfv9+2rGDBgmzfvh0XFxd69epFv379AHBxcQFg5cqVbNiwgTx58uDv72+3XYvFwsWLF/Hw8GDHjh28//77/7qtIvJs1iBecHAw3bp149y5c7ZlHTt2ZPTo0cTGxtK6dWtbrbEGG1atWsXu3bvJnTs33t7edtuNjIzk559/5v79+2zfvl2f0Z7AeuzHjBnDt99+S3R0NAApUqRg5syZlC1blq1btzJgwADOnTuHyWSyvWfVqlWcOXOG999/3240NoDr16+zePFizp49S1hYmI79C7LehwcMGMCIESNsQSlPT0+WLFlCxYoV+eWXX2xTxTk5OdmO/bJlyzh69CjvvffeY+Gd8PBwduzYwe+//65r4TUzGY/G20RERERERERERETkMUOHDqVXr16MGDGCjh072jp+oqOjqVevHitWrCBnzpwsWbIEPz8/kiRJwuLFi+nfvz8mk4ldu3aRPHlyu22eOHGCMWPG0LJlS3Lnzp0AeyUi8uqMHTuWLl26MHbsWBo3boynp6dt2ZdffsmiRYvIlSsXM2bMIFeuXLi5ubFw4UIGDhwIwO7dux+rk7/99htjx46lffv2rzQMExkZSXBwMOPGjaNSpUoEBwdTsGBB2/K9e/dSsmRJoqOjKVmyJJkzZ7ZNv+Po6Mj27dsJCAjAYrHYdTxHRETw4MEDkiVL9sraKiLPdvPmTapUqcKePXsICgqiXbt2pEuXzrZ85MiRdO/eHYB27doREBDAH3/8wbZt24iKimL37t34+/vbTdkEcPbsWVxcXJ4YwJaHfvrpJ/Lly0emTJkYO3YspUuXtoUdr169Sp06ddi+fTu5c+emT58++Pr6snPnThYuXEh4eDh79+7Fz8/vse2uX7+eLFmykClTpvjepbfa2bNnSZ8+PUmTJmXQoEG0atXK9jMdHh7OF198wYYNG8iZMydNmzblvffeY9OmTaxfv57bt2+zb98+/Pz8HrsWDh8+TPLkyR8LrcqrpfCOiIiIiIiIiIiIyAvYv38/NWrU4MGDBwQHB9OhQwe7AM+XX37J8uXL8fHxIW3atCROnJjTp0/j6+vLmjVrCAwMfKyTFx52ID9peHoRkbfNjh07aNWqFTdu3KBXr140atTILsBTv359Fi5ciKOjI/nz5yciIoLr16/j7e39zDr54MED3N3dX3l7b9++zbhx4xgwYAAVKlSgT58+dgGeH374gRIlShAVFUWGDBkoW7Ys2bNnp3r16qRJkwaz2Wwb6QB4rLNTROLP6dOnCQoKYs2aNXTs2JEOHTrYBXjGjRtH586dAUiTJg2ffPIJyZIlIygoCD8/v8euZ3kx9+7dY+XKlQQHB+Pl5cWIESMoW7asXYCnYcOGfP/997i4uODh4UGOHDlImjQpkydPxt/f3+7Yq47+e7t376ZWrVpER0fTr18/2rRpYxfgqVu3LuvWrcPZ2ZkUKVLw/vvv4+HhwahRox47HxK/FN4REREREREREREReUGHDx+matWq3Llzh5CQkMcCPNYReFKnTk2zZs2oWLEi6dOnJ3ny5HoQLiLvPMMw+OGHH2jatCmXL1+mb9++jwV4GjVqxNy5c8mSJQulS5emWbNmpE2b9ol18lV14j66nbgBodu3bzNmzBgGDRr0xADP/v37KV26NBEREcyfP5969eoBqKaLJJBHr+e4/z59+jSdOnVi/fr1TwzwjBkzhqCgIFKlSsXq1avJly8foOv537p//z4rV66kW7dueHt7PzHA07hxY9avX0+FChX4+uuvSZkyJc7Ozjr2r8nu3bupXr06ZrP5iQGe2rVrs2PHDgIDAzl8+DBOTk44OjrqfCQwh+evIiIiIiIiIiIiIvLfYbFYnvrvvHnzsmrVKpIkScKAAQMYP348sbGxALi4uLBgwQKqVavG5cuXOXXqFPnz5yd58uRYLBY9CBeRd8ajddLKZDJRsGBBpk+fTurUqenfvz+zZ88mPDzcts7s2bOpW7cuJ06c4O7du3zwwQdPrZOvOrhz9epVABwcHGz7kDRpUjp37kzv3r1Zv349gwYNYv/+/bb3FypUyDYqULFixWyvq6aLxD+LxWK7nu/fvw88rBPWsSoyZszI2LFjqVChAuPGjWP8+PGcO3fO9v7OnTvTq1cvwsPDSZs2re11Xc/PZzabn7rMw8ODatWqMWLECG7dukW3bt3YtGkT0dHRAKRIkYLZs2eTK1cufvrpJxInToyzszOgY/9Pxb0PW/8e97WiRYuyYsUKHB0d6devH5MnT7ZdJ56enoSGhpIrVy4uXLjAvXv3bOdB5yNhaeQdERERERERERERkSf48ccfee+993Bzc3vsW97WEXjCw8Pp1asXHTp0sHVCREZG0r59e4KDgwkMDEyg1ouIvH4///wzmTJlInHixHavG4bB/v37adasGVeuXKF37940atQILy8v2zpt2rQhKCiI9OnTx0tbe/bsyZEjRxg5ciQ5cuQA7EfguXXrFoMHD2bMmDF8/vnndO7cmSJFitjeHx0djYuLi0YlEHkD9O7dG4vFQseOHUmRIgVgH9Q7deoUbdq0YceOHbRv357WrVvbfSYLDw/H09NT1/M/MG7cOD788EOKFi36WMDy/v37rFixgi5dupA6dWoGDx5M2bJlcXV1BeDGjRuYzWZSpEih6bFekREjRuDt7U3Dhg1xdnZ+bOrJXbt2Ua1aNRwdHQkODqZdu3a2437v3j0ePHiAr6/vE6eslPinMyAiIiIiIiIiIiLyiGHDhlGkSBEWLFhAVFSU3be64eEIPPPnz8fV1ZVx48YxZswYYmJiAHBzc2PatGkEBgbaRuUREXnXjB49mvz58/Ptt98SERFht8w6As+YMWPw8PBg3LhxzJo1i7t379rWmTx5MunTp4+XOnn79m2uXr3Khg0bGDp0KMeOHQPsR+Dx9vamadOmZM2ale+++44JEyawa9cu2zY0SoTIm+H8+fN8//33jBo1ilmzZtlG1Hp0BJ4ePXqQKFEi5s6dy9dff82ff/5p20bixIkxDEPX80vavXs3nTt3plWrVhw8eJBHxwjx8PCgcuXKtGnThl9//ZXhw4ezZcsWoqKiAEiWLBkpUqSwG0FJ/rnff/+d4OBgBg8ezNKlS4mJibG7rwEUK1aMcePGcfv2baZOncqkSZNs5y1x4sQK7rxhdBZERERERERERERE4jAMg/fee4+UKVMyaNAgFixYQGRk5GMBnkKFClG5cmUuXbrE9OnTGTJkyGOd0E5OTvHdfBGR185sNuPv709gYCDBwcGsWrXKNoWNlYODA0WKFKFEiRKcPXuWKVOmMHnyZO7du2e33uuok49O65U0aVJ69epFx44dWbRoEYMHD35igCdbtmzkz5+f9OnTs2zZMqZPn24LZqqjWSRhPBoQSZcuHZMmTaJkyZL079+fGTNmPBbgMZlMFCtWjPTp02MYBiNGjGDBggW2a91kMumafgGPHvucOXMyatQorl69StOmTTlw4MBj6yRNmpQaNWrg7OzMoUOHaNy4Mbt377ZbR0GRf+bRY50+fXpWrVqF2WymT58+hIaGPjHAky9fPhIlSsRff/1Fx44dmTt3rt12dD7eHDoTIiIiIiIiIiIi8p/2aCevyWSibNmyzJo1C4C+ffuycOFCW4DHYrFgsVhwdXWlcOHCfPDBB/z1118sXLiQBw8eJMQuiIi8VnHrpMViwdHRkUqVKjF27FgSJUpEt27d+Pbbb+0CPLGxsXh4eFCxYkXy5cvHzZs3mT9//mtvq9lstnVE/v777xw/fhyADBky0KFDB9q1a8eSJUsYNGiQXYDH6vfff6dmzZpMmjSJQYMG2UbcEZH4F3eElgsXLnDlyhXgYYB6wIABFCtWjIEDB9oFeKycnJyIiYmhc+fOtGjRggYNGiik8BLiHvvw8HCio6Px9vamSZMmBAcHc+HCBZo1a2YX4LGG2NOlS0e6dOlo164d6dKls01VKP9c3PNx/fp12+8i5cqVY8qUKURGRhISEmIX4ImOjgYga9asZMmShT59+pA7d25Kly6dkLsiz6AKJSIiIiIiIiIiIv9ZcTt5d+zYwbJly4iMjMTFxYVPPvmE6dOn4+TkZBfgcXBwsL1n/fr15M2blxMnThAWFoanp+dj34oVEXmbPVon16xZw/Xr13Fzc6Ns2bKMHDkST09PW4DHOrKOk5MThmGwdOlSUqdOzbZt29i2bZttuprX1VbrNDijRo2iWrVq9OrVi9OnTwMQEBBAp06daNeuHaGhoQwYMICDBw/a3h8aGsrNmzcpXLgwrVu3xt/fH7PZ/FraKiLPFrf2TJw4kbp16zJkyBCuXbsGQIECBRg0aJAtwPPNN99w7tw5W8Bh/vz5hIeH88knnzB16lRdzy8h7rGfM2cOHTp04NtvvyUqKookSZLQuHFjQkJCbAGeffv2ERkZaav78+bNw9nZmVatWrF7925Sp06tY/8vxD0f06dPp1WrVgwZMgTDMHB2dqZ8+fJ88803tgDPokWLbL/PmM1mJk6cyIULF2jQoAEHDx7Ez89P5+MNpTFbRURERERERERE5D/JOnoEwKBBg5gyZQo+Pj6kSZOGIkWK4OjoSMmSJZk+fTrNmjUjJCSEe/fu0bJlS1xdXVm6dClHjhzhyy+/JEOGDIB9x7GIyNsubk0bMGAAkyZNImXKlEyfPp1kyZLh4uLCp59+islkIigoiKCgIMLDw6lVqxY+Pj4sW7aMX3/9lQYNGvD+++8/ts1XKW5NDwoKYvLkyRQsWJAWLVqQMWNG23oBAQF07NgRBwcHJk2axJEjR6hUqRL3799n1apVeHh4UKhQIdv6quki8S/u9dy1a1emTJlC5syZKVeuHL6+vrapsQoUKMDAgQMJCQlh8ODB/Pjjj3zxxRf88ccfzJs3D09PT7JmzWrbrq7n54t77Hv06MHXX3+Nm5sbNWvWxMXFBcMw8PLyonHjxgAMHDiQpk2b0qpVK8qVK8fmzZuZMmUKadKkIXXq1Li5uQE69v9U3PPRvXt3pk6dip+fH/Xr17cF1ZycnGwBnhYtWtCzZ09+/fVXWrVqxerVq/n666/JmDEjSZIksYWAdD7eTCZDXwMRERERERERERGR/xhrpw887BQaP348VapUoUOHDhQtWtRu3djYWMLCwmjZsiVnzpzhgw8+IEWKFOzZs4dkyZKxb98+0qRJkxC7ISLy2jxaJ8eNG0eNGjVo2bIlxYsXt1snKiqKzZs306tXL44ePUqOHDlIlSoVe/fuxdfXl71795I2bdp4afewYcPo168frVq1om3btnbBnbjOnTvHypUr6d27NxERETg6OpIzZ06+++47AgICsFgsmmJHJIENGDCAwYMH07JlS9q0aUOWLFmeuN7hw4eZMGEC8+bNs72WJUsWvv/+ewIDA3U9/wO9evVixIgRtGjRgpYtW/Lee+8B9veG27dvs3jxYsaOHcupU6dwc3MjMjKSgIAAwsLCCAgIsFtf/rm+ffsyZMgQWrZsSYsWLZ54PqKioti9ezctW7a0jTgHD0Or27dvJzAwUOfjDafwjoiIiIiIiIiIiPxnTZ48mW7dutG8eXPat29P+vTpn7iexWLh1KlTtG/fnoMHD+Lm5ka2bNmYPXu2bRoGfYNVRN5FkydPpkuXLrRq1YoOHToQGBhot9zaKR4Z5rce5wABAABJREFUGcnJkycZMmQI69atw8fHh0yZMjFr1qx4q5N//PEHn332GX5+fkybNo3MmTPblm3bto07d+4QERFBvXr1bK///vvv/PbbbyRKlIgCBQqQLFky1XSRN8DBgwepXLkyBQsWZPTo0XZBvJ9++onY2FgMw6BAgQK211euXMn58+dxd3enatWqpEiRQtfzP7Bu3Trq1KlDtWrVCAkJsY0wCXDx4kVcXFxwcHAgWbJk3L9/n7Nnz/L1119z69YtUqZMSZcuXWxTZenY/3vbtm2jWrVqVK5cmQEDBtjdhy9cuEBUVBTp0qXDxcUFeBiqGjduHA8ePMDDw4PmzZuTKlUqnY+3gMI7IiIiIiIiIiIi8p90+/ZtqlWrxqVLl1i5ciU5cuSwLVuyZAm//vorf//9N0FBQWTLlg14+O3WkydP4uzsTPLkyfH09NSDcBF5Z92+fZsaNWpw7tw51qxZYzf9zOLFizly5AjXrl2jTZs25MmTx7bs+PHjJEmSBE9Pz3itk/v27aNYsWIMGTKEbt26ERMTw+nTp5kyZQqTJ0/GwcEBs9lM/fr1mTt37hO3oRE6RN4Mq1atonr16oSGhlKzZk2io6O5evUqkydPZsqUKURHRxMbG8vkyZNp3rz5E7eh6/mfGTp0KP3792fHjh0ULFiQ2NhYIiMjmTJlCnPnziUqKoq0adMyffr0p46GpM/Hr87EiRPp0KEDO3bsoFixYpjNZsLDw5kwYQJz5szh8uXL5MiRg+nTp5M3b16791pH2tH5eDs4JXQDRERERERERERERBLCgwcPOHbsGIUKFSJHjhyYzWYOHDjAlClTWLhwIS4uLkRHR7N582bWrl1Lrly5MJlMdp0UhmHoQbiIvLOioqI4ceIEOXLksAV39u7dy9SpU+3q5IIFC9izZw8ffvghANmzZ7dtIz7rZHh4OBaLhaVLl1K0aFE2bNjA8uXLuXDhAnXq1CF//vzMmDGD+fPnU7JkSRo0aPDYNtTRL/JmuHbtGgBbtmyhePHiLFiwgIULF/Lbb79RokQJMmbMyLRp02jZsiXvv/8+hQoVemwbup5fjmEYGIbB8ePHbWEpeBhqnzVrFmFhYWTJkgVvb2927dpFvXr12LRpE97e3nbbMJlM+nz8Ct2+fRuA69evAzB37lxmz57Nnj17KFiwIBkyZGDbtm00atSIPXv2kDhx4se2ofPxdlB4R0RERERERERERP6TDMPA39+f1atXM3DgQE6fPs2mTZuIioqiV69efPLJJ2zbto0hQ4YwdOhQFi1a9Ng2TCZTArRcRCR+GIZBkiRJ2LRpE926dePy5cts3ryZmJgYevfuTbly5di+fTt9+vRhzJgxzJ49G2dnZ7vaGJ91smzZstSvX5/58+dTtGhRTCYTuXPnZuvWrWTNmpUkSZKQM2dOypYtS0RERLy1S0ReXo0aNRg7dizTp09n7ty5REdHkzVrVjZs2ECuXLlInjw56dOnp1u3bly5ciWhm/tOMJlMmEwm6taty4IFC6hTpw6pUqXizz//xM/Pj2XLllGoUCHSpk1LuXLl2LNnD1euXLEL7+iz8atXqFAh3NzcqF69OsmTJ+f69ev4+/vz7bffUrhwYXx9falRowYrV67k8OHDfPzxx7b36ny8XRTeERERERERERERkf+kNGnS0L17d7p06ULfvn3x8vKiYMGCTJo0icDAQJydncmTJw/jxo3D09MzoZsrIvLKWEdGeJ5UqVIxefJkatWqxahRo0iaNCkFChRgwoQJpE+fHmdnZz788EOGDBmCl5cXLi4ur7Xdz5r2w7ps7ty5FCxYkAcPHpA6dWqqVKmCh4cH8HAKne3bt5MoUSLbKGoveixE5NV61rVnNpvx8fFh586dDBgwACcnJwIDA2nUqBFeXl629U6fPo2Pjw/p06ePr2a/9V5k+qRy5cqxfPlyQkJCSJs2LZUrV6ZXr14kS5bMto6DgwOpU6fG19f3dTf5P69MmTLMnz+fxYsXY7FY+OCDD2jXrh0+Pj62dQzDIFOmTGTOnDkBWyr/lsI7IiIiIiIiIiIi8k558OAB7u7uz1zH2mFUvXp1MmfOzLVr1/Dy8iJXrly4ubkBDzs35s2bh8ViIW/evHbvExF5m0VGRuLu7v7cTlyLxcLHH3/Mzp07OXv2LN7e3rz33nt2dfKbb77BbDbzwQcfAK++Tm7cuJGNGzcyZswYHB0dn9rmuMtat2792HLDMFi5ciUrV64kf/78tim+VNNF4s+2bdu4ffs21apVw2QyPbVeWK9nX19fxo0bh6Ojo926hmGwYsUKNm/eTKFChciQIUN878pb5/Dhw6RIkQI/P78XCvBUq1aNkiVLkjRpUmJjY3Fy+l+sIDQ0lF9++YWyZcs+cYomeb5Dhw7h7e393J9di8WCg4MD1atXp1y5cnh4eNheswoNDWXfvn2UKFGCpEmTvuaWy+tkMgzDSOhGiIiIiIiIiIiIiLwKmzZtIigoiEWLFvHee+89c91ndTAbhsGSJUsYNGgQ7u7ubNiwQd8sFpF3wqZNm6hXrx47duwgR44cz+3EfVqtNAyD0NBQBg4cSKJEiVi/fv0rr5Nbt26lTJkyAHTq1InRo0cDLzZyxKOGDh3K9OnTiY2NZffu3fj7+z/WASoir8/27dspVaoUACtWrODzzz8Hnh/4e9Ly0aNHM3XqVNv17Ofnp4D1M1iPffbs2dm0aRNp06Z9oTr6pGM6depUxo4di2EY7NixgzRp0ujYv6Rt27ZRunRpsmTJwoYNG547cpT1+D7tfIwZM8Z2PtKmTavz8RbTJxIRERERERERERF5J2zevJlKlSrx66+/UrduXY4fP/7M9Z/WGX3v3j169uxJjx49uH//PitWrMDX1xeLxfK6mi4iEi82btxItWrVuHHjBqVKleL333+3jXDxNE+qldevXyc4OJhu3bpx7949li9f/srr5LZt2yhTpgyFChUid+7cjB07lg4dOgA8t81x/fzzz/j7+zN8+HD8/PxswR2z2azgjkg8Wb9+PaVKlSJnzpz4+PhQvXp1li9fDmALJTxN3Bq0b98+cuXKxdChQ/Hx8WHHjh22kWQUVniyNWvWULp0aby9vTl+/Dg1atTgwoULL1RH4450dPToUSpWrMiAAQNwdXVl8+bNpEmTRsf+Ja1fv55y5cqROnVqTp8+zeeff86ZM2ee+R7r8bX+abFY2LlzJxUqVCAkJAR3d3e2bt1qC2XpfLy99KlERERERERERERE3npHjhzh008/5aOPPqJFixacOnWKzz///LkBnkedPXuWDz74gPHjx5MrVy52795NQECAOnlF5K13/PhxypcvT8GCBWnVqhVXrlyhaNGiLxTgievvv/+mUKFCjBw5kg8++IA9e/a8ljrp7e3NZ599xpQpU5g/fz4fffQREydOfOkAT86cOalQoQI9evRg1apVtuDOy47cIyL/3Pnz5ylYsCDz5s1j6tSp+Pr6UqtWrRcO8FglT54cT09Pmjdvzrp162y1R9fzk8XExDBv3jzy58/P2rVradSoET/88AM1a9Z84QAPQFRUFD/88AMHDhygcuXKbN68mcDAQB37f2D69OkULlyYuXPnEhwczLFjx6hWrdpzAzxxOTg4cOrUKX7++We+/PJLNm/erGvhHaFps0REREREREREROSdMGjQICpUqMAHH3zA8OHDGThwIAEBAaxatYrs2bO/8HZWrlxJREQEn332GUmTJtWDcBF5Z4wbN47ChQtTsGBBhgwZQu/evfHx8WH37t1ky5bthevd1q1buXz58murk9bprO7fv4+HhwcAP/30E23btmXfvn20a9eO8ePHA8+eQivu1CHW9TRVlkjCuHz5MqlTpwZg4cKFdO7cmWvXrrF06VJq1KgBPH9KU5PJREREBE5OTri4uOh6fgG3b9/m0qVL5MiRA4CGDRsyb948ChYsyLJly2wjFz2vhlssFs6ePUuqVKlwd3fX5+N/yGKx8Mcff5A9e3YiIiIYPHgwI0eOJEeOHKxcuZIMGTK88LbOnj1LypQpcXNz0/l4Ryi8IyIiIiIiIiIiIm+1Jz2svn//PmPHjmXIkCH4+/u/UIAnboeR9e/qFBKRd8HTatmgQYMICQn5RwGe5237VbUR/lfnf/75Z9q0afNSAR4RiX8vUhcWL15Mx44dXyrAI8/3tGMf97g2atSIuXPnvnSA50nbkmd73rUQHh7O8OHDGTFixAsHeB7dps7Hu0O/dYqIiIiIiIiIiMhbx2Kx2P4eN3ADDztxPTw86NKlC8HBwZw7d+6FptCK+9Db+ncFd0TkbRW3Tj5ay2JjYwHo3bs3AwcO5ObNm/9oCq0nbfufsm5nxowZ3Lx5026ZtUM5d+7cTJkyhcKFC/+jKbREJH5Yr+ft27cTHR1tt8xam+rUqcO4ceP+8RRa8jjDMGzH/vbt23bLTCaTrU7Onj2bBg0a/KMptKzbkhdjPR9Hjx61/exbf74tFguenp50796dbt26vfAUWo/ed3U+3h36zVNERERERERERETeOtaH1i1atOD777/HbDbbHlxbp0Vxd3d/6QCPiMi7wlonGzVqRFhYmF1nuJOTk60TsVevXgwYMOBfBXhelUmTJtG8eXNGjBjxWMez1QcffMDkyZMV4BF5w3Xp0oVSpUqxcuVKYmJibK87ODgowPOaWD8LV65cmUKFCnH16lW75XHr5L8N8MiLq1GjBpUrV2bPnj22UXKsQat/GuCRd5PCOyIiIiIiIiIiIvJW+vHHH5kxYwa9evUiLCzMrrPB+jBcAR4R+S/bt28fS5cupXXr1uzfv9+uMzxuB3rv3r3fiABP+fLladu2LaNGjWL48OHcunXrietZR+D56KOPmDhxIu3btwcU4BF5k3z++ed88skntGzZkhUrVtiNwPNogGf8+PG2AM+yZcsABXj+qYiICPLly8fff/9NjRo1FOBJYBaLhZo1a/LgwQO6du3Knj17sFgsdqN8KsAjVgrviIiIiIiIiIiIyFspT548bNiwgevXr9OtWze2bNmiAI+ISBx58+ZlyZIlREdH06xZM3bv3v1YgMdaN9+EAE/GjBnp0qULbdq0Yfjw4QwYMMA2xdejrCPwfPTRR0yaNEkBHpE3TNGiRRkyZAgffvghdevWJTQ09KkBwi+++MIW4Kldu7YCPP9CokSJ6NixI3379uXXX3+lfPnyXLlyxW4dBXjij4ODA9WrV2fatGmcPXuWli1bsmvXrsfWUYBHQOEdEREREREREREReUs5OjpSunRpZs2axenTp+nQoQM3b960W0cBHhH5L3N1deXTTz9l7NixXLt2ja+++uqx0WzidtLGV4Dn0c74uFPqBAQE0KVLF5o1a8bHH3+Mk5PTU7ejAI/ImyfutVewYEFGjRpFgQIFSJo0qW20ESsFeF4t67H38vKicePGdO/enSNHjvDHH388tq4CPK9e3J9TwzBsx8/JyYnKlSszceJEzp49+8SfZwV4BMBkqNqJiIiIiIiIiIjIW8xsNrNp0ybg4ZQrT2KxWHBwcODBgweMHj2awYMHExAQwKpVq8iePXt8NldEJN5FRUWxfv16EiVKxKeffvrEdax1EmDQoEGEhITg4+PD7t27yZYtG2azGUdHx3/dFsMwbB34p06dIlOmTLZlU6ZMoXjx4uTMmZMHDx7g7u7+Qts8fPgwHTt2ZPfu3bRo0YKpU6f+63aKyMuLe32PGDECLy8vWrZsyZ07d0iSJMlT3xe3/ixcuJCgoCCuXLnC4sWLqV27dry0/W0T95g96vjx42TPnp3bt29z/fp1uzr7qLi1vUGDBsyfP59ChQoRGhpKunTp7M6pPN2zzsfevXt577338PLy4u+//yZVqlTP3U54eDhDhw5l9OjR5MiRg+XLl5MxY8bX1Xx5Q2jkHREREREREREREXkrWb/N6ujoSLly5Z4a3AH7EXg6dOhA3759uXTpEhUrVuTkyZPx1WQRkXhl/f62q6srVatWtQV3nvWtf4BevXoxZMgQbt++zYcffsipU6deSXAHsHUCFy9enHr16vHbb78B0L59e9q2bcu+ffswm80vHNyJiYkhb968zJ8/nyRJkvDNN9+wbt26V9JWEXk51uu7f//+9OjRg99//50bN248M7gD9vWnXr16zJgxA4AWLVrYaoTYswZFWrRowcKFC22vN2nShIIFC/LHH3+QNGlSW3DnaeN5xB1hZ+7cubRo0YL9+/fTvHlz7t69q+DOC7Kej6pVqzJ27Fjb602bNqVWrVq2ET+twZ2nnY+4I/D069ePfv368csvv9CmTZvHRs6Td8/TxxoUEREREREREREReYM8+o3WuJ0JJpPpmd94hYcPw2NiYvD09KRHjx4sX76cw4cPs2HDBjJkyPDKOqZFRBLKo3Uw7ogJ1qlnTCbTUztjrXXS2dmZHj16sHLlSg4dOkRoaCg9evR4ZXXSMAwCAwOZP38+ISEhuLi4EBoaSlBQEGXLln2p/8fZ2Rl4OFpQbGwsPXv25L333nsl7RSRFxN39JbLly+zZs0aGjZsSKdOnUiWLNkLbSNu7Vq7di1JkyalSZMmeHl5vZY2vwu2bNnC9OnTOXjwIGnTpmXlypXMmTOHli1bPnbcnhXCiVtzDcPA29ubDz74gMjISB3/l3Do0CFWr17Nrl278PPz44cffmDWrFm0adOGgIAAu3WfdT6s14KLiwunT5/Gy8uLbNmy2U0xKe8mTZslIiIiIiIiIiIib7y4nUKhoaHs2bOHo0ePkitXLkqWLEn58uVxdXUlNjYWJ6fnf2exRYsWzJgxg+DgYJo1a4a/v//r3gURkdfqSXXy1KlTpE2bltq1a5M3b158fHyeG3S0+uqrr1iwYAEhISE0btz4tdTJ3r17M2TIEADq16/PxIkT8fLyeulpWs6fP8/7779PgwYN6NGjxzOnJBGR12fx4sWkT5+eGjVqsGDBAkqUKPHS29i5cyclSpSgS5cudO7cmdSpU7/6hr4j7t+/z5o1a2jdujXOzs5cu3aNoKAgunXrRvLkyV96e19//TWtW7emR48etG3bljRp0ryGVr+brPetsLAwSpYsiaurK1FRUQQHB9OhQwd8fX1feptTpkyhbdu2BAcH07p1a52P/wCNvCMiIiIiIiIiIiJvNMMwbB3SQUFBTJw4kcSJE+Pt7c2BAweYMmUK9evXZ9KkSXh4eLzQNu/evUvnzp1p06aNOnlF5K33aJ2cMGECHh4eeHt7s2PHDhYsWEC9evXo3bs3gYGBL7TNxIkT06ZNG1q0aBEvnecXLlzg0qVLeHl5vVRwxzAM0qVLx7Fjx3BwcCBlypSvsZUi8jSrVq2iXr16+Pn54ezsTNasWQFeOoz38ccfs3//fvz8/BTceQ4PDw+++OILpk2bRlhYGClSpCBr1qy24M6LhjWtqlWrhpeXFyVKlFBQ5B8qUaIEpUqVYtu2bbi6upI0aVJbcCduyPZFtGjRAi8vL0qVKqVr4T9CI++IiIiIiIiIiIjIW2H48OEEBwfTokUL2rVrR/bs2Tl37hzly5fn+PHjNG/enK+//vqZ24jbgXTnzh2SJEkSH00XEYkXI0aMoGfPnnZ18syZM9SsWZOffvqJVq1aMWnSpGd2pMetk3fv3n2lU6bE7UiOiopixowZXL9+nbNnzzJnzhzKly/PwIEDyZs37yv7P0Ukfpw7d46BAweyZs0arl69yuLFi6ldu/ZLbeNlgz4Cv/zyC61atcLT05O9e/eSMWNG+vXrR5UqVeymS3xROgcvz3rMDMPg0qVLfPnllyRKlIgNGzbg5eXFqFGjaNq0KfDigaqXDfrIu0HhHREREREREREREXnjnT17lgoVKpAsWTKmTZtGtmzZiIyMZPv27TRt2hRPT0/27NlDsmTJbO95WueDOiVE5F105swZqlSpQtKkSZkxYwZZs2YlNjaWtWvX0rFjR5ycnDhw4AA+Pj4J0r64HZE///wzrq6uZM+enYiICBIlSkTHjh2ZMGEC5cuXZ9CgQeTJk8euXt+7d4/EiRMnSNtF5Nms1+r58+cZNGgQs2fPpmDBgixZsoS0adMmdPPeaYZhcPToUXx8fNi7dy/NmjUjICCAgQMHUqVKFYAXnlZWXl7cMM61a9fw9fXl6tWrpEiRwjYFnKenJ2PGjKFJkyYAxMTE4OzsDOjciL0XHydLREREREREREREJIFcvHiR48ePU69ePbJly0ZsbCzfffcdLVu2xMXFhd27d5MsWTIsFgtnzpzBbDY/NaCj4I6IvIuuXLnC77//Ts2aNW3BnRUrVtC+fXtMJhP79u3Dx8cHi8XC+fPniYqKire2xQ3uDB06lBo1apA3b15OnjxJokSJABg9ejTt27dnw4YN9O7dm0OHDtnq9XfffUdQUBDHjx+PtzaLyJNZLJanLkuXLh19+vShUaNG7Nmzh2bNmnHr1q14bN27zWw22/07NjYWk8nE+++/j5+fH5UqVWLixImcPXuWkJAQvvvuO8xmM05OTpjNZjZv3sy+ffsSqPXvHrPZbAvuTJkyhQoVKtC+fXtSpEgBPJwCbsOGDYSHh9O5c2dmzpwJgLOzM2azmW3btvHtt99y586dBNsHebMovCMiIiIiIiIiIiIJKu7g4IZhPNYxAfD3338D4O3tDcDSpUvp1q0bDg4OHDhwgOTJkwNw69YtgoKC2Lt3bzy0XEQkYcStk9YaevnyZcxmM6lTpwYgNDSUbt264ejoyMGDB/H19QUejgzQunVrfv7553hpq2EYtuBOUFAQ/fr1I0+ePKxcuZLMmTPb1nN0dGTMmDG2AE/37t3Zvn07S5cupWfPnsyaNSvBRg0SkYfihhVOnDjBnj172L9/P5cvX7at4+fnR0hICM2aNeP777+nXr16CvC8AhaLxVZLZ82aRatWrahXrx6LFi2yhTHd3d2pWbMmkyZN4q+//iIkJITVq1djGAabNm2icePGfPXVV0RGRibkrrwT4p6Prl270r17d1xcXMiRI4fdOp9++inff/894eHhdOnShenTpwOwfv16mjRpwpAhQzQ9ltho2iwRERERERERERFJMHGHmn/Ujh07yJ49OylSpODEiRPkyZOHGjVqULt2bVq3bo3JZOLAgQO2DmmA9u3bM2fOHLZt20a+fPniazdEROJN3KmkTp8+TcaMGQE4fvw4hQsXpnjx4jRs2JBOnTo9sU527tyZadOmsXXrVgoWLBhv7Z4xYwZt2rShVatWtG/fngwZMjxxPYvFQvfu3Rk9ejQAbm5upE6dmi1btpA+ffpn3jdE5PWJe+0NGTKE6dOnc/bsWQDy5s1LixYtaNasmW39ixcvMmDAAKZPn065cuVYuHChLYQt/1zXrl0ZPXo0Tk5OxMbGAtCwYUM6d+7Me++9B8CDBw9YsWIF7dq1w9nZmRw5cnD69GkAtm/fTqZMmRKs/e+akSNH0qNHD9q2bUv79u1t92Qr6z178+bNVKxYkdjYWPLmzcu5c+dwcXFhx44dj71H/rv06UZEREREREREREQSjLUTqEKFCvTv39/2erNmzahevTonTpzAMAx8fX0pUqQI8+fPp2HDhjg6OvLLL7/YdUjPnTuXNWvWUKFCBbJnzx7v+yIiEh+swZ1u3bqRK1cuzp8/D0DatGnJmzcva9asoWXLlphMJn777Te7Ojlv3jxWrlxJxYoVbZ28r5t1RLVVq1aRJk0a2rRp89TgDjy8L4wcOZKpU6fSqFEjWrVqxa5du0ifPr3dqB8iEn8Mw7Bde926daN3796kT5+e0aNHM2vWLE6cOEHHjh0ZPHiw7T1p06YlJCSE5s2b8/3331OxYkVND/QPxB2HY/ny5XzzzTc0bdqUAwcOsGnTJurXr8/8+fMJCQmxjajm7u5OrVq1CA0NxcPDgzNnzpAlSxb27NlDpkyZbKEf+XdOnjzJnDlzKFSoEB07dnxiCMdkMmEYBmXKlGHbtm0UKlQIs9nMhx9+yL59+8iYMaPOh9g4JXQDRERERERERERE5L/tjz/+4PvvvycsLAw/Pz9+++03Zs6cSZs2bciYMSMmkwlvb29CQkLYtWsX169fp2HDhnh5edm2MWPGDIYPH46rqytjxozBw8PDbnQKEZF3zd27d4mMjGTHjh18+eWXeHl5MXXqVIoWLcq1a9do27YtiRIlsq3/zTffMGrUKFxcXBg7dmy81UmTycTVq1fZsmULn3/+OZkzZyY2NhYnp2d3UbVo0YIWLVrY2mg2mzW1iEgCsdaJyZMnM336dNq2bUu7du1sU98NGDCAS5cu0adPHxwcHOjZsyfwMMDTp08f7t69y/bt24mOjk6wfXgbxa3RkZGR/PTTT2TOnJlu3brZRs/JnDkzqVOnto1WFhISQu7cuXFxcaFs2bIcPXqUe/fu4eXlRaJEiTCbzc+tv/JiLl26xPHjxxk+fDjp06d/5rqGYVC0aFHWrl2Li4sLDg4OOh/yGE2bJSIiIiIiIiIiIgnG2inx888/U6xYMaKiooiNjaVHjx506dKFZMmS2b5xbDKZ2LRpE9WrV+f+/fvky5ePwMBA/vzzT44dO0aaNGnYvHkzgYGB6uQVkXeWtW6ePXuWjz/+mAwZMrB9+3bb8t27d1OtWjWuX7/Oe++9R5YsWfjzzz85fvw4fn5+bNq0Kd7r5O3bt8mQIQN58uRh69atjy23Tsdz4cIFfv/9d0qXLh0v7RKRF/fXX39Rq1YtUqZMyYgRI8iePTt3796lQIEC3L17lyZNmjB69GgiIyMZPHiwLcAD8Pfff+Pi4oKPj4+mvvsHunfvzrFjx3B2dubDDz+kV69ediHIixcvMmnSJEaNGkWlSpXo27cvH3zwwWPbUbD91Vq2bBm1a9dm4sSJtGnT5rFgqvVn/fbt2yRNmhSwPwc6H/IoVUYRERERERERERFJMNah5HPnzk2JEiVsD71dXFxIliwZAGaz2bZe2bJl2bp1K02bNiU8PJywsDAcHR1p164dO3fuVHBHRN551o6+5MmT8/HHH7Njxw5mzJhhW160aFH2799P/fr1cXR0JCwsDDc3Nzp06JBgddLNzY20adOyc+dOli1bZgtlGoZhNx1P7969adq0KVevXo23tonIi4mOjiY6Opr69euTPXt2IiIi+OSTT7h16xbDhg1j4MCBhIaGAg+v5UGDBtnemypVKnx8fOyud3kxN27c4Pz582zYsIFvv/2WU6dOYRgGTk5OtlqaNm1a2rZtS1BQEGvWrGHQoEEcOnTosW0pKPJq+fj4AA9DPHfu3LE7J9afdcMw+OSTT+jTpw9gfw50PuRRGoNJREREREREREREEpTJZOLvv//G0dGRatWqsW7dOoYOHUqSJEno1KkTTk5Otm+uWiwWChQoQO7cuYmKiuLq1aukS5cOR0dHHB0dFdwRkXfKozXN+m/DMPDw8KBbt26sWrWKTZs20aRJE0wmE7GxsWTIkIFp06YBD0dkCAwMxDCM11onnzWahpubG7169aJRo0bMnDmTwMBA8ufPbzf6wJIlS9i1axclSpSwmxZRRN4M/v7+zJ8/n1y5chEbG0v37t05ceIEgwcPplatWsDDKZx8fX1xdHQkJCQELy8v2rdvb9uGwgovL1myZPTr1w9fX1/mzJnDb7/9xq+//kquXLls4XaTyWQL8Dg6OjJkyBCSJEnC119/rSmZ/qVn3dtKlixJ0aJF2blzJzNnzqRZs2Z4enoSFRWFq6srFouF+fPnc/XqVZycnPR7ijyXps0SERERERERERGRN8KVK1dImTIlR48epWDBgpjNZoYPH07Hjh0BiImJwdnZGcD2UFxE5F1y7do1PDw8SJQokd3r3333HWXLlsXd3R14GHaxWCwAtGjRglmzZrFhwwY+/fRT2/L4nJYjbofkzp07OX36NDdu3CBlypR88cUXODs7c+3aNfr27cu0adMoXLgw9erVo3bt2hiGwcKFC5k0aRIWi4WwsDDSpk2r6UREEsizwgrWa/3q1auUKlWKpEmTsnXrVlxcXACIjIzkww8/pGLFioSFhbFs2TICAgLis/lvtWfVvePHjzNx4kS++eYbatWqxciRI/Hz83vsfefPn2fhwoXUrVsXf3//eGv7uyjuve3ixYtcuXIFk8lExowZbSHTnTt38tVXXxEVFUWLFi3o2LGjbYqsRYsWMXDgQFxcXNi0aRMpU6ZMqF2Rt4TCOyIiIiIiIiIiIhKvntYpZH1UaTKZOHDgAJ988gmxsbEMGzaMTp06ARAbG0tYWBiXL1+mQoUKtqm1RETedgcPHqR69er06dOHBg0a2DrDhwwZQu/evcmePTsNGzbks88+I3v27Lb3LVu2jNq1a1O5cmXmzJlj6zSML3Fres+ePZkyZQrh4eG25Xnz5mX48OF88sknnDt3jsmTJ/P1119z//59/P39iYqK4tatW2TIkIH169dr+kORBBT32jtz5gx37tzhzp07FChQAFdXV9uyEydOkD9/fsqXL8+SJUuAhyHrsWPHMnbsWE6cOEHixIlto4Fp9Jfni3vsr127RkREBFFRUWTJksW2zu+//8748eP55ptvqFu3LsOGDXtigMdal1VL/7m497bBgwczd+5cTp06BUDq1Knp3bs3VapUIU2aNISGhtKnTx9OnTpFlixZyJcvH+fOnePw4cMkS5aMnTt3EhAQ8MxgnAiAfjpEREREREREREQk3pjNZttD6+3bt/P111/Tp08ftm7dSkxMjK3ToUCBAoSFheHk5ETPnj0ZNWoUAOvXr6d58+YMHTrUNgqPiMjbzjAMLl68yOXLl/nxxx/tRl6oUaMGnTp1wsXFhe7du1O4cGGGDh3Knj17AKhZsyZ16tRh+/btXLp0CcA2Kk98sNb0kJAQhg8fTrVq1di+fTvnz59n8ODBnD59mi+++IKNGzcSGBhI7969Wb9+PdWqVSNDhgwULFiQgQMHEhYWpuCOSAKyWCy2a2/w4MFUrFiRggULUrJkSQoVKsTIkSO5efMmAF5eXiRPnpy1a9cyc+ZMLBYL8+bNY+7cuWTNmtUuSKLgzvPFPfajR4+mcuXK5M6dm/z581OzZk0WLVoEQLZs2ejQoQPNmzdn0aJF9OjRgwsXLgDYptCC/9Vl1dJ/znoMu3XrRp8+fUiXLh3ffPMNgwcPJkOGDHTv3p3g4GAuXrzI559/zurVq6lWrRoREREsWrSIW7duUadOHfbt20dAQIDd70AiT6ORd0RERERERERERCRexP22ae/evZkwYQL37t2zLW/UqBHNmzenYMGCttcOHjxImTJluHv3LtmzZ+fKlSskSpSI7du3kzFjRk2rIiLvjMjISI4cOUK2bNnw8vLil19+ITAwkCRJkhATE8O9e/eYMmUKS5cu5ejRoyRNmpTq1avTqVMn9u7dS+fOnSlbtiyhoaHx3mG7d+9eqlevzscff8yQIUPImDEjAAsWLKBNmzYkTpyYX3/9FW9v72duR6MSiCS8bt26MWrUKEqUKEHFihVtU9sdP36cUqVKMWfOHHx9fdm0aROVK1cmOjqapEmTcvv2bQICAggLCyMgIECf0V5Q3OMUFBTEmDFjyJs3L0WKFOHBgwcsXboUd3d36tSpw5gxYwA4deoUo0aNYtq0adSvX58BAwZoerLXYNmyZTRq1Ii6desSFBRElixZiI2NZcqUKXTs2JE8efKwc+dOPDw8bO+5efMm169fJ3369BiGgYuLi0Kp8sIUdRQREREREREREZHXzjAMu2lVRowYQbVq1WjdujVp06Zl0qRJTJ48mTt37tCxY0eKFi0KQP78+dm5cyddunQhPDycjBkzMnnyZNKlS6dpGETkneLm5kaBAgUAGDFiBH369GHOnDlUrlwZDw8PvL296dWrF/Xr12fv3r0MGTKEmTNnsnHjRnLmzImHhwf79u3jxx9/tAtBxodTp05x5coVGjRoQMaMGYmNjWXZsmX07t2b5MmT88MPP+Dt7U1kZCRubm7Awyl2rCOoWTuvFdwRSVjLly9n0qRJNG3alK5du5I5c2YAUqZMSYMGDbh8+bLtGi5btix79+5lxIgRODg44OfnR6dOnUidOrXCCi/BGtyZN28eEydOpFWrVnTs2NF27DNlykSPHj3Yv38/9+/fx8PDg0yZMtGlSxccHR2ZOnUqXl5ejB8/XjX0FduxYwfu7u60bt2aLFmyEB0dzXfffcfo0aPJkCEDGzduxMPDg9jYWBwdHTGZTPj4+ODt7W07r4Zh6FqQF6bfbEVEREREREREROS1sz7AXrRoETNnzqR169a0a9eOLFmycP/+fbZs2YKLiwurVq0iIiKCXr16UaRIEQDef/99Vq5ciYuLCxaLBXd3d8xms4I7IvLWijvSgmEYdlOmWCwWAgICSJs2LT179sRkMlG5cmUSJUoEgL+/P/7+/nz88cf8+OOPjBo1in379nH37l08PT3JlCnTa237kzrlf//9d+Bh4BIejlbQo0cPHBwc2L9/P8mTJwfg2LFjTJkyhRkzZthNfajROUTiX9xr2VqTduzYgaurK+3atSNz5szExsaydOlS+vXrR0BAAJs3b8bT05OoqChMJhN58+Zl9uzZts9mjo6OCu68gLijjFnvARs2bMDX15c2bdqQOXNmzGYzoaGhTJ8+nYCAANasWYOHhwdRUVG4urqSOXNm2rZti5eXF61atVJw51969Of2/v37bNu2jUyZMpE7d26ioqJYtWoV3bt3f+zedujQIRIlSkSuXLkA+3ua7m/yMnQVi4iIiIiIiIiISLy4c+cOq1evJlWqVDRp0oQsWbJw9+5d8uXLx61btxg3bhxt27Zl48aNjBo1ip07d9re6+HhgaurK+7u7voGq4i81SwWi11wx2Qy2Wranj17MJvNfP7554wdOxZnZ2e6du3K6tWriYiIsL3HMAzSpElD5cqV2blzJ1OmTCEoKIgjR46QLFkyLBbLa2u7ta0TJ05k0aJFAKRKlQqAlStX2gV3Dhw4gK+vr+39I0eOZNWqVfz111+vpX0i8mzbtm2jV69eALagjVVUVBT79+8nXbp05MqVi8jISFasWEHPnj2xWCwcOHCAZMmSAfDnn3+ye/duzGazbSQea3hEn9GebM+ePYSGhgLYBW1MJhPh4eH88MMP5MyZkxw5chAVFcXy5cvp2bMnsbGxdsf+r7/+4siRIwBkz56dQYMG4e/vb3cu5eXEvbetWbMGs9mMh4cHgYGBXLt2jUuXLrFp0yZbcOfAgQO24A5AkyZN6Ny5M9HR0Qm1C/KOUHhHREREREREREREXou4nQjWKa7MZjM9evQgd+7cPHjwgPLly3Pjxg2GDx9O06ZNadmyJalSpWL16tVMmDCBsLAw4PFODhGRt5W1nlWvXp0hQ4bYXm/WrBmlS5fm0KFDuLi4UK5cOUaOHImbm5tdgMdkMtnqoLXO1q1bl6FDhxIQEEBsbOxrG4HBut3evXvToUMHfvrpJ2JjY6lQoQLe3t4MHz6cTp06YTKZ+Omnn+yCOzNnzmTXrl18+eWXpEmT5rW0T0SezDAM7t27R8uWLRk6dCi9e/cGHgZtrIFCV1dXUqVKxf379wHYuHEj3bp1e2IQ74svvmDatGmYzWZbPdLnsyczDIOrV69SpkwZ6tSpw969ex9bx83NjUSJEtmCl999990Tj31MTAyff/45ixcvttV/a+hEoal/znpv69q1K7Vr12bKlCnAw3DUmTNn6NixI23atMHR0ZH9+/fbXQujR4/m2rVrlCtXTqOCyr+mnyARERERERERERF55eKOjtOzZ0/8/Pxo06YN06dPx8PDA8MwGDNmDIcPH6Z3795Ur14dR0dHsmfPzocffsgff/zBypUrcXV15aOPPsLFxSWB90hE5NU5duwYq1atYteuXaRNm5ZDhw4xc+ZM2rRpg7+/PwCurq6UL18eeNih2LVrVwC7KbTidtZa//46Og/jTidy5swZVq9eTZMmTWjbti1OTk5kzJiRbt26MWTIEMLDw1m5ciVJkya1vX/evHkMHz4cb29vevbsiYuLi93UYSLyeplMJhInTsyiRYv46quvGDJkCGazmaFDh+Lg4EB0dDTOzs7kzZuXdevWUaNGDQ4cOICTkxN79+61CyuMGTOGv//+m6+++kphhRdgMplIkSIFI0aMYO/eveTIkcNueUxMDA4ODuTJk4eFCxfSuXNnVqxYYZua6dFjf+XKFTJnzqxpsl6BuNOXhYWFMX/+fJo1a0bZsmUB6NOnD1u3bmX58uX4+Piwb98+UqZMaXv/0qVL+eabb8iUKRMNGjTQOZF/zWQYhpHQjRAREREREREREZF304gRI+jRoweNGzdm+PDhtiH/AapWrcrhw4f5/fffbR3RZrOZnDlz8vnnnxMYGEj58uVtHdkiIu+SH3/8kapVq3Lr1i0ePHhAly5dCA4OxtvbG/jflFpRUVFs2LCBrl27EhkZyciRI6lSpQru7u7x3uYNGzbg4+NDpUqV+Pbbb/noo49sy06dOsWkSZP45ptvyJIlC8WKFaNAgQKsW7eOrVu3kjhxYnbs2EFAQIBdGEhE4oc1qPDTTz9Ru3ZtTp06Rffu3Rk6dKhtndOnT1OoUCFu3LhB8uTJ+eOPP+xqknUqp1SpUrFq1Sq7YIk8WdyASExMDM7OzoSEhJAxY0YaNGhgW2/dunVUqlQJeDgV4dmzZ3F2drZtY8WKFfTo0YOAgABbmERejWvXrrF48WJGjhzJpk2byJ49u20UpNWrV9O7d2/Onz9Pq1atqFSpEkmSJGHevHksXLgQR0dHdu/ejb+/v925FvknFN4RERERERERERGRV+bRDtnixYuTNm1a+vfvT+bMmYGHHRBRUVHkzZuXe/fusW3bNtuy+fPn07t3b6ZMmULFihWfuE0RkXdF1apVWb16Ne7u7vTo0YM+ffoAj9e9uAEes9lMSEgIX3zxBW5ubq+1fXE7IufPn0+DBg3ImjUrjo6OHDx4EHd3d7u2njt3jnXr1jF8+HDOnTsHQNq0afn4448ZMWIEadOmVU0XSUAvEuAJCwujcuXK3Lt3jx49elCzZk3c3NyYNWsWoaGhWCwW9u7dq7DCS4g70tjhw4fJly8f7u7uzJ49m1q1atnWGzduHJ07d8bHx4fp06dTqFAhXF1dmThxInPmzCE2Npa9e/eSLl06Hft/Ie75CAkJYdq0aRQpUgRvb29mzJhhm0rOZDIRERFBWFgYffr04aeffsLJyQmLxYKHhwd58+Zl7ty5+Pv7694mr4TCOyIiIiIiIiIiIvLKjRw5Eg8PD0aNGsXUqVP59NNPbcusD8yto/I0adKESpUq8dtvvzFz5kxcXV3ZsWMHyZMnT8A9EBF59eJ2GJ47d4527drh6upKWFgYFouFkJAQ2rRpg6Ojo61jNu4IPBs3bqRBgwZkypSJHTt22EYtex3idgyfP38eNzc3WrZsybZt24iIiOC7776jXLlyj+0XwK1bt/jrr7+4ffs2uXLlwsPD47Ggj4jEn7jXqPXvzwrw7N69my+//JJz585hMpkwDAN3d3cKFCigsMJLetIUgXPnzqVt27YAzJgxg9q1a9uWTZw4kQ4dOgCQJEkSoqKiMJvN5M6dm6VLl2r0sn/p0WM3duxYhg8fztWrV8mePTvbt28nRYoUj70vOjqa2bNnc/36daKjoylatCj58+cnadKkOh/yyii8IyIiIiIiIiIiIq/Unj17KFasGOnTpycqKooVK1ZQsGDBxx5sHz9+nOHDh7No0SJiY2MByJkzJ2vXriUgIEDfKBaRd0rcGvj333+TKlUq7ty5g6OjIydPnqR8+fLExMTQr18/2rZti8lksk2xYhUdHc327dvJlSsXadKkiZd2d+rUiWnTpvHzzz9jNpvp27cvy5Yto2rVqsyePZskSZLYrf+02v2kDmwRef3iXpPW2mP1rADPxYsX2bVrF8eOHcPV1ZWCBQuSP39+kiRJorDCC4p77O/evYuXl5dt2bx582jZsiWOjo6PBXh2797N7t27OXLkCL6+vnz00UeUKVMGHx8fHft/Ie59qFmzZqRJk4b+/fszadIkJk2axJUrV5g8eTI1a9a0u/c+63cS/b4ir5LCOyIiIiIiIiIiIvKvPKlDdvTo0XTt2hV4+A3iNm3aPHHdy5cv88svv7Bnzx4yZsxIhQoVSJEihTomROSdEremTZo0yTZyxYoVK2zr7Nmzh+rVq9sCPC1btsTZ2Rmz2cyWLVu4c+cOn3/+ua1D8XXVybh1esmSJbRs2ZIaNWoQFBREtmzZ+O233+jXrx8rVqygUaNGTJ06FRcXl1feDhH59+LWicmTJzNr1ixy587NzJkzX2gKrSdRWOHFxD32s2bNYvHixVSrVo1WrVrZ1nlWgOdJdOxfjVGjRtG9e3dKlCjBt99+i8ViYd68eQwZMgQvLy9mzZrFRx99pMCpxDunhG6AiIiIiIiIiIiIvL3idiLcvn2bpEmTAtClSxfc3d1p27YtXbp0ITAwkIoVK9qmXrA+DE+dOjWpU6e2Tb1i3aaCOyLyrohb07p27cqUKVMoWLAgFSpUsFuvSJEirFy5kmrVqtG/f3/MZjNt27Zl48aNtgBk+fLlbeGd1x3cuXPnDj///DM5c+akd+/eBAYGYhgGOXPmpH///gDMnj0bQAEekTeQYRh2tWfq1Kl8+OGHFCxYEAAHBwcsFgt58uQhNDSU2rVrM3z4cEwmE0OGDAEejvZlvbat9UHhkeeLW/e7devGtGnTSJ06td1yBwcHvvrqKwBatmxJ06ZNAWwBntjYWBwdHe0+O+vY/zNxg1TR0dHs3buXOnXqMHDgQDw9PQFo1KgRJpOJ/v3706xZM6ZPn64Aj8Q7jbwjIiIiIiIiIiIi/1rPnj25evUqffr0ITAw0Pb6pEmTaN++PZkzZ2bSpEmUKVMG0PQpIvLfM2rUKHr06EHbtm1p06YNmTNnfuJ6+/bto1q1aly5coUcOXJw7do1XF1d2b59OxkzZoyX+tmzZ09Onz7NxYsXKVmyJAMHDsRsNuPg4GD7v48dO0bfvn1ZsWIFjRs3ZsqUKQrwiLyBrKOMtGvXjnbt2pExY0a75U8agSc4OJhBgwYlUIvfHYMHDyYkJITWrVvbPg9bxQ3Axx2BZ9asWdSsWTOhmvxOGzt2LLGxsfTv35/p06dTp04d4H/n4t69e8yZM4f+/fvj6+urAI/EO8XzRERERERERERE5F+5desWP/zwA7Nnz2bChAn89ddftmVt27ZlzJgxnDx5kjZt2rB582YA27eIRUT+C86ePcucOXMoUKAAnTp1empwB6Bw4cLs3LmTwoUL4+zsTP78+W1TC8bGxr72TsSbN29y5swZli9fzr59+7h16xaAbQQIqxw5ctC/f3+qV6/OrFmzqFevHjExMa+1bSLycs6cOcPcuXPJnz8/HTp0eCy4Aw9H4DEMwzYCT7Zs2RgyZAjDhg1LgBa/nSwWy2Ov/fTTT8yYMYNSpUrRpUuXx+q+deQjgK+++oqvv/4aBwcHateuzerVq+Ol3f8lR44coUuXLkyaNInkyZOTJUsW4OEIR9ZrIHHixDRs2JC+ffty7do1WrVqRVhYmH5nkXij8I6IiIiIiIiIiIj8K97e3sycOZPq1aszfvx4xo0bZxfg6dixI2PGjOHUqVO0bduWLVu2AOhbrCLyn3Hp0iWOHTtG1apVCQgIeGJHb1yZM2dm69at7N69m+XLl5MuXTrMZjNOTk6vva0+Pj4MHDiQjh07YjKZ2LJlC3v37n3iujly5GDAgAGUKlWKsLAwwsPDX3v7ROTFXb58md9++43KlSuTPn36Z65rDfDMmTOHIkWK2EYlkae7cuUK8L8AVFxnz57l3Llz1KlTx25UyrgcHBwwm83AwwDPiBEjyJAhA3ny5Hmt7f4vypYtG4sXL8YwDM6dO2eb9tHJyck2ol3cAE///v359ddfGTBggIKpEm8U3hEREREREREREZEX9mjHhPXf6dOnZ+TIkVStWpWJEyc+NcBz8uRJateuzc6dO+Oz2SIiCcoaannatFLWzltrRzCAq6srHh4euLm5YRgGjo6Or7xdTxtNIEuWLDRv3pxWrVpx8uRJpk6dyqlTp564bvbs2ZkyZQrHjx/Hx8fnucEkEYk/169fB8Dd3R14OMpIXNbr9fbt27ZQdYECBdi2bRsBAQGPrS//s3fvXvLkycPYsWOBx0PpJ06cwDAMfHx8gKcf+wcPHthea9GiBUeOHLEFNuXfsx5nFxcXqlSpwtixY0mVKhVLlixh/vz5dsGduAGe+vXrM3PmTObPn68pISXeKLwjIiIiIiIiIiIiL8Risdg6Ju7duwfYT38VGBjIqFGjnhngGTRoELGxsc+cMkZE5F2TKFEiAJYtW8b58+dxcPhf90zcYE61atVo3rw5YN8R/DpGKjObzbbtxsbGcu/ePbuRc7Jly0a7du1o2rQpixYtYvjw4Zw8efKJ28qcOTPJkyfHYrHY7ZuIJCxvb28Ali5dyo0bN2yjjMDD2mMdMaZ06dJ07drV9j5nZ2eAeBnt621kNpt58OABf//9N8eOHbMbmcUauvHz8wOwTYH1pGMfExNDsWLFWLhwoe391vvF6whs/hc8GiCNe09yc3OjfPnyjB8/HoChQ4eyYsWKJwZ4PD09adSoEX5+fgpSSbzRJygRERERERERERF5IdaH37169WLAgAH8/fffgH2AxzoCT7ly5Zg6dSoTJkzgzJkztm0EBwdz8eJFUqdOrQfhIvJOedaIM0WLFqV69eocPHiQFStWcOvWLeBhaMZkMmGxWJg1axbnz5+Pl/poNpttHcOzZ8+mQYMGFC1alE8++YSxY8eyf/9+ALJmzUrnzp1p0qQJs2bNYsSIEU8N8AAK7oi8YT7++GPKlCnDDz/8wPTp07lz5w4mk4moqChb7Zk3bx5XrlzB3d1dn81ewJYtWxg5ciRFixblp59+YuTIkTg7O/Pjjz8C/wvdlCxZkjRp0rBixQrbdE0mk4nIyEjbZ+d58+Zx9uxZrl279tSR0OTFmc1m231o06ZNjB49mubNmzNx4kTb+UmUKBEVK1Zk6tSpXLlyhT59+rB8+XLblxSs5ykuBakkvpgMVQIRERERERERERF5QRcvXqR69eocOnSIvn370rRpU1KlSgVg97B7x44dVK9eHZPJRMOGDWnZsiUZM2a0rQevZyQJEZGEEDcMc+7cOa5cuYKzszPJkiUjXbp0AGzdupU2bdpw/fp12rVrx5dffmmriwsWLGDIkCE4OTmxadMmW119HeLW6i5dujBx4kQ8PT1JnTo1J06cwGw2kytXLrp3706dOnWAh9O/jBo1ipkzZ9KsWTM6duxItmzZXlsbReTfs46EtW/fPho2bMitW7do2rQpnTp1wtfXF4CFCxcyePBgnJyc2Lx5MylTpkzgVr/ZwsLCKFmyJGXLlmXGjBm20XX69u3L4MGDmTp1Ks2aNbOtv2jRIlq1akVgYCBt27a1W7Z06VL69u1LokSJ+P77723nRP6ZuCO/9ejRg4kTJ9pNSZYmTRq+/PJLhg0bBkBERATr1q2jZcuWpEiRgkGDBvH5558rhCoJSuEdEREREREREREReaonTYHyww8/MHDgQDZv3kyvXr1o3rz5YwEei8VC4cKF+euvv7h27Rp9+vQhJCRE31wVkXdO3Do5aNAgZs6cydmzZwHw8vIiJCSEhg0b4u3tTWhoKMOGDePIkSP4+/tTtGhR/vzzT44ePYqPjw9hYWEEBgbGy/RTEyZMoGPHjnTu3JnGjRuTI0cO9u3bx+rVqxk+fDhp0qRh3Lhx1KhRA4BTp04xevRovvnmG7p06cKwYcNU00XeApGRkaxfv56QkBCOHTtGYGAg+fPn58KFC/z888/4+vrGa+15W/31119UqFABLy8vhgwZQsmSJW3Lvv32W+rXr4+3tzchISE0bdoUgKtXrzJ//nwGDRrEnTt3KF26NB988AHHjx9n9+7deHl5sWvXLgICAnTsX5H+/fszcOBAGjRoQJMmTYCHv7sMGzaMa9eu0axZM7755hvg4bWxZs0a2rVrZxsB77PPPkvI5st/nMI7IiIiIiIiIiIi8kRxR5I4duwYsbGxvP/++wAcOnSIPn36sHXrVrsAT9wRHd5//33q1KnDpUuX6Nq1K/7+/gm2LyIir9KTOlm7devGqFGjKFeuHLVr1yY8PJwVK1awZ88eateuzYgRI/D19eXo0aNMnz6dZcuWcefOHbJkyUKRIkXo27cvadKksau9r4NhGNy4cYOqVaty/fp1vv/+ewIDA+3WmTx5Mu3ataNYsWLMnj2bDBkyAPDHH38wa9Ys2rRpo5oukoDi1okXqRkxMTGcO3eOvn37snfvXs6dO0fOnDkpVKhQvNWet93OnTupWLEiPXr0oFevXsDDwGa1atXIkSMHmzdvpmbNmiRKlIgBAwbYAjy3bt3i0KFDdO3alT///JO7d++SPn16ChQowMiRI/Hz89Oxf0V++uknKlasSN68eZk0aZLdve348eOUKVOGS5cuMWDAAHr37g08vDaWLFnC8OHD2bx5M6lTp06g1osovCMiIiIiIiIiIiJPELcTYeTIkcybN48HDx6wdu1a21QpcQM8PXr0oFGjRraH5AsWLKBPnz4sWrSIwoULP7ZNEZG30a+//kry5Mkfm9ZqxYoVNGrUiC+++ILu3bvbpsMaM2YMQUFBZMmShYMHD+Lp6Wl7z/Xr14mKiiJ16tSYzWacnZ3jrU7+9ddf5MmTh9KlS7Ns2TLAfjqtBw8e0LlzZ7755htWrVpFlSpVbO+1tlE1XSThDR06lOzZs1O1atUXHrnlzp073Lhxg4CAAMxmMy4uLrqenyEyMhI3Nzf27dtH2bJlKVy4MJs2baJLly6MHTuWUaNG0b59e9u0h7Vq1SJRokT079/fbpqs8PBwbty4wcWLF8mePTvu7u64u7vr2L9C69ato1KlSsydO5f69evbrgnrMf7xxx8pV64cgYGBrF271nYvj4mJITY2VudDEpzG3hIRERERERERERE7hmHYHlp36dKFPn364O/vz+TJk8mWLRvW7wN++OGHDBgwgNKlSzNs2DDatm3LvHnzCAkJISQkhMSJE9uCPoAehIvIW23jxo0UK1aMefPmERMTY7dsz549ODg40KxZMzJmzEh0dDTLli1jwoQJZMyYkd27d+Pp6UlsbKztPd7e3qRNmxYHBwecnZ2B+K2TDg4OXL58mZiYGMxmsy24A+Du7k7p0qUB2LRpExaLBYvFYtdG1XSRhPXzzz/Tq1cvNmzYAPDc4I71Gk6SJAnp06fH0dERFxcXQNfz02zcuJGBAwdy+/Zt3nvvPapWrcqWLVvIkiULY8eOpXfv3lSrVg0nJycAypYty9KlS4mIiKBv377MmDHDti1PT08CAwP56KOP8PHxwd3d3e4zt/xz1p/t8+fP2/6MG0h1dHTEMAxy587N559/zuHDh/n5559t73d2dsbd3d22rkhCUXhHRERERERERERE7FgfdH/99ddMnDiRVq1aMWHCBD799FPbcmuAJ1++fAwbNoyvvvqK9evX07BhQwYNGoSLiwtr1qzB29vb9kBdRORtdfDgQerVq0fu3LkpWrSoLWwDEB0dzc6dO8mYMSP58+cnNjaWlStXEhQUhMlkYu/evSRPnhyA3bt3s2vXLiB+Ogjj1t8HDx7Y/p48eXI++ugj9u7dy7Zt22wdm4Zh2AJGxYsXBx4GeRwcHF5oRA8RiT+pUqUib968hIaGsn///ueuH/cajhvWkyfbsWMH5cuXZ9++fdy4cQNPT0/mz59P6tSp+fPPP8mcOTMNGzYkMDDQLtAZN8ATEhLCzJkzbcviBkpA5+GfevR3C+vP9kcffUSiRIk4cOAAFosFk8lkt66TkxMFChQAHo5AJfKm0SctEREREREREREReUxkZCRLly4lffr0tG7d2jYFjFXcAE+uXLmYPn063333HRMmTGDGjBns2rWLwMBAzGazOnxF5K1mNptZvHgxJpOJoKAgPvroI+DhN/utU86kSpWKK1eucOzYMdatW0f37t1xcHDgwIED+Pr62rYVFBRE586diYiIiJd2W+vv4sWL6dmzJ/PmzQMgceLEVK1aFYAvv/ySvXv3YjKZMJvNODk5ERsby4IFCwB47733AGw1X0TeDKlSpaJDhw7cvXuXffv2AY+HGuSfOXfuHO3bt+fDDz+kT58+ts/BS5Ys4fLly6RKlYqTJ0/Ss2dPoqKicHZ2thtZLW6AZ8CAAUycOBFQWOdViHtv27lzJ3PnzrX93KdNm5aSJUuyevVqBg8eDDwM9sQNTR05cgQPDw8CAgISZgdEnkG/NYuIiIiIiIiIiMhjrly5QlhYGCVLliRz5syYzebH1nm0A6JSpUq0bduWxo0b4+vri8Vi0dDzIvLWs1gsnDp1ioiICHLmzAlA48aNqV+/PleuXAEeftv/0qVL9OvXj/bt2+Po6PhYcGfcuHGcP3+eWrVq4ebm9trbbK2/PXv2pFWrVqxfv96ug7lJkyYEBQVx48YNypQpQ2hoKBcvXgRg4cKFTJ8+nRw5clCxYkVAnc4iCeXR4Fzcf5cqVYocOXIwZswYLl26pMD0K3Lt2jXOnj1LmTJl+OSTTwDo06cPhw4dYvny5WzYsIGyZcuybNky6tatS1RUlC34aFW2bFmWL1/O+fPnmTNnDvfv30+o3XlnxL23DRgwgHr16tGtWzfb1HHJkiWjc+fO+Pr60q9fP4KDg7l69art/rVq1SrWrl3Lhx9+SI4cORJsP0SeRhVcREREREREREREHuPg4IDJZOLChQtPDOFYv+F68eJFtmzZ8tRtiIi8rX755RdOnjyJs7MzX3zxBbGxsQwbNowmTZowZ84ccuXKZauNjRs3plixYixfvpzw8HDWrl1rF9xZsmQJU6ZMIUOGDDRs2PC110fr9vv27cvw4cP58ssvWbZsGXXq1MHJyckWyBwxYgS9evXiwYMH1KlTh7x585IuXTqaNm1KZGSkbT80modI/DObzbapfwDbiF1xpwJKkyYN5cuX5+LFi2zevBnQ6DuvgslkwsXFxXZMu3TpwuDBg/Hx8aFEiRK89957LFq0iJIlS7Jq1aqnBnhKly5NWFgY3333HR4eHgm1O+8M672tW7duDBw4kKJFi7J69WpbyBSgRIkSzJkzBz8/P4YNG0bJkiWpWbMmFStWpHnz5sTGxjJ37ly8vLx0rcgbx2RonEMREREREREREZH/LIvF8lgnsmEYREVFkStXLu7fv8/ChQspUaKErfMo7tDzjRs35vjx46xZs4bkyZPHe/tFRF6HTZs2Ub16dYKCgujWrRvh4eEMHTqU8ePHA9CqVSuCg4NJmzYt8LCWLl++nFGjRnH8+HHatm1LsWLFCAgIYPbs2SxatAgnJyd2796Nv7//E2vvq7Z3716qVq1KyZIlGTFiBP7+/nbLzWazLXz03XffsWvXLrZu3Uq6dOnIlSsX7dq1I1WqVHbricjrtX37dg4ePEi3bt2A/33m6tmzJ8ePH6dKlSo0atTI7j2XLl2iQIECZMmShW3btiVEs985UVFRBAUFMXnyZNKlS8f58+fp3bs3TZo0ISAggNjYWJycnLh16xa1atVi69atfP755yxatAhXV1fb8rhUS1+NJUuW0LRpU+rXr0/37t0JDAy0LYv7O8q+fftYvHgxq1ev5tKlS/j7+/Phhx8yatQo0qVLp/MhbySn568iIiIiIiIiIiIi76K4D61Xr17Njz/+SKdOnfD29sbNzY127doRFBTElClTSJ8+PQEBAZhMJttD8UWLFrFlyxYqVKiAp6dnQu6KiMgrc+jQIerVq0f+/PkpVaoU7u7uuLu7c/bsWUwmE4ZhcPXqVRIlSmR7j4ODA59//jlubm5MnDiR4cOHM3z4cAASJ05MgQIFmDVrFv7+/vHWYXjs2DGuX79Oo0aNHgvuADg6OtpCRFWqVKFKlSo8ePAAd3d32+vq3BSJH4ZhcPfuXb788ksuX76Mg4MDQUFBmEwmjh07xk8//cSmTZtYvXo1M2fOpEqVKnz55ZekTp2aFClSULJkSRYsWMDSpUupVatWQu/OW81sNuPq6srEiRNZt24d58+fJ126dNSsWdMuuGM2m/H29rYdc+sIPIsXL8bFxeWx+qla+mps374dZ2dnmjVrZhfcAWz3aJPJROHChcmXLx8hISGcO3eOtGnT4uXlhbu7u+5t8sbSyDsiIiIiIiIiIiL/QXFHfejduzczZszg6tWrhIaGUrNmTQBOnTpFcHAwy5cvp3z58jRs2JBy5crh6OjIvHnzGDt2LPDwIXqaNGnsvu0qIvI2MgyD3r17M2XKFObMmUOVKlUA2L9/P82bNydnzpw4ODiwePFi6tevz+DBg/Hz87PbRlRUFCtWrODKlStERkZStGhR3n//fZIkSRKvHYbdu3dn5MiR/Pbbb2TPnv2xGm1ty40bN0iWLJnda6rnIgljy5YtNG3alHPnzjFs2DDbCDzwcCq/uXPnsn79ek6cOEGqVKlo3LgxNWvWxN3dnRw5ctCqVSsmTpyYgHvw7pgzZw6NGzcmY8aMnD59mtKlS7No0SKSJ09uq5XWP+OOwFOyZEnWr1+Pi4tLQu/COyciIoLcuXOTJEkSDh48CPDY/ep59y/d3+RNpvCOiIiIiIiIiIjIf1hwcDAjRoygcePGtG3blvfff99u+ZEjRxg9ejQrVqwgIiKCjBkzEh0dzdWrV/H392fjxo0EBgbqG6wi8k4wDIP69euzePFifv31V7Jnz06jRo04fvw4kydPJmvWrNy+fZv+/fszc+ZM6tevz9ChQ0mTJo3t/U/rFIyPqbLiGjBgAP369WPChAm0bt3a7v+2ttMwDOrWrcsXX3xhCyqJSMKwXpc7duygbt26XL58mUGDBhEcHGxbJzY2lujoaMaPH09YWBibN28GoEaNGmzbto07d+6wa9cuChUqlFC78c7YuHEjN27coHDhwgQHBxMaGsonn3zC0qVLSZYs2RMDPGXKlOHMmTOcOHFC08m+BlFRURQoUIDz58+za9cucubMabfcep+9ceMGY8aMYdCgQQrqyFsl/j4lioiIiIiIiIiIyBtlw4YNTJ06lbp16xIcHGwX3LF+5+/9999n8ODBLF68mNKlS+Pt7U327Nnp06cPO3fuVHBHRN4pJpOJOnXq4O7uTt++fWnQoAFz584lX758+Pn5kThxYvz8/AgKCqJJkybMnz+fnj17cunSJdv7n/ad6fgM7gBUq1aNFClSMH/+fC5fvmx7PTY21tbOkSNHsm3bNmJiYuK1bSLyOOt1Wbx4cRYtWkTq1Knp3bu3bQo+eBhOSJQoET179mTjxo18++231K1bl127dnHz5k3MZjPr16+3rSsv5knH6tNPP6VatWqkT5+euXPnUr16dbZv306tWrW4ceOGXXDHOoXW1q1b+f3330mePLmO/2vg6upKmTJluH37NmvXruXBgwe2ZWaz2XafHTVqFAsWLODo0aMJ1VSRf0Qj74iIiIiIiIiIiPxHDRo0iMGDB7NlyxaKFCny3PUNw8BisdgFdRTcEZF3zZ07dxg7diyDBg3CYrHQrFkzQkJCSJs2rd3oOSdOnGDkyJFPHIHnTXDr1i26d+/OjBkz+PTTTxkzZgwZM2a0TeWyYsUK+vTpg4+PD6tXr8bHxyeBWywi8OQReIYOHUr37t0BiImJwdnZ2bZ+REQE165dY/z48axbt47o6GgOHz6Mt7d3Qu3CWyXuZ9kjR45w7do1bty4wQcffECGDBlsxzomJoa6deuyYsWKZ47AA/E/0tp/gfW62LdvH40aNeL27dtMnTqVkiVLkiRJEtt6y5cvp3v37uTKlYsFCxaQOHHiBGy1yMtReEdEREREREREROQ/xmKxYDKZKFmyJL/88gvHjh0jZcqUGIZh19Fg7Xh4UqfEs6aGERF529WoUYNVq1ZhMpkoVaoUc+bMIXXq1ABPDfA0atSIfv36kS5duoRsup2zZ8/SoUMHVq9eTY4cOShevDgff/wxGzduZOPGjZhMJvbs2UNAQIA6m0XeENbPac8K8DwpKBIdHc2QIUMYMGAAEyZMoG3btgm5G2+FuHWvf//+zJo1i/PnzwOQIkUKypYty9SpU/Hw8AAeD/AsW7YMHx8fhdnjUWxsLFOmTGHgwIG2qS4rVapEhgwZmD9/PnPmzMFsNrN79278/Pz0O4u8VRTeERERERERERER+Y9q2bIl06ZNY+PGjZQpU8ZumbUz4/bt27Rr144pU6bg6emZQC0VEYk/4eHhVK1alVSpUpE4cWJmzpxJxYoVbSPXwOMBnjFjxjBt2jTat2/PmDFjXmsI5kU7ia0dlufPn2fy5MmsXLmSU6dOAZA4cWKKFi3KN998Q7p06dTxLJJAHg3NRUdH20bHsgoLC6NevXqPBXjivtf69+vXr5MuXTpq1qzJvHnz4m9H3nI9evRgxIgRVKpUicqVK5M6dWpGjBjBzp078ff359ixY7i5ueHg4GAX4MmdOzdbt27VKEevwIsESK33tZiYGGbPns3MmTM5ePCgbbmTkxMffPABy5Yt09S+8lZySugGiIiIiIiIiIiISPx49JunefLkAWDMmDGkT5+eTJkyAfbTMcyYMYOFCxfy5Zdf8umnn8Z/o0VEXrNHa6OnpyffffcdAJGRkSROnJjx48cDMHbsWDJkyICDg4OtozFLliy0b98eLy8v2rZt+9pHr7F2RC5dupRatWo9cR8ATCYThmGQLl06+vfvT69evdi3bx9ms5ksWbKQKlUqPDw81LkpkkDiXnvz5s1j586dHDx4kJw5c/Lxxx/TsmVLAEqUKGH7LNazZ08Aunfvbjc6orXuREdHkyxZMm7dukVERASJEiVKmJ17i6xfv57JkyfTpEkTevbsSYYMGQC4ffs2u3btIjw8nJiYGBIlSoTFYsHZ2ZlFixZRsWJFfvzxR2JiYhJ4D95+cUf/PHPmDBkyZHjivclkMtnOQdOmTalQoQKrV6/mzz//xDAMPvroI4oXL243nZnI20Qj74iIiIiIiIiIiLyjnvcNVovFQs2aNVmzZg1t2rShefPmZM+e3bZ8xYoVBAcHkzZtWlauXEnSpEnjodUiIvHn0c69iIgIXFxccHL633ef//zzTyZNmsT48eOpWLGiLcAD9qGZJ00x+LpMmzaNli1bsnDhQurUqfPc9Z82bYimExFJGHGvvaCgICZNmkTSpEkJDAzkzz//5Nq1azRu3Jju3buTKVMm2xRa9erV49KlSwwfPpyuXbvabfP+/fvMmTOHdu3aERISQr9+/RJgz94+AwcOpH///uzfv598+fJhNptZsmQJffr0AeDgwYMkS5aMBw8e4OrqavtsHRMTw507d0iePLmmHXxF+vTpw+LFi22jxD3N8+5dOh/yttJPrYiIiIiIiIiIyDvIbDbbHlpv2bKFb775hq5du7Jr1y6uXr0KPPz2alBQEEWKFGH8+PHUqVOHb775htWrV9OhQwc6duzIgwcPmDNnDkmTJsVisSTkLomIvFJxQzYzZ86kXr165M2bl7JlyzJ69Gju3LkDQPr06WnXrh0dOnRg3bp1dOrUiTNnzgD/G90G/jciTnx80986fdfWrVsBnlufn9bJqeCOSMKwXnujRo1i3LhxNG3alI0bN7J//342bNjAJ598wqxZs5g2bRoWiwXDMChevDgLFy7E39+f7t27M3nyZLttRkVFsWbNGmrUqGEL7mgMh2ezWCz8+uuveHt7ky9fPiwWC8uXLyc4OBjDMPjhhx9IliwZAGfPnuXrr7/GbDbbRn9RcOfViYqK4sSJE5w5c4YlS5Y8c93n3bt0PuRtpZF3RERERERERERE3jFxOxF69+7NlClTuHv3LoZh4OTkRLVq1ejevTu5c+fGbDZz5MgRJk6cyJw5c2zb8PLyIk+ePMyZM4eAgAANPS8i75S439rv0qULkyZNwtfXl2zZsnHmzBn++usvatasSZs2bShWrBgmk4lz584xfvx4xo8fT5UqVRg2bBiZM2dOsH2oXbs269evZ//+/eTMmTPB2iEi/8y1a9coVaoUSZMmZebMmWTOnJnY2FjWr19Py5YtcXd354cffiB58uR279uyZQs9evRg5cqV+Pv72y27cOECfn5+gEYfeVGNGjUiNDSUX3/9lZ9++onOnTvj4ODAgQMH8PX15f/Yu++oKM63jePfXaoKSBM7Yu89GnvXWGLFHo29o2IDFARRsSuKvRdExYq9o9gQsXdjjb2Cnbq77x+enXcRzU8TkUjuzzk5UWZ2fGbXuWfd59r7gQ/3jJo1a2JiYsKGDRuwsrJK5VGnTefPn6dSpUq0bduWRYsWpfZwhPjupGILIYQQQgghhBBCCJGG6HQ6ZaLG09OTcePGUatWLUJCQnjw4AFt2rRhw4YNDBw4kFOnTmFkZETp0qVZsmQJO3bsYOXKlUyZMoXt27cTEhIiwR0hRJqkD+5Mnz6dGTNm0LNnT3bu3Mm+ffvYtm0bzZs3Z926dQQHB5OYmAiAo6MjAwcOZPDgwWzatInx48ej0WhS7Rxq1qzJu3fvmDdvHgkJCdJhQ4gfzKNHj7h48SJt2rQhf/78xMfHs379evr374+ZmZkS3ImPj+fmzZvK4+rUqcOxY8dwdHRMVoP0wR3D94Pi0/Q1s0mTJsTGxtKzZ0/c3NwwMjIiPDxcCe4AzJkzh6tXr1K1alUyZMiQWkNO07RaLXny5FG6ToWFhaX2kIT47oz/9y5CCCGEEEIIIYQQQogfhX5COigoiMWLF9OrVy9cXV0pWLAgsbGxnD17FmNjYw4fPsyAAQOYNWsWpUuXBqB+/frJjqfVaiW4I4RIc3Q6HS9fvmTVqlWULVuWAQMGkD9/fjQaDdeuXSMiIgInJyd8fX0xMTFRHufo6EifPn2wsLCgc+fOKVYfdTrdZ+tvYmIixsbG9O7dmxUrVrB3715iY2OxtLRM0lFICJF6Pr4WPw5C63Q6JXij3y8kJAR3d3el64u+405CQgLNmjVj7NixNG3aFECpS5+rQVIH/jf9c1SqVCkqVapEaGgoVlZW3L17N0lnneDgYKZPn07OnDnp3bu3vC/+m/SdoD71pQB92MzCwoK2bduyfft2du3aRfXq1eVLBOI/RSKXQgghhBBCCCGEEEKkMa9evSIkJAQ7Ozv69u1LwYIFefPmDaVKleL58+cEBgbSuXNnwsPD6d+/P6dOnQL4ZNcG+da2ECItUqlUPHv2jJMnT9K0aVOl68W6detwdXXFxMREmTxPSEjg1q1bymNz586Nl5fXJ7te/BMXL14kMjJSGZ9+snLMmDEEBwdz9+5dAIyNjdFoNGi1Wpo2bcoff/zBzJkzlccJIVKXVqv9bHBn9erVvH37FpVKhZWVFUZGRuzfv5+FCxcybNgw1Go1ERERSbq+eHl58fDhQ2xsbJSfybX+7eTOnZtJkybh5OTE69evGTZsGGvWrOHkyZO4uLgwdOhQYmJi2LBhAw4ODmi12tQe8g/l4cOHAMmCOwcOHOD9+/fK9aLvcteqVSuqVatGUFAQz58/l+CO+E+Rf3kLIYQQQgghhBBCCJHGaDQaMmXKxMiRIylevDgxMTHUr1+fqKgoxo4dS4sWLZg2bRoFCxYkMjKSIUOGEBkZKRNBQoj/FGPjD4sT6IOLf9X1olKlSqxZs0Z5rD7Y+K0mFW/fvk2JEiUYPnw4J0+eVH6+Y8cOfHx8aNeuHQ0aNMDLy4vHjx8TFxeHWq2mRYsW2NjYsG3bNt68efNNxiKE+Gf09aF27dqMGTNGqRP9+/enW7du7Ny5E61WS968eenVqxchISEMHToUlUrFyZMncXBwAD7UpqCgILZs2UKtWrUoU6ZMqp3Tj+jjcOXnlhbU6XRUqlSJ4OBg6tWrR2BgIO3bt6d8+fIsXbqUYsWKER4ergQ2Jdj+5Y4cOULp0qWZMWMG8P/3TD8/P2rXrk2VKlUYMmQIDx8+VF4vY2Njateuzf3795kzZw5arVaWhRT/GVJdhBBCCCGEEEIIIYRIY2xtbfH09KR169ZotVpmzJhBZGQkAwcOpH379qhUKjJkyECmTJmwtbXl0KFD+Pn5Kd94FUKI/wITExMyZszIjh07mD59+ie7Xuh0OsaMGUNsbCzW1tYpNhZra2tcXV05dOgQY8aMUTrwNGzYkIMHDxIQEMDr168ZN24cP//8M926deP06dPkz5+f4cOHc/z4cfbu3Zti4xNCfJ1Tp05x/fp1fHx8WLp0KYMGDWL27Nn069ePypUrKwGQ1q1bU7lyZd68eUPLli2V5bC0Wi3z58/Hx8cHtVrN9OnTsbCwkBDDV9AHRcLCwoDPdytSqVTodDrKlStHYGAgERERLFy4kKVLl3L8+HHWrl1Lzpw5Zfmmv0Gr1RIdHc2kSZOYO3eu8vMKFSowbtw4YmNjmTFjBmXKlKFfv37s3bsXtVrN4MGDKVq0KLt370atViuvkRBpnUonf9OFEEIIIYQQQgghhEjTnJ2diYiI4O7du0m+LVyhQgX69u3Lo0ePaN26Nblz507FUQohxPfn6+uLr68v6dOnx87OjnPnzikhHZ1Ox5o1a/Dy8qJ48eKsWLECKyurFBvLy5cvmTRpEhMmTKBx48Z4eHhQsWJFZfudO3cIDw9n8eLFhIaGki5dOurXr4+5uTk7duygSpUqrFixIsnSOkKI1HPgwAE8PT05fvw4AO7u7ri4uJA9e3a0Wq3ynmzDhg1MnTqViIgI8uTJQ/Hixfnzzz+5fv062bJlY9euXTg5OUl45G8YNWoUo0eP5ujRo0nq6dfS6XTSofJvOnz4MC1atEClUuHj40O/fv2UbW/fviU4OJgtW7awdetWABo3bky9evW4desW/v7+LFiwgO7du6fW8IX4roxTewBCCCGEEEIIIYQQQoiUodPpePXqFX/++SdxcXHcuHGDAgUKALB8+XJu3LiBnZ0dv//+OwCJiYnKMjJCCPFf0KpVK86dO0dISAjOzs68evUKa2trEhMTmTNnjrLUx6xZs7Cyskoy4f6tWVtbM2zYMAAmTJgAgKenJ+XLlwfAyckJJycn2rVrx9atW9m3bx9LliwhMTGRuLg4zpw5w5MnT7CxsUnRcQoh/po+6FGzZk1sbW2VriHp0qUje/bswIeltfTXqbOzM7lz52bXrl0sWLCAY8eOkSdPHgYOHIiLiwuZM2eW4M7flCVLFgDOnDlDxYoV/3ZtlODO31e1alU2bNiAs7Mzvr6+AEqAx8LCgm7dutGtWze2bdvG/v37Wbp0Kdu3b0elUqFWqzl48CC//fYbZmZmcl8TaZ503hFCCCGEEEIIIYQQ4gfztd/+1X/ruF27dnTp0oVTp06xaNEizM3NOXDgAPb29ik4WiGE+Hc7cOAA06dPZ+vWrdjZ2VGyZEkeP37Mn3/+iaOjI9u3b/+uXS+io6OZPHmy0oHHy8uLcuXKARAfH4+pqamyb0REBBEREaxcuZKTJ0/SqVMnlixZIhPNQqQyjUZDQkIC9evXx9bWlps3b3LhwgX8/f0ZOHCgst/HYZLXr1+jUqmwtLRU3u9JcOfve/jwIfXq1SMmJoaIiAh5z5uKDh06hLOzMyqVCl9fX/r06QNAQkKCslwcwM2bN1m9ejW7du3i2LFjmJubc/jwYcqWLZtaQxfiu5HwjhBCCCGEEEIIIYQQPxDDSZ6oqChsbW0/u69+0ufly5cMGDCAlStXKtuKFi3K1q1bcXJykg4NQog0xbCmvXnzBktLy0/uZxiEvHfvHlu3bmXx4sVER0eTO3duatWqRY8ePXBwcPguk+eGf8ZfBXi0Wi0qlSpJQOfNmzdUqVKF169fc+jQIXLmzJmiYxVCJPepcHVMTAxarZaTJ08yZMgQTp8+zfTp0xkwYACQtF7pOyDqa4Es1fRtDBo0iBkzZjBv3jx69uwp73tT0ecCPPq/+/rXRqfTodVqmTZtGu7u7nTo0IEFCxZgZmYm14RI0yS8I4QQQgghhBBCCCHED2jYsGHExMQwa9asL56EWL58Offv38fe3p4WLVqQKVMm+Ta3ECLN8vLyInPmzPTv3/+zte7jyfH4+HhUKlWSLgApNdH7v4774sULpk6d+skAj+G49ZOeS5cupVu3bixYsIDu3bt/8/EKIT7PsMbEx8cTGxtLYmJikpD1jh078Pb25vTp08k68OzevZsjR47g4eFBhgwZvvv4f2Qf13d9bdX//PHjx/z000+ULFmS7du3p+JI/xv+173tcwEew8cZ/rpWrVrcu3ePCxcuYG5unvInIEQqkgWshRBCCCGEEEIIIYT4wbx8+ZKNGzdiamqqLKHyJd/O7tSpU5Lfa7VaCe4IIdKk69evM336dHLnzk23bt1Inz79J/f7uG4aGxsrE4b6upoSwR3DyeZ9+/Zx7do1rl27RrVq1ShSpAhFihTBzs6OQYMGATBhwgQAJcCjUqmU8Rkbf5jqyZo1KwDPnz//5uMVQnye4fU8f/589uzZw6VLlzA1NaVDhw7UqVOHMmXK0LBhQ1QqFSNHjmTQoEHodDo6dOjA4cOH8fT0JCYmhoEDB0p453+4ffs2ZmZmZMuWDSDJc//TTz9RokQJ1Go1RkZGaDQaMmTIQNWqVQkODmb9+vW0bNkyNYefphleC9evXyc6OprY2Fhy586tdISrVq0aGzZswNnZGR8fHwD69OmDWq1WQjtqtVpZTqtixYocPHiQyMhIqlatmmrnJsT3ID3BhBBCCCGEEEIIIYT4wVhaWtKqVSuuXbvGzJkzgeQT0F9ClgwQQqRV+fPnp2nTply6dInQ0FDgQ2DxfzGsiym1NIdhcNLDw4OWLVsyaNAg5s+fT+vWrWnVqhUrVqwAIFOmTAwbNgwPDw+2bt3K2LFjOXnyZLLxvXr1ivDwcNRqNXZ2dikybiFEcjqdTrmehwwZQr9+/Thz5gy5c+fGxMSEESNGMHToUGXp0gYNGjB+/Hh+/vlnBg8eTNmyZencuTOvXr1i//792Nvbf1Gt+q+6ePEiBQsWZNq0aTx69Ej5+bJly+jTpw+VK1fm999/V2oofHjf3KtXL9RqNfv37wc+vG7i2zK8t40dO5ZGjRpRoUIFatSoQZ06dXB3d1f21Qd4dDodPj4+zJ07F0BZMgvAxMSE9+/fExUVhZmZmYTaxH+C/OtcCCGEEEIIIYQQQogfjJGREb1798ba2poDBw6k9nCEEOJfRT/x7ebmRoYMGVi9ejXw7wks6scxatQoJk2aRNOmTQkNDeXWrVvMmzePW7du0blzZ2Wy38bGRgnw7Ny5kyFDhnDu3Lkkx3zy5AmTJ0+mfv369OjR47ufkxD/VfoQ3ezZs5kxYwZ9+/Zlx44d7Ny5kx07dtC5c2cOHjzI1q1biY2NBaBu3bpMmzaNQYMGkS1bNho3bkxERAR58uRBo9H8a2rVv1FiYiLVqlVj7ty5zJ49m4cPHwIfuksuXryYjh07snHjRjp37kytWrWYOHEiT548oVq1akow8syZMykWzvwv0/+9dXNzw9vbmzx58jB16lQWLVqEWq1m8uTJ1KhRQ9nfMMAzduxYpk6dCiQNpq5bt4758+fTpk0bypQp813PR4jUoNJJtFAIIYQQQgghhBBCiH8lfev4j3+vb0nfr18/5s6dS0hICE2aNEnFkQohROr4uE4aevHiBe3atWP//v3s2bOH2rVrf+fRfd6pU6do1KgRFStWZOLEiRQoUICEhATCwsJwdnYmS5YshIeHY2trqzwmKiqKUaNGsXHjRs6cOUOmTJmSHPPIkSNUqVIF+OvnRQjx7eh0Ol6/fk2zZs14/vw5GzZsoECBAmi1WtatW8fw4cPR6XScPHkSOzs7ZSkg+HCdxsbGolarMTc3T7LkkPi8c+fO4e3tzY4dO3B3d6dXr17KkkwAERERbNy4kfXr13P79m1y5MhB7969uXDhAps3b6Z///6MGzcuxZZF/C/buHEjHTt25Pfff2fYsGHkyZMH+LCkWZ8+fbC2tubPP//E0tJSeczhw4epXr06xYsX5+jRo1hYWCjboqKi8PHxUTqNyr1NpHXyt1sIIYQQQgghhBBCiH+ZxMREdDqd8uH0rVu3knxYrZ/Y+fXXX4EP30qNjY2VZRaEEGleYmIi8GECz7BOvn37VqmBGo0GADs7O1xcXNDpdBw5cgT49yyVcu3aNZ4+fUrfvn0pUKAAiYmJrF+/nm7dumFra8vRo0extbUlLi6Op0+fAmBra8uYMWO4dOkSmTJlSlbzJbgjRMo5c+YM58+fT/ZzlUpFVFQUkZGR1KxZkwIFChAfH8+6detwc3NDp9MRGRmpLGd3//59Xr9+rTw2ffr0mJubJ1l+S3yavn6XLFkSX19fGjZsyMSJE5k/fz73799X9vv555+ZOHEi586dY+LEieTPnx8fHx927txJbGwsoaGhJCQkJFmiSXwbx48fB6Bnz57kyZOHxMREVq1axcSJE8mTJw/Xr1/H0tJS6UIFULVqVY4dO8b27duxsLBQXpPExERsbW0luCP+U+RvuBBCCCGEEEIIIYQQ/wJ79+6latWqaDQajI2NlUlZLy8vSpcuTceOHblw4QLR0dHKYxo0aEDz5s3Ztm0b9+/fl0kIIUSatnfvXrp06cLz58+TTOANGTKEkiVL4uPjw82bN5NMgFepUoXy5cszffp0bty4kSpLpXwqWHn9+nUAnJycSEhIYP369Xh4eKBWqzlx4gT29vYAPHv2jMmTJ3Pnzh0AMmbMSMaMGZMElz4mk5tCfFvXr1+nbNmy/P7771y4cCHZdrVajUqlIl26dMCH7iPu7u7Jruc3b97wyy+/sHXrViDp8kCyjNP/ZvgclSpVKkmAZ968ecoSWvAh+GFhYcGwYcPYv38/gYGBtGzZkixZsnDq1CkCAgKSHVN8nY//zREfH8/p06fJli0bpUuXRqPRsGHDBqX7VHh4uBJiu379urKkJUCFChXIkSMHGo1GeU2MjY2THF/ubeK/QP6WCyGEEEIIIYQQQgiRyjQaDStXruTo0aPUqVNHWTbh+fPnODg4kDt3blavXk3FihVp3749Gzdu5M2bNwC0bt2aV69eMXnyZOLj42USQgiRZvn5+REUFMTQoUN58eIFKpWK58+f8+7dO9KlS4efnx9lypRhwIABbNu2DfjQrUZfJ3fu3Al8OkzzrXx87MTERGXC8Y8//lC6AhUoUACAs2fPcvDgwSQT/YbLYbm6urJu3bpkk5ZS64X4fvLnz0+XLl04f/48ffv2VTrw6HQ6NBoN5ubm5MiRg5UrVzJ16lTc3d1RqVREREQkuZ6nTJnC48ePkywLJL6evo5+HOCZM2eOEuAxDMIDtG3blvnz53PgwAHs7OwIDQ2VjpVf4ePnKi4uTrkPvX37FvhwXzIzM+Pdu3c8efKEDRs24ObmluzeptPp6N27Nxs2bODdu3dJjivdp8R/nYR3hBBCCCGEEEIIIYRIZUZGRkybNo3OnTsTFhZGjRo10Gg02NvbM2DAACIjI9m6dStt2rThwIEDtGzZknr16jFlyhRq1apFwYIFOXPmDDExMcC/Z1kYIYT4lg4cOECtWrVYsWIFrq6uPHv2DHt7ewICAjh37hz+/v7UrFmTWbNm0aRJE5o1a8batWtp164dJUqUYPXq1Sm+7Ib+2C4uLhw6dEjpHNCzZ0969erFrVu3AChatCj29vZ0796dzp07Y2JiQnh4eJKJ/gULFhAZGUnDhg3JnDlzio1ZCPF5+qDI4sWLcXFx4ejRo/Tt25dz586hUqkwMjIic+bMdOzYkUePHuHj44NWq+Xq1as4ODgAH4IPwcHBrFixgmrVqlGjRo1UPKMfz8fBEcOAR6lSpfDx8flkgEdfjw3fFxcsWJC2bduyZ88eDh48mPKDTyP0z+WmTZsAMDMzA8Db25tp06bx5s0bTExMaNKkCY8fP8bV1ZURI0agVqs5fvx4knvbzJkz+eOPP6hQoYLSrUoI8YGEd4QQQgghhBBCCCGESGVarRY7OzumTp1Kx44dOXr0KNWqVVMmjExMTGjUqBGLFy9m7969TJgwgTt37uDm5kbVqlV5/fo1J0+eZNGiRYB0ZBBCpD2JiYmoVCr27dtHtWrVCAoKYtCgQTx//hxTU1OMjIwYOHAgISEh7N69m+7duxMeHk7btm2pUqUKT58+5fjx4yxcuDDFx7po0SLmzJmDh4cHN2/exMPDg0WLFlG8eHFsbGwAKFGiBL169eLNmzc8e/aM2bNnkyVLFuUYQUFBTJkyBWtra0aOHImZmZkEM4VIBUZGRsr7sYCAAFxcXDh27Bj9+vXj3Llzyn5DhgyhY8eOvH//ngIFCvDgwQO0Wi0ajQZ/f39GjBiBTqdj3rx5ZMyYUbq+fCGNRqMER06ePMmmTZuYN28e586dU5aSLVOmDN7e3p8M8MD/vy/Wh35KliwJwIsXL77nqfzwWrVqhbOzM3PmzAFg0KBBjB07FiMjI+U5Ll++PAULFiQ4OJjo6GiuXr2aJHy6du1aAgICyJs3L506dZKlsIT4iEon7/aEEEIIIYQQQgghhEh1+m4Q0dHRuLq6EhgYSKVKlQgLC8PIyIi4uDjlW64ADx48YN++fQQFBREWFkZCQgKVK1dmw4YNZMqUSQI8Qog0JzExUelkU6NGDQ4dOkT79u2ZMWMGdnZ2ypKDAAkJCTx79gx/f39OnjxJWFgYAB07dmT58uUpPlYvLy/GjRtH5syZefLkCd7e3nTv3p0cOXIkGWfv3r1ZsGABVlZWdOvWjWzZsnH06FEOHTpEhgwZCAsLw8nJKcljhBDfn+E1OGDAAGbNmkWlSpWYPXu2Ega5ffs2o0aNIjAwEGNjY4oVK8aLFy948uQJBQoUYMuWLXI9fwXDTmkjR45k4cKFPH36FPjQCcbZ2Zl+/fpRrVo14MMyhD4+PuzYsQN3d3f69etH1qxZkxzz1atXjBkzhoCAAFatWkXLli2/70n9wDZt2kS/fv14+vQplStX5vDhw7i7u9O7d29y5cql7LdhwwZ69epFVFQUY8eOpWzZsuTNm5d58+axbt06tFotR48exdHRMcW74Qnxo5HwjhBCCCGEEEIIIYQQ/xL/K8CTkJCAiYlJsg+6N23axObNm1m5ciU7duygXr16qXgWQgiRcv5XgEe/XV8ntVotiYmJrF69moULF3Ls2DH27dtHrVq1UmR8hpPyRYoU4caNG9jb27Nw4UIaNWqETqdT/tPvN2HCBNauXcvZs2cByJkzJ9WrV2f8+PFkz55dJvqF+JfQvw+DpAGeWbNmUapUKWW/+fPnExoaytWrV8mbNy9Vq1alQ4cOZMqUSa7nv8HT05Px48fTokULOnXqRIYMGdi8eTMzZ84ke/bsBAYGKkuR6QM8e/fupWfPngwfPjxJ55eIiAgqVapE8+bNWb9+fSqd0Y/r5MmT1KhRg7i4OKpVq8bWrVtJnz49Go0GlUql/Ptk69at+Pr6cvr0aeWxpqamVK5cmaVLl+Lo6CjXghCfIOEdIYQQQgghhBBCCCFSiU6nS9YhRz/hHBUVxaBBg5IFeAwnrg1DPHv37qV+/fo0atSI4OBg0qVL993PRwghvjV9ndRPZahUKuLj4zE1NQU+HeDR18aPa+zatWtp27Yt7u7ujB8//pM1+FvQarVcvnyZOnXq4ODgwMWLFylfvjwzZ86kXLlyyn6GE5fPnj3j/v37vH79mkKFCmFtbY2ZmZlMbgqRSr6kI8hfBXiAJLXqS48pktq/fz+tWrWiQYMGjBo1ivz586PRaNi5cyfNmjWjYMGCHDt2jIwZMyqPOXv2LAMHDuTPP//k3LlzSbY9f/6cPXv20L59e0Beky+lv18uW7aMrl27kj59emJiYpg1axZ9+vQBPjyXKpVKua/evn2b27dvExkZibm5OeXLl6dIkSJkzJhR7m1CfIaEd4QQQgghhBBCCCGESAWGH1rHxcURHR1N+vTpsbKyUvaJiorC1dWVlStXfjbAYzj5XLlyZaKiooiMjMTCwuL7n5QQQnxDH0/uvXz5Emtr6yQ/1+l01KhRg8OHD38ywANJJ2dLlixJfHw8Z86cwdzcPMXGHhMTw+3bt8mXLx9jxozBz8+Pn376idmzZysBno/P4+MgUUqFi4QQf83w2tyzZw8PHjzg7t27VKtWjfz585MjRw5l3/79+zN79uxkS2gZdiKRgMjfN2XKFEaMGEFYWBgVK1YkMTGRdevWMXz4cFQqFZGRkdjb2xMfH09cXByWlpYAXLp0icyZM2Nvb//ZWiqvy9d78uQJ+/fvR6PRMHToUJ4/f87UqVNxdXUFPjynhp3lPkWedyE+T64MIYQQQgghhBBCCCG+M8NJoRkzZvDLL7+QJ08eihYtirOzM1euXCEmJgZbW1v8/f3p2LEjx44do3r16mg0GoyNjUlMTARQJiNevXqFkZERWq2WFy9epNq5CSHEt2BYJ+fNm0fDhg3JkycPP//8My4uLkqdU6lUHDx4kGrVqrFq1SoGDhzIixcvUKvVaDQaAGWS8OnTpxgbG5MhQwZiY2O/2Vi1Wm2yn6VLl44iRYpgamrKmDFjGDp0KCdPnqRfv35ERkYCYGRkhE6nIyIiguPHjyc7jgR3hPj+DIMHHh4eNG3alB49euDr68svv/xCw4YNOXr0qLL/zJkzcXFx4dixY/Tr14/z588DH65vfe2RoMLX09fvyMhIMmTIQKlSpYiNjWX9+vV4eHigUqk4ceIE9vb2ANy5c4fVq1fz+vVrAIoWLYq9vb3SDeZT5HX5a5+6t2XOnJl27drRsWNHli9fjp2dHYMHD2bGjBnAh+dUf/3cuHGD58+fJzuGPO9CfJ5cHUIIIYQQQgghhBBCfEeGk0JDhgxhyJAhREVF0a1bN4oXL87evXtp1aoVa9eu5c2bN9jZ2TF9+nQlwFO7dm0lwKMXFxfHli1bOHLkCHXq1CFXrlypdXpCCPGPfVwn+/fvz61bt6hXrx4qlYr58+dTuXJlTp06BSQP8AwZMoRnz54l+eb/+/fvWbNmDefOnaNKlSpYW1t/k7FqNBplInLTpk2MGjWKVq1a4evry/Hjx5X9Jk2alCTAc+bMGQC2bdtG27ZtmTJlCvHx8d9kTEKIv08f9Jg4cSLTpk2jUaNGrFmzhsDAQFq2bMnFixepUaMG27dvVx4TEBCgBHjatm3L5cuXU2v4aYa+fhcpUoQ3b95w/fp1wsLCcHd3R61Wc+LECTJlyqTs36dPH+bMmaOE2/UkKPL3GN7bLly4wK5du9i2bRvh4eHKNVK/fn1WrFhBpkyZGDx4MP7+/srjt27dSs+ePVmxYsUnQ0BCiE+TZbOEEEIIIYQQQgghhEgFCxcuxMXFhR49etC/f38KFixIVFQUvr6+zJw5k4YNG7Jx40ZMTExQqVRER0czZMgQli1bRtOmTdm0aZNyLI1Gw8SJE7lz5w4LFiwAZLkVIcSPb/bs2QwaNIhevXoxYMAA8ufPT2xsLF26dCE4OJjatWuza9cutFotJiYmANSuXZsDBw7g4uLCjBkzktTB/v37c/fuXTZv3gz88zppuPSHm5sbc+bMQavVYmVlxbNnz9DpdMyaNYtWrVopk8weHh5MmjQJBwcHqlWrxvHjx4mLi+PYsWPkzZv3b49FCPHPGF7Pb9++pXXr1jg4ODB69GgcHR2V/SZOnIinpydqtZpjx47x008/Kdu6dOnC2rVruXXrFpkzZ/7u5/Cj+lQt1r8e69evp3Xr1hQpUoSXL19iZmbG0aNHyZIli7LvrFmzmDBhAh07dmT06NHK/UD8PYbXgre3N/Pnz+fZs2fK9m7duuHq6krhwoVRq9Xs2bOH33//nadPn+Lh4YGtrS0LFy7k4cOHXLp0Kcn1I4T4axLeEUIIIYQQQgghhBDiO9LpdOh0On799Vdu3rxJSEgIhQsXJj4+nm3btuHq6oqZmRnHjh1L8o1igBcvXjBmzBhcXV1xcnJKsi0hIUGZrDD80F0IIX5EsbGxNGjQgOfPnxMcHEyRIkWIjY1lz5499OvXjwwZMnD48OFkdVKn09GqVSumTJmi1Mm/mhj+Fvz8/Bg5ciQ9e/akS5cu/Pzzz6xdu5aRI0dy/fp15s+fT5cuXZSOaRMmTGD69OmoVCry5s3LqlWrcHR0JDExMUlXNSHE9zd58mSyZMnCqFGjmDhxIi1btkSn05GYmKi8z/L19cXX15cGDRoQFBSEhYWFcu1GR0djY2Mj78W+kOESidHR0VhZWaHT6ZLUwubNm7N582YyZMjAoUOHKF26tLJtzZo1eHt7Y2Vlxfbt2yU09Q0NHz5cuQY6dOhAunTpWLVqFStXrqRChQr4+/sr4bX9+/czYMAArly5gpGREYUKFWLr1q04OTkleY2FEH9NwjtCCCGEEEIIIYQQQnxHOp2OZ8+ekTt3bjp27Mi8efOIi4sjJCQENzc31Go1kZGR2Nvbo9PpOH36NGXLlk3yeJVKlWSS13BiWjruCCHSgrt375IvXz4GDx7MhAkTSEhIYOPGjcnqJMCpU6coVKgQGTJkSHIMwzppOJH+LevkH3/8wS+//ELRokWZMWOG0j0nJCSEnj17YmZmxtmzZ7Gzs0syhitXrmBiYkKmTJnImDGjTG4K8S9w6tQpKleujImJCVqtlvXr19OgQQOlZhhew9WrV+f27dtERkaSOXPmFKsxaZnhczZ16lS2bNmCsbExRYoUYfjw4WTLlg2AN2/e0KJFC/bv30+5cuXo1asX1tbWbNu2je3bt2NqasqRI0fIlSuXhKa+kV27dtG+fXuaNGnCyJEjlXvbggUL6NOnDzly5ODixYtYWloqj7l48SKXLl3i7du3NG7cGAcHB7m3CfGVpHoJIYQQQgghhBBCCPENffxducTExGT7ZMiQgfTp0xMVFUViYiLbt29XJqRPnDihTEirVCoaN27MsGHDlMfqJ4MMv5FsOEEkk0VCiH87wzqp1WrRaDTJ9klMTESn0/HmzRtiY2OTBHcM62RiYiJ9+/Zl3rx5yY5hWCcNJ3O/tk4+evSIN2/efHLbvXv3+PPPP/n999/JmzcvCQkJrF69GldXVzJkyMCZM2ews7MjNjaW6Oho5XGFCxcmX758ZMyYEa1WK5ObQvwLFCtWjNmzZ1OoUCFiYmLYuHEjUVFRSs1Qq9XEx8cDULx4ce7fv8/58+eVbXryXuzL6J8zDw8Phg0bxqVLl7hy5QqzZ8/m559/JjIyEgBLS0u2bt1K+/btuXjxIt27d6dly5Zs27aNihUrcuzYMXLlyoVGo5Hgzld4//79Z7edPXuWd+/e0a1bN+XeFhwczLhx48idOzenTp3C0tKShIQE5THFihWjTZs2dOvWDQcHB7m3CfE3SAUTQgghhBBCCCGEEOIbMfym9cOHD5N0ffD19eXEiROoVCpMTU3JkycPhw8fxtfXF1dXV4yMjIiIiFCWgNHpdIwePZq4uLgkywMIIcSP7OOOFGq1WpncW7BgAeHh4QDkzp2bwoULc/ToUZYuXYq7u7sS3DFcKmv06NFcvXqVAgUKpMh4IyMjKVGiBIGBgZ8M8Lx69QpAWaplw4YNeHh4oFKpkoSMXr58SfPmzbly5UqyY8hksxDf38dha61Wi5mZGR06dKB3794UKFCATZs2sX//fiWwo9FoMDU1BSAqKgp7e/tky5iKr3PhwgXWr1/PgAEDOHbsGA8fPsTHx4c3b97QqFEjwsLCADA3N2flypUcPHiQTZs2sWLFCg4cOEBQUBA5c+aUDi9f6cCBA/Ts2ZOzZ89+cntkZCR2dnZUrVoVgPXr1+Pm5oZKpSI8PFy5tx0/fvyT4VmQe5sQf4dcNUIIIYQQQgghhBBCfCP6Cen69evTvHlzHj58CICrqyu+vr6EhoYSGxuLiYkJXl5evH37lnHjxqHRaAgPD8fBwUE5VnBwMIGBgZQtW5YGDRqkyvkIIcS3pq+TFSpUoGLFisrP+/TpQ79+/bh69SqxsbGoVCo6derEpUuXGDx4MDqdjpMnTyYJ7qxatYqgoCAqVapEtWrVUmS80dHRWFlZMWbMGIKDg5MFeCwsLADYvXs3K1eu/GzIaOTIkVy7di1JlwIhROrQaDRKLYqLiwP+P2igD/C4ublhYWHBoEGDCAoK4tGjR0o4ZNOmTezZs4ciRYoowT3xZfShKf3/Hz16xJMnT+jUqZMSwvTx8WHcuHEAODs7KwEegHLlytG0aVM6dOhA0aJFyZAhAzqdToI7XyExMZGNGzeyatUqpk2bxoULF5RtWq0WgKxZs/LixQv27t1LSEgIHh4en7y3jR07lgkTJvDixYvvfh5CpEXG/3sXIYQQQgghhBBCCCHEl3r+/DmOjo4cPHiQQYMGYWFhQWBgIG5ubnTo0AFzc3Pgw8R1nz59mD9/Po6Ojpw/f55SpUphamrK3LlzlW+xLl68GBsbG7RarXyDVQiRJjx//hxTU1OOHDlCq1atyJEjB/Pnz2fgwIH88ssvSp1s2bIlYWFh7N69m4IFCxIXF6eEaWbOnMnMmTPR6XQsXLhQWX7qW9fJWrVqMX/+fIYOHYqHhwcArVu3xsrKCoA6depQo0YNAgIClCURT58+jY2NDfBhgnrFihXs3buXhg0bki9fvm86PiHE1zFcymfOnDkcOHAACwsLihcvzuDBg4EPAZ7ffvsNlUrF6NGjGThwIMWLF6dly5YcOHCAmzdvkiFDBlasWIGVlZW8R/tCht1xHjx4gIWFBc+fP6d+/fpKl8mEhARMTEzo27cvKpUKHx8fnJ2d2bBhA9WrV0/WvQ1kmbKvZWxsjJeXF2q1mpkzZ5KYmMjw4cMpXry48ve4bt26zJkzh9GjR/Pnn39iZGTEyZMnsbOzU44zd+5czp8/T/fu3cmYMWNqnY4QaYpK93FfOCGEEEIIIYQQQgghxD/y5MkT5s2bx+jRowHo2rUro0aNInv27EkmHa5fv86SJUuYNWsW7969w9HRkbi4OF6/fk2hQoXYuHEjuXLlkqUAhBBphr4GPnv2jIEDB7JmzRoA3N3dGT58uBKK0e935coVRo4cSUhICOnSpSNbtmy8e/eOFy9eUKBAATZv3oyTk1OK1En9GBITEwkNDcXNzY379+8zYcIE2rRpg6WlJVqtlqCgIMaMGcONGzeYN28ePXv2VI6xZMkSxo8fj7GxMfv37ydbtmyfnHwWQnxfbm5uTJkyBVNTUxISEtDpdNSsWZPg4GBlSaC4uDiCgoLw9/fn0qVLFClShMKFC1OuXDl+++03smfPLu/RvpBhwGn8+PGsWrUKnU6HVqvl6dOnhIaGUqJECSBpyGfu3LmMGjUKtVrNihUrqFu3bqqdQ1rz7NkzfH19mTNnDm3btlUCPABv376lf//+LF++HHNzc/bt20elSpWUxwYFBeHr64ulpSU7d+5M0j1UCPH3SecdIYQQQgghhBBCCCG+scyZMxMVFaVM0N6+fVvZplKplJ/nz5+fESNG0LRpU2bNmkV0dDTW1tbUrFmTZs2aYW9vL5NCQog0RaVSodVqyZQpU5IlpE6ePKkEd/SdF3Q6HYULFyYgIIB27doRGBjIixcvyJQpE7Vq1aJNmzZkypQpxeqkPmBjbGxMrVq1mDRpEm5ubkoHnlatWpExY0ZatWrFgwcPmDVrFsOHD2fDhg389NNPnDhxglOnTmFjY8POnTvJli2b1HQh/gWOHj3KqlWr6N+/Pz179iRjxox4enoSGBhIw4YNWb9+PY6OjpiZmdG+fXu0Wi3Tp08nKioKFxcXKleujLGxsVzPX0Ef3PH09GT8+PHkyZMHW1tbLl++TFxcHJs2bcLJyQkrKyuMjIyU57ZPnz6o1Wr69OnD4MGDOXXqFKampql8NmlDpkyZ8PHxAT50oQKUAI+FhQWdO3cmOjqarVu34u/vz+nTpylWrBirV68mJCQEMzMz9u7di4ODg3SfEuIbkc47QgghhBBCCCGEEEKkAD8/P16+fMnbt29ZtGgR9erVY/r06eTPnx/40NEB/rrVv3wQLoRIy7y9vbl9+zbPnz9n9+7dNGrUiK1btwKQmJiIsbFxsjoYFxeHmZmZ8vvvUSf/qgOPs7MzNjY2xMXFsWvXLlatWsX69evR6XTkz5+fOnXq4OnpKcEdIVKR/hrW/z8wMJDBgwcTFhZGkSJFAIiKimLixIlMmTKFMmXKsGHDBhwdHQGIjY1l1apVeHt7Y2RkxLx586hbty7GxtIj4X8xrHuPHj3i119/pVKlSgwZMgQnJyfWrFlDQEAAp06dYvLkyXTv3p306dMne+yKFSuoXbs22bNnT7VzSas+7sDj7u5OyZIlATh+/DiBgYHMnTtX2d/GxobKlSsze/ZscubMKfc2Ib4hCe8IIYQQQgghhBBCCPEPfW4JFI1GQ1RUFJMnT8bf35969eoxY8YM8uXLl2S/ly9fYm1trUxCy5IqQoi05nN1TafT8fLlS7p27crmzZv59ddf2bJlC5A0qBMVFYWtre3/PN638FeBoISEBEJDQ3F3d08W4NG7e/cuGo2GnDlzAkiHDiFSkeG19/LlSywsLFi6dCknT55k/vz56HQ6NBoNxsbGvHz5kgkTJjB58uRkAZ64uDhWrVqFj4+PEuCpU6eOXNdfaOvWrbx69YpevXqxb98+KlasqGzbt28fo0aNIjIykkmTJtGjR49PBng+9Xvx5T4OsRl68uQJY8aMUQI8bm5ulCpVStkeGRnJgwcPePXqFeXLlydHjhxYWlrK6yHENybhHSGEEEIIIYQQQggh/gHDD60fPnzI27dvefv2LaVLlwZQls2aO3euEuDx9/enQIECAGzbto2ZM2cyYcIE5TFCCJGWGNbJt2/fEh0dDaCEW+BD4MXV1ZWQkJAkAR6dTsfu3bvZunUrXbp04aeffvpuY71x4wavX78mISGB0qVLY2JigkqlIj4+ngMHDiQJ8OiX0NKPWQKYQqQ+wyDetGnT2Lx5M8bGxjx58kQJ4uk7uej31Qd4pkyZQrly5Vi1ahW5c+cGPgR4Vq9ejY+PD48ePeLUqVMUL1481c7vR7Fx40ZatmxJlSpVSEhIIDw8HPj/JRIB9u/fj4+PzycDPOKfM7y3vXv3Dp1Oh5GREenSpVP2efz4MWPHjv1kB55PkXudEN+e9HMTQgghhBBCCCGEEOJv0mq1ygfhkyZNYs2aNfzxxx8kJCRQtWpVWrduTefOncmdOzf9+vVDpVLh7+/PwIEDGTJkCA8ePGDKlCncv38fe3v7VD4bIYT49gwnDKdNm8aGDRs4c+YM5ubmVKlShcGDB1O2bFkcHR2ZMWMGACEhITRp0oR58+Zx4MABxowZw7t37/D29k7RsRrWdF9fXxYvXsz9+/cBKFGiBB07dqRVq1Y4OjpSs2ZNJk6ciLu7Ox4eHgC0adMGS0tLmcwU4l9CH9zx8PBg0qRJZMqUCbVazbNnz7C0tCQ0NJQ2bdpgamqKWq1Gq9VibW2Nh4cHRkZGjB8/HhcXF2U5PzMzM9q1a8e7d+/4448/JLjzhYoWLUrPnj1ZunQpCQkJbN26lcaNG2NiYqLcI2rXrg3AqFGj8PT05P3797i6uiYJl4i/x/DeNnv2bLZv3869e/dInz49bdu2pUqVKpQrV44sWbLg5eUFwJw5cwAYPnz4Z/+ey71OiG9POu8IIYQQQgghhBBCCPEPDRs2jKlTp1KhQgVq1apFfHw8q1at4t27dzRv3px58+ZhamrKgwcPmDt3LgEBAbx9+xYTExOyZctGaGgouXPnltbzQog0xfBb+UOHDsXf358SJUpQu3Zt7t27R1hYGBkzZqRr16707t2bjBkz8uDBAwYPHsy6deswMzNDp9ORJUsWQkNDyZMnz18uafWteHl5MW7cOCpUqEC9evU4d+4cZ8+e5d69ezg7O+Pn50e+fPmSdOB58uQJI0aMoEuXLlhYWKTo+IQQf83w/dT169dp2LAhDRs2xNXVlVy5cjFlyhTmzJlDfHw8CxcupH79+sr++hoTFRXFvHnz6NChg7J0lr6mJSYmYmxsnGR/kZzh63Dt2jXmzZtHQEAAzZo1Y+zYsRQuXDjZfqGhofTv35/3799z4cIFqaff0NChQ5k2bRp2dnZky5aNW7du8e7dOwoVKsS4ceNo1qwZkLQDT4cOHRg0aJB0BxXiO5HwjhBCCCGEEEIIIYQQ/8CqVavo2rUrXbt2ZdCgQeTPnx/48M3W/v3789NPPxEaGqpMPjx//pzTp0+zYcMGsmbNSq9evciaNasEd4QQadbSpUvp3bs33bt3p3///hQqVIiXL18ydepU/Pz8qFevHiEhIZibmwPw5MkTgoKCOH/+POnTp8fLy4ts2bIlmTD/lgzr771796hWrRpNmzbF1dUVJycn3r17x8WLF/H29mbv3r107NiRCRMmkDVrVhITEzlw4ABdu3bF2tqa48ePkyFDhm8+RiHE1zt27BjXr1/H1dWVAwcOUKpUKeDD8ldr1qxh1KhRxMfHs2DBAn755ZdkgRx9WEfeo32Z/xVkunr1KjNmzGDBggV06dIFd3d35X2z4XN85MgR8ufPT+bMmWVppn/A8Dndtm0bXbt2pUuXLnTv3p38+fNz4cIFgoKCmDRpEra2tixbtoxff/0VgKdPn+Ln58fMmTPp27cv06dPT5H7rxAiKQnvCCGEEEIIIYQQQgjxhQwnJfSTCV26dGHHjh2EhoZStGhRNBoNwcHBeHt7o9FoOHnyJHZ2dsTFxWFqappkAkJ/PJkUEkKkFZ+qky1atODs2bNs376dwoULk5CQwObNmxk6dChGRkZERERgb2+PRqNBrVajUqmU4yQkJCRZWiUlrVu3jpw5c9KpUyfWrVtHiRIlkpzPo0ePaN++PSdPnmT58uW0aNECgMTERI4dO0aBAgXIkiVLio5RCPFlFi5cSK9evWjcuDEajYZt27YBKCHAhIQE1qxZg7e3txLgMezAI76OYY3evXs3ly9f5urVq1StWpXixYtTsmRJ4EOAx9/fn0WLFv1lgAekq9E/YRh6iomJITQ0lL59+7Jv3z7l+dabNm0aQ4cOpUKFCqxYsYJ8+fIBH4K0M2fOpGfPnkr3KSFEypLwjhBCCCGEEEIIIYQQf+HgwYNcvnyZvn37AkknEt6+fUu5cuWwt7fn8OHDxMXFERISgpubG2q1mhMnTpApUybgw3IBr1+/ply5cvItYiFEmnL8+HFu375Nu3btgKRdK6Kjo8mXLx/169dn1apVxMbGsnnzZqVORkZGYm9vD8D58+fJly8f6dOn/+6TtvqJ/vz58xMTE8PJkydxcHBItt+OHTto2rQpv/zyixIGMCRhTCH+HY4cOcKYMWPYu3cvpqamhIeHK0v/GIYD9QEerVaLv78/TZs2lWv4KxnWaw8PD2VJMiMjI2JiYsiTJw9DhgyhT58+ANy4cYPJkyezcOFCunbtioeHhxIYEd+Wu7s7kydPplGjRtja2rJ8+XLgQ7gHUP490rdvX+bNm8eWLVuU7jv6/aT7lBDfj8QVhRBCCCGEEEIIIYT4BJ1OR1RUFC1btsTFxYV58+YBoFar0Wq1AFhYWJAxY0bi4+OBDy3pPxXc0Wg0tGjRguXLl6PRaCS4I4RIM54/f07NmjUZMGAAwcHBAEmWm7GxscHW1pbXr18DsHPnziR1Uh/cAWjYsCEjR45UjvE9NWvWjKpVq3Lz5k1ev37NjRs3gA9dOgxVrVqV3Llzc/36dZ4/f57sODK5KUTq0ocSqlSpwqhRo2jevDnx8fEEBQXx6NEj4P/fy5mYmNC2bVvGjBlDVFQUfn5+JCQkpObwf0j6eu3r68ukSZNwdnbm4MGD3Llzh5UrV/LkyRP69evHggULAMiXLx9ubm706NGDlStXMmLECG7fvp2ap5BmmZmZkSFDBnbt2sWZM2d4/Pgx8CG0o//3iE6no379+gBs3rwZQPm3jn4fubcJ8X1IeEcIIYQQQgghhBBCiE9QqVTY2tqyaNEiMmfOjIuLC3PmzAE+TFLEx8ej0WgoW7YskZGR9OzZk0GDBmFkZER4eLgS3IEP7eifPn1K0aJFpf2/ECJNsbe3Z+nSpcTGxjJixAhWr14NfKihiYmJxMXFkStXLvbt28fgwYMZOHAgRkZGHD9+XKmTOp0OPz8/YmNjKV68+Hc/h4SEBDJlysSmTZuoWbMmr1+/ZvDgwcTHx2NsbExiYqIykWlpaUn69OmxsLDA3Nz8u49VCJGU/trUMwxIV6xYkaFDh1KvXj1mzZrF0qVLefr0KZA0wNOmTRuWLVvG1q1b5br+m86ePcv8+fP59ddfGT58OBUqVMDa2prMmTNjbGxMgQIFcHZ2VvbPmzcvQ4cOpXnz5oSHh5MxY8ZUHH3aow+xjR49mhEjRmBtbc3169cJDw8H/v+6SUxMRKVSUblyZYyNjUmXLh3w/QO0QogP5MoTQgghhBBCCCGEEOIT9B96N2vWjEWLFmFtbc2AAQOUAI+pqSlGRkb89ttvqFQqFi1aREJCApcuXSJLlizAhw/G161bx/z58ylSpAitW7eWrjtCiDSnbdu2LFu2jAcPHuDl5aUEeIyNjTE3N2fUqFEYGRkxffp0EhMTiYiIIHPmzMCHWrt+/XqWLVtGyZIladKkSYqOVaPRJPuZiYkJALa2tqxdu5a6dety4sQJnJ2diYuLw9jYWOkmtHbtWi5fvkyxYsUwNTVN0bEKIf6aRqNRQgY3b97k6NGj7Ny5k0ePHhEXFwd8CPCMGjWKqlWrMmbMGBYtWpQkwKPRaDAxMcHZ2Zls2bJ9skaI/+369es8fvyYPn36UKBAARITE1m/fj1du3bF2tqaI0eOYGdnR3x8vNIBKX/+/IwfP55z585ha2ubLIglvtynQmz6znHDhw9nyJAhaLVaOnXqxIkTJ5TrRh9QXbVqFYmJieTMmRP4/38HCSG+L5VOrj4hhBBCCCGEEEIIIT5Jv+wLwPbt2+nUqRMvX74kICCAvn37KvutXLmS33//HTMzM6ZPn069evUwNTVl3rx5BAYGkpiYSHh4ODlz5kSr1cq3WYUQadKGDRv47bffyJ49O2PHjqVdu3YAvHv3jlmzZjF27Fhy5cqFt7c31atXR61WM3/+fBYtWoROp+PIkSMpWicNj7tx40bu3r2LjY0NlStXJl++fMp+UVFRtGnThv3791O6dGm6detGsWLF2LlzJ9u3byc6Oprw8HBy5MjxzccohPgyhtezn58fS5YsUZZesrOzo3379vz++++ULVsWgOPHj+Pt7c3hw4cZOXIkPXr0SNIlUXw5jUaTbBmlCRMmMGLECC5cuECBAgVYv349Hh4eyZaSffDgAZMmTcLFxYX8+fMrjzd8zy2+juHrcfnyZd68eUORIkUwNTXFzMxM2W/ixIl4enqSLl06AgICKFOmDCVLlmThwoUEBATw/v17jh07poRrhRDfn4R3hBBCCCGEEEIIIYT4C18a4FmzZg09evTg3bt3mJubk5CQgJGREWXLlmXVqlXkypXrk5MdQgiRlhgGeMaMGUP79u0BuHv3LmvXrmXkyJHExcWRNWtWYmNjiY2NpWjRoqxbt+671clhw4YxdepU5fdFixalb9++9OnTR/lZdHQ0bdq0Yd++fZiYmODg4EDhwoWxtLTE398fR0dHqelC/AsMGzYMf39/qlevTuPGjfnjjz84ceIEp0+f5ueff2bKlClUrlwZgIiICEaOHMnx48fp378/gwcPxs7OLpXP4MdiGJoaNWoUNWrUoEaNGoSEhNCiRQuWLFlC9uzZ6d69e7LgDnzo1HbkyBEOHz5M7ty5U+s00gzDf6eMGDGCmTNn8u7dO/LkyUOjRo3w8vJK8vxPnDiRUaNGodFoSJcuHUWLFuXmzZvkyZOH1atX4+TkJPc2IVKRhHeEEEIIIYQQQgghhPgLOp0OnU6nTFT8VYDn/PnzHDlyhAsXLpAxY0YqVqxItWrVsLGxkQ/ChRBplr6+6ScRDQM8o0eP5rfffgM+1NMLFy6waNEiHj16hJWVFdWrV6dRo0bY2dl9lzq5fPlyBgwYQIsWLahbty5RUVGMGjWKV69e4enpyahRo5R9o6KiaN26NYcOHaJixYpKkMfwnIUQqSckJIS2bdvSpUsX3N3dleDB7du3mTJlCgsWLKB69erMnDmTYsWKARAZGUnfvn2JiorizJkzWFlZpfJZ/JjGjh2Lt7c3Xbp0ISAggLt371K3bl2io6OxtrYmXbp0HD16NEkXl4ULFzJ27FgaNGjA9OnTMTc3T8UzSFsmTZrEiBEjqF69OsWKFePo0aOcPn2amjVrsmLFCrJnz67sO378eGbMmMHz588ZN24cPXv2RKPRfLf7sBDi8yS8I4QQQgghhBBCCCGEgS/50Hrbtm107tz5kwGeT5GlsoQQacmX1LTPBXj+yTG/haFDh3LixAmWLl1K3rx5Abhy5Qo1a9bk6dOnjBw5El9fX2X/Fy9e0KpVKw4ePEjLli1ZsWIF5ubmMsEpxL/AiBEjmDZtGhEREZQsWTLJdZmQkECPHj0IDAxk6tSpuLq6Ko87e/Ys2bJlw8HBQZZr+kKGz+2bN2+oXr06ZcuWZejQoRQsWBD4EArx9PRUQpzNmjVTHr9y5UpGjx6Nubk5u3fvJmvWrPLcfwNarZaYmBgaN25MtmzZmDBhgrKkY9u2bVm7di0VKlRg3bp1SQI8fn5++Pn5YWZmxtGjRylSpAjx8fGYmpqm1qkIIZDwjhBCCCGEEEIIIYQQCsOJiaCgIM6ePcv58+epVq0aZcqUoUGDBsq++gDPq1evmDFjhhLgSUhIwNjYGJVKJZMSQog0x7BObty4kcuXL/PHH39QvXp1ihcvTvny5ZV9P7eEVkJCgtLBJiXrpOFY9eGgX375hcqVK+Pt7Q1AYmIixsbG3Lx5k8qVK38ywKPvwBMaGkrLli1ZuXIlpqamEswUIpVoNBpUKhUNGjQgNDSUK1eukC9fvmT7nThxgoYNG5I1a1YiIiJInz59ku1yDX+9wMBAEhIScHd3JyQkhMqVKyeptUOHDmXatGmYm5vTuXNnsmfPTkREBEeOHMHKyoqDBw/K0kz/0Md/b1+9ekX58uWZM2cOtWvXThLC6dy5MytWrPhkgGfSpEkMHz4cCwsLDh8+TIkSJeR1ESKVGaf2AIQQQgghhBBCCCGE+LfQf1g9bNgwpk6dSrp06VCpVOzduxcAT09PhgwZgrW1Nb/++ivLly+nU6dODBw4ELVaTe/evZUJaUCCO0KINEWn0yWrk/qg4sqVK8mcOTMuLi54enoC4OzsDMBvv/3GyJEjMTIyok2bNt+lTmq1WmWsgYGBXLhwATs7O0xMTNBqtcD/h4g0Gg158+bl6NGjVK5cmTFjxmBkZKQEfGxtbVm7di1t27Zl/fr1PH/+nL1798oEpxDfycchP/21V7ZsWfbu3cvVq1fJly9fsuBB+fLlKVKkCJcvX+bZs2fkypUryXEluPN1Nm3aRKdOnfj555+xs7Mjb968ymujf+6nTJmCo6Mjq1evZtGiRSQmJuLk5ETTpk0ZO3Ys2bNnl4DIP2D43B0/fpznz59jY2ODWq3GzMwMQLmvGRkZsWzZMgBWrFhBq1atWL9+PdmyZQPAzc0NAC8vL6pVq8b+/fspW7bs9z8pIYRCOu8IIYQQQgghhBBCiP88w0mhJUuW0L9/fzp06EC3bt2ws7Pj0KFDjBo1inv37tGnTx+mTJlCunTpANi+fTvdunXj6dOnLF26lE6dOqXmqQghRIqbM2cOQ4cOpW3btvTs2ZP4+HhOnz7N0KFD0Wq1DBo0iKlTpyr7b9iwgc6dO6NWq1m+fHmSpVRSmj5kZKhUqVIcPXqUdOnSKROc+v/fvHmTGjVq8ODBAyZPnsyQIUOU7jxRUVHUqlWLAgUKsHbt2u92DkL8lxmGFW7fvg1A7ty5gQ+1pVWrVuTJk4cjR46QJUsWpSuPPphTuXJlXr9+zZEjR8iYMWPqnMQPwrCji1arTRLY1OvduzcLFiwAPoR5mjZtqmwzfK2ioqJ49OgRr169okCBAlhZWWFqairBnX/A8N8rHh4eTJs2jcTEROUetnDhQrp166a8jobPtb4DT4ECBTh48CBZsmRRjjtp0iQ8PDzo3r278toKIVKHhHeEEEIIIYQQQgghxH+a4Qfhb968wcvLiz/++IP58+fj6Oio7Hf48GG8vLw4fPgwEyZMUL6tCh+WjvHx8WH79u1JHiOEEGnBx0t0tG3bltevXzN37twknSyOHTtGixYtePr0KdOmTcPV1VXZFhQUhLe3N4cPH1a+9Z/SY924cSM9evSgbdu2NGvWDEtLS1xcXDh9+jQNGjRg3bp1pE+fPlmA548//qBNmzZs3rxZqen6be/evSNDhgxAyi75JYRIGgbx9/dn9erVWFhYsHjxYiXA89tvv7F69WoqV67MmjVrkiwLtH79enr06EGLFi2YO3euspSQSM6wnj1+/DhZuMPBwYHOnTuTmJjIiBEjmDJlCsWKFWPJkiX89NNPnzzOX/0Z4u+bNWsWQ4cOpX79+tStW5fjx48TFBRE+vTp2bdvHxUqVEh2XwNo3rw5hw4d4vLly2TOnDnJ/XL79u00atQoNU9LCIGEd4QQQgghhBBCCCGEAGDEiBE8e/aMs2fP0rRpU7y8vNBoNMD/L8+wf/9+mjZtilar5ciRI5QuXVqZhIiNjcXc3Fy+USyESLOGDRuGubk5ERERtG3blq5du6LRaFCr1eh0OtRqNWFhYdSuXZvSpUuzadMmcuTIoUzYxsTEJOl28619PDEcHBzMqFGj2Lx5MwUKFAA+hDRbt27N7t27adKkCatXr04yJn2XnY9///HxZRJaiJRleI0NGTKEuXPnUq5cOdzd3WnYsGGS4MEvv/zC3r17yZ49O/369aNw4cKcOHGCNWvWkJCQQHh4eJJQj/i82rVrc+DAAe7evUuOHDno27cv8+bNY9q0afTs2ZP06dOTmJjI0KFDCQgIoHHjxowZM4YSJUqk9tDTrI8DtC1btiQhIYGZM2cqAdMJEybg4+ODsbExYWFh/PTTT5/swBMVFYWtre0nt33qzxJCfF9y9QkhhBBCCCGEEEKI/7wnT55w6tQpFi9ezKlTp3jz5g3wIbRjZGSE/vtvtWvXZujQocTHx/P06VNUKpWyzczMTHmMEEKkNTdv3iQkJAQ/Pz/27dvHnTt3gA81T79EjVarpXr16nTr1o3Tp09z8+ZNAGUCXr/cYErVSf2fM3jwYNKlS8e6deto3LixEtxJSEjA0tKSNWvWUK9ePbZs2UK7du2IiYlJFtTRT17qf294/I9/LYT49vTXWEBAADNnzqRXr14sXryYhg0bAh+u0cTERAB2795Nr169SEhIYMSIETRv3pzp06fj4ODA4cOHyZ49uxLIFn+taNGiAFSsWJFOnToxb948XF1dadWqFenTpwc+1MWpU6fSt29ftm7dipeXF+fOnVOOIX0jvi39/cjDw4NJkybx6tUrfvvtNxwdHYmPj1e2jR8/noSEBKpXr87JkyeV+7K+Aw+QJLgDye/HEtwRInXJFSiEEEIIIYQQQggh/vMyZ87M5MmT6datGwAhISGcOHFC2a5SqUhISACgYMGCaLVaLl++rGwz/L8QQqRFefPmZfbs2dSuXRsjIyNOnz7Nw4cPP7lvsWLF0Ol0/Pnnn995lB/ExcWh1WrZvHkzN27cQKPRoNFoMDExQaPRkDFjRtauXasEeDp06MD79+8/G9QRQqSOly9fEhQURJEiRejXrx/58uVLst3Y2FgJ8MydO5ft27ezfPlypk2bxoYNG9ixYwe5cuWSrohfQB+4CQgIYNKkSTx69IjAwEC6d+/OpEmTknUuMjIyYsaMGfTt25dt27bh7e3N+fPnAamfKeHq1asEBgbi4eHB4cOHefv2LQCmpqZKMGfw4MFMnDiRxMTETwZ49CSgI8S/l1ydQgghhBBCCCGEEOI/RavVJvm9frKiRIkSuLi40LFjR65fv86SJUuUrhEAJiYmAFy4cAFjY2MKFy78/QYthBDf0cddE/QTg/Xq1WPYsGH8/PPP7Nixg0WLFiWpqfoJwRs3bmBmZkaOHDm+36D5/3HPnDmTgQMHYmZmxtGjR7ly5YrSWUffgcDKyoq1a9fSsGFDNm3ahIuLy3cdqxAi+Xuyjz169IjIyEiaNGlCvnz5Prm/sbGx8vOyZcvSsWNHXF1dqV+/PjY2NsmCC+LTVCqVUuvv3r2LVqtFpVIREhLCs2fPAJSglJ4+wNOvXz+2bt2Ki4uLEm4X31ahQoWYPXs2VapUISEhgbNnzxIXFweQpLPOoEGDmDBhAiqVivLly3Pu3DkJ6wjxAzH+37sIIYQQQgghhBBCCJE2GH7z+uHDh1hZWaFWq5VlAEqWLKksi7Vw4ULevXtH165dqVmzJlqtlo0bN7Jq1SoKFy5M+fLlU/NUhBAiRRjWyZcvX5IuXTolvAgfAjxqtZqRI0cyatQoYmJi+P3335VAY0hICBs3bqRo0aKULl06Rceq0+mSLWWlH79+8nLy5Mk0bNiQ8PBwsmfPriyNpQ/wrFy5EhcXF7y9vVN0rEKI5PShgsmTJ9O+fXulu4v+2o6NjQXgwYMH6HS6ZCEE/fX+6NEj7t+/z88///zZP0N8nv551Nd+Z2dnzM3NiY+PJyAggNKlS3PkyBHy5cun7Kt/jYyMjAgICODly5fs2rWLTJkypfLZpD3657xZs2YAvH37llmzZlG4cGH69OkD/H+Ax8jIiEGDBvH+/XsWLVqEra1tKo5cCPG1VDpZeFAIIYQQQgghhBBC/AdotVplAmf8+PEEBQVhZGRE1qxZmThxIiVLllT2vXDhAuPHj2fNmjWYmZnx66+/cvXqVRISEtBqtezdu5dcuXIlOaYQQvzoDGvaxIkT2bp1K7GxsVSpUoWOHTtStmxZZd/9+/fj5eVFREQEBQsWpFSpUjx69Ih79+4BEBoamqJ10jBklJCQwIsXL8iYMSOmpqbKz7VaLcOHD2fy5MnkzJmTY8eOJQnw6P+v9/HvhRApb8WKFXTu3JkuXbrg5+dHlixZlG3Pnz+nSJEi5MyZk+Dg4CTLZhmG9zp27Eh8fDyLFi3C0tLyu59DWuHh4UHevHnp0aOH8jMfHx/GjBmDg4MDR48eJW/evMTHx2Nqagp8CFbpQ1fR0dFKtyN5f/z3fPzc6TsgGQZVN2/ejLe3NxcuXGD+/PlJXi/De+ObN2+wtLSUZeOE+IFI5RRCCCGEEEIIIYQQ/wn6D8K9vLzw9PTk7du3qFQq9uzZQ40aNVi9ejUxMTEAFC9eHA8PDzp37kxcXBxnzpyhWrVqTJ8+nWPHjpErVy40Go1MTAgh0hR9TRs+fDjDhw/n5s2bREdHExAQQJMmTdizZ4+yb+3atRkzZgxVq1bl5s2bhIWFUatWLTw8PDh69GiK1knDicjZs2fToEEDcuXKRbFixfj11185d+4c79+/R61WM378eNzc3Lh37x6VKlXiwYMHnwzuABLcESIV1KlTBx8fHwIDA/Hy8uLRo0fAh3COvb09v/32G2fOnCEgIEBZGghQwgwrV65k79695MyZE3Nz81Q5h7Tg8OHDTJo0ievXrwMoXY98fX0ZOXIkT58+pXLlyly/fl0J7uzYsYMBAwYQHBwMgI2NzSc7JIkvY3jPXL9+PR4eHjRu3JjOnTuzd+9eoqOjAWjatCmjR4+mWLFi9OrVi4ULFyrHMDIyUpaRs7S0RKfTSXBHiB+IVE8hhBBCCCGEEEII8Z9x9+5dtmzZQv/+/Tl48CBnz55lyZIl5MyZkx49erB69Wrev38PQIkSJRgwYAAdOnTgzp07WFtbU6xYMezt7eWDcCFEmnX+/HnWr19P//79CQ0N5ebNm0yYMIHXr19Tv359du7cqexbp04dRowYQaVKlXj8+DFZsmShR48eZMmSJcW+6W9YfwcPHszAgQO5f/8+derUIUOGDOzevZv69esTGBjI8+fPUavVjBs3TgnwVK9enbt370pQR4h/iWzZstG7d2+8vLxYsmQJw4YNIy4uTgnn/Pbbb5QtW5ZZs2bRq1cvjh8/jkajQafTsXTpUsaOHYutrS1DhgxJssSf+DoFCxbEwcGBx48fA2Bubq6EQAwDPNWqVWP//v3Mnz+fIUOGcOjQIapUqaIcx7BDjPhyWq1WubcNGzaM33//HX9/f8LDwwkMDKRhw4Z4enpy8uRJ4EOAZ+zYsUqAZ/HixcqxDMNT8noI8WORd6dCCCGEEEIIIYQQIs36uPX8kydPuHLlCgsXLsTJyQmAzp07Y2dnx6hRoxgwYAAAbdu2JX369JQqVYphw4YRGxvLxIkTiYmJwdXVFUdHx9Q4HSGE+OY+rpNRUVG8evWKzp07U7hwYQDc3Nywt7dnyJAhNGrUiO3bt9OgQQMAfvnlF4yMjPD09KR37968fPkSNze3FAs46ici582bx8yZM3FxcWHgwIHkzp2bmJgYpk2bxtKlSxk+fDjm5uZ07NhRCfAYGRkxfvx4WrVqRXh4eLKlSIQQqSNz5sz06NGD+Ph4fvrpJ8zMzJRtP/30E9OmTcPT05Nly5axfv168uXLR0xMDHfu3CFr1qzs27ePrFmzyvJAX8hwyTH4/44vVlZWXLp0CZ1OB3wIgejvEb6+vpiYmDB16lTq1q2LSqUiT548nDhxguzZs8tz/w8ZLu07depU+vbtS4cOHShSpAjBwcGsWrWK+fPn8+zZM7y8vChZsiRNmjRBrVbj7e1Njx49SJcuHe3bt0/lMxFC/BMqnb4CCyGEEEIIIYQQQgiRhhhOIly/fh0zMzMluLN+/XoAEhISlG9pb9u2DR8fH65du0ZAQADt2rUjXbp0AFy8eJGxY8eyceNGBgwYQL9+/cidO3fqnJgQQnwjhnXy3r17pEuXjq1btxIUFMS+ffuApHVy6dKlDB48mFevXiUJ8ADs378fT09PTpw4weTJkxkyZEiKjVun09G4cWOuXLnC7t27yZcvn7IMVnx8POvWrcPNzQ21Ws3Ro0eVwKVWq2XixIm0b9+eXLlypdj4hBB/T3x8vLIkEyQNmVy6dImwsDAWL17Mq1evyJo1K1WqVGHAgAES3PkKhs/Tx8+Zs7MzYWFhnDt3juzZs3/yMZs3b+bSpUskJCTQq1evFO209l9z69YtGjZsSObMmVm5ciU5c+ZUtp0/fx5/f39WrFiBu7s7fn5+yrWxYcMG5s2bx+LFi+ULBkL84CS8I4QQQgghhBBCCCHSHMPJntGjR7Nw4UISEhLQ6XQ8e/aM3bt3U7duXSDphMT27dvx9vbm5s2bjB07lm7duikBnkuXLjF27FiCg4NZuXKlfLNVCPFDM+y4M3bsWFatWkVcXBw2NjY8fvyYvXv3Kp13DOukPsDz5s0b1q9fT7NmzZRjhoaG4unpSUREBNu2baNhw4YpMvYXL16QJ08eypYtS2hoqBLc0df++Ph4PDw8mD59Ov3792fGjBlJQkiA8hghxL/bx11i4uPjiYmJwdLSUumeJeGRr+fr68utW7coWLAg2bJlo3Tp0koAMzw8nLx58ybZ/+MubfrXRZ77byciIoKKFSvi4eHBuHHj0Ol0SZbTOnHiBN27d+fatWucPHmS4sWLK4+NjY3F3NxcXg8hfnDyzlQIIYQQQgghhBBCpDn6SZ5Ro0YxevRoSpYsSc6cObl8+TLPnj0jMDCQPHnykDdvXoyMjJQPuhs1aoRKpaJfv37MmDGDLl26KMcsWrQo7u7uVKlSRYI7Qogfnn4S1svLi3HjxlGgQAGcnJw4dOgQGo2GlStX4ufnB5CkTnbp0gW1Wk2XLl3o27cv9evXx9jYGGNjY2rVqsXIkSM5ceLENwvufDxxD2BlZUWuXLm4c+cOz58/x97eXtlPp9NhamqKq6srgYGB3L17FyBJcAeQ4I4QPwjD619/fRt25wEkrPCVtm3bxowZM3j58qXys4wZM6JSqXj58iWDBg0if/78lCtXjqxZs1KyZEmsra15+/YtFhYWwP+/LvLcfztarRaAuLg44MPfd8Pnt3z58rRs2ZJRo0Zx6tQpihcvroSqzM3NAXk9hPjRSecdIYQQQgghhBBCCJFmGH7b9N27dzRq1IiCBQvi6emJo6Mjhw4dYt68eQQHB+Pi4sKgQYNwcnJK9tj9+/dTtGhRsmTJ8tk/6+NvIAshxI/g46WymjVrRsWKFRk0aBB58+YlJCSE8ePHExkZiZ+fH8OHD//kY9euXUulSpXIkSMH8OmQzT+tk4aPf/PmDenSpVNCN+3bt2fNmjW4u7vj6emJhYUFOp0OnU6HWq3m8ePHFC1alKpVqxISEvK3xyCEEGnVgwcPiIqKIjIyklu3bhEREcHBgweVWq/RaABIly4dJiYmtGvXjhkzZiQLT4mvY3hvM/z15cuXqVy5MgAHDhygVKlSymP03eOOHj1K1apVmT59OgMGDPjuYxdCpCyJlgshhBBCCCGEEEKINEM/qbxkyRLi4uI4efIkI0eOxNHREYBq1aphb2+PWq1m1qxZ6HQ6Bg8ejJOTU5LOErVr1wb+euJZgjtCiB+Rvk5u3rwZExMTbt26xcyZM5UlUpo1a4a1tTWenp54enoCKAEewzrZunVr4P8DPR8Hd+Cf1cmPl+rasWMHDRo0oGXLllhZWTF27FgiIyNZsWIFTk5OtG/fXllGBz4sgxgTE0P58uWBT4eLhBAirfur97JZs2Yle/bsyvJLBw4cYP/+/YwePZqGDRty9+5dbt26xZkzZ4iKisLDw0OCO/+Q4b1tx44d3Lt3j3LlylGmTBmKFClCjx49mDJlCn5+fvj5+VGgQIEkyz7u2bMHIyMjihUrlpqnIYRIIdJ5RwghhBBCCCGEEEKkKWFhYdSsWZPSpUuTkJDA4cOHyZgxY5IPvq9cuYKfnx+rV6/GxcWFwYMHkytXrlQeuRBCfB+bNm3C2dmZEiVKoFKpOHXqFGq1msTERKWzTVhYGCNGjCA8PDxZB56UZjjZ7ObmxoIFC7CysmLq1Km0bNkSlUpFYmIia9euxc3NjZiYGJo1a8aQIUOwtLRkx44dTJ06FYDDhw+TOXPm7zZ2IYT4tzAMily5coUHDx6g1Wqxt7enTJkyyn7698hRUVFky5aN1q1bs3Tp0iRLMMXHx2NqaprkmOLrGN7bPD09mT9/PiqViqCgIKpVq4a5uTmvX7/m999/Z8uWLfzyyy/4+voqIdTg4GBGjhxJxowZ2b17N7a2tql5OkKIFCDhHSGEEEIIIYQQQgiRprx+/Zq5c+fi6+tLbGwsc+fOpVevXkDySQw/Pz/Wr19P+/btGT16tLL8ixBCpGVXr15lxowZrF27lujoaDZv3kzjxo2BpJOLYWFheHp6cuzYMUaMGMHYsWO/6zjHjh3LqFGj6NOnDy4uLhQsWDDJ9rdv37Jnzx68vb25fPkyGTJkQKfTodVqcXR0ZNeuXTg5OclksxDfiXS4+vcwrOU+Pj4sWLCAJ0+eKNtdXFzo378/+fPnT/KYggULkj17dg4ePAjIa5oShg8fzqRJk+jRowedO3emQoUKSbbfvHkTDw8PNmzYgImJCVWrVuXt27dcvXoVKysrDh06hJOTkyzhK0QaJOEdIYQQQgghhBBCCJEmGH6A/erVK5YtW8bQoUMpXrw4U6ZMoVatWkDSAM/Vq1dxc3Pj7NmznDt3Dhsbm1QbvxBCpDTDSdhr164xa9Ys5s+fT4MGDZgxYwZOTk5A0np66NAhevfuzYsXL7h+/TpWVlbfZaxnz56lSZMmFClShLlz55I7d+7P7hsdHc306dO5e/cucXFx/Pzzz7Rt25bMmTNLcEeI78Swvjx58kQ6Xv1LDB8+nIkTJ9K8eXM6dOiAsbExq1evZu3atdSoUYNp06ZRokQJpfNaw4YNOX78OBcvXiRLliwSDvnGduzYQZs2bWjTpg3e3t7K0r6fMn78eLZv3865c+fIkycPP/30E6NHjyZ79uxybxMijTJO7QEIIYQQQgghhBBCCPF3fPxtU8NfZ8yYkd9//534+HiGDx+On58fxsbGVKtWDSMjI+UD70KFCjFt2jRsbGywsbGRbxcLIdKUj+ukYX0rWLAgLi4uJCYmMn/+fCwtLZkwYQI5cuRArVYrj61WrRqLFy8mT548WFlZfbM6+fHYPv79rVu3uH//PlOmTPnL4I5Go8HGxgZfX99P/hkyuSnE96GvC3Xq1CFr1qyMGTNGCQSK1LF9+3bmzZtHp06d8PLyIm/evAA8ePCANWvWcOPGDfLkyQOgLJlYqVIldu3axfv37yW48zcY3iM/1RknIiKCd+/e0adPn78M7sCH4NWgQYN49eoVDg4OJCYmYmJiIsEdIdIwCe8IIYQQQgghhBBCiB+O4YfWR44c4caNG9y8eZNy5cpRpEgR8uXLh42NDT169ECr1eLp6Ymvry8+Pj7JAjz58uUDPv0BuxBC/KgM6+S5c+e4c+cOr169IkeOHFSrVg1jY2MKFiyIq6srOp2OBQsWoNPpmDhxohLg0R+jYsWKwLetk/rjhIaGUqtWLdRqNTqdDp1Oh1qt5ty5c8D/BwI+nqzU//7Vq1eoVKpPdk6Tmi7E96XT6ciUKRNBQUHY2NgwePBgCfCkoI/DlB///vTp08TGxtK9e3fy5s1LQkICGzduZMKECeTJk4fjx49jYWFBQkICJiYmwIfaamxsjKWl5Xc/nx/dx8+/4X1Up9Oh0Wg4ceIEFhYWSZYrM6TfPzo6GhsbG8zNzTEzM0OlUikBKwnuCJF2yTtXIYQQQgghhBBCCPFDMeyk4OXlRePGjenatSt+fn40a9aMVq1aMX/+fACsra3p1asXfn5+hIWF4evry+HDh4HkH3zLJK8QIq0wrJM+Pj7UrVuX5s2b07lzZ+rUqUPr1q3ZuHEj8KEDz6BBg+jZsyerV6/Gw8ODBw8eAClfJ7t27UqdOnVYvXo18CGoo9PpAChdujQAZ86cUcai36bT6ZQQZteuXdm3b983HZcQ4u9RqVQEBQXRp08f5s6dy7hx47h9+/Zn99df03parTalh5hmGAZF7t27B5CsK1pERAQODg5UrlwZnU7H+vXrcXNzQ6VSER4ejr29PQDh4eEsXboUgO7du/Pnn38qyw6KL6d//qtXr06TJk0AlHuVPnxjb2/Pu3fvuHPnDgCJiYnK4/X3toSEBKZPn87Vq1eTHFe6gwqR9sknEkIIIYQQQgghhBDih6KfPPby8mLChAnUr1+fHTt2EBQUxMCBAzl37hx9+vRhypQpwIcAT+/evfHz8+PYsWMMHDiQiIiI1DwFIYRIUfo6OWLECMaOHUu1atVYtWoVCxYsoFGjRuzcuRM3NzcWL14MfAjwDB48mJ49e7Ju3Tp69+7No0ePUnycDRo0oHDhwri4uLBmzRrg/wNDefPmxcbGhgkTJrB+/Xrgw8RlQkKCMoG5dOlS9u7dy/Pnz5OFAIQQ35c+hKBWq5k9ezZt27Zl0aJFvHnz5rOPUalUHDp0iBkzZihdt8SX0dfB2rVr079/f65du6Zs04egsmbNyvPnzwkLC2Pz5s14eHigVqs5ceIEmTJlUvb39fXFz8+PqKgosmfPTtasWWXZwb/p6dOn2NnZsW3bNjp37gx8uK/Fx8cDKEGq8ePHAx+WK0tMTESr1SqvqY+PD7NnzyY6OjpVzkEIkXpk2SwhhBBCCCGEEEII8cM5evQoc+bMwdnZmQkTJpArVy4A2rVrR40aNWjevDnDhw8nc+bMdOzYkYwZM9KvXz/evXvH8uXLyZ07dyqfgRBCpKy9e/cyZ84c2rdvj5+fH46OjgDUr1+frVu34ubmxtSpU8mZMyf16tWjQIECDBs2jDdv3hAWFoapqWmKj7FVq1akS5cODw8POnbsSEJCAh07dgSgePHiTJo0iR49ejBkyBDi4uL47bfflKVd1q5dy5QpUyhUqBAtW7aUjgRCpCKtVqss6bN582aaNm1KYGAgQ4YMoUSJEp993O3bt2nYsCE6nY48efLQuHHjZEsPib9WqFAh5s6di62tLe7u7hQsWFAJQdWuXZvFixczevRobty4gVqt5uTJk9jZ2SmPnz17NpcvX6ZXr15YWVkpP5cg1d/j4ODA9OnTsbW1ZcmSJcTExBAcHKzcUxs0aEDBggUJDg4mW7ZsTJ06Vbl2dDodGzduZNOmTZQpU4YiRYqk5qkIIVKBVF4hhBBCCCGEEEII8cO5efMmL1++pFOnTuTKlUtp66/T6WjatCmrVq1Co9Gwbt065RvfFhYWuLu7c/HiRRwcHGRpBiFEmnbt2jXev39Pp06dcHR0VGpejhw56Ny5Mz4+Pty4cYNNmzYpj8mbNy9+fn5cuHABOzu7FK2T+k45v/76K2PGjCFHjhzY2Ngk2datWzcmTJjAvXv36NixIy1atGDgwIE0btyY3r178/79ezZu3EimTJmkpguRivRBDw8PD5o3b86sWbMAKFWqFJB8eSy99OnT4+7uDsD27dsBWRroa82ePRsPDw+WL1/OqFGjuHLlirLt119/pU2bNhw4cIAXL14QHBycJLizatUqZsyYQfbs2enbt68SIhH/jKOjI97e3rRr145169Zx6tQpdDodOp2OXLlyERwcjLW1Nf7+/jRp0oQVK1Zw4sQJ3N3dGTZsGG/fvmXRokVkzJhR7m1C/MeodNJLUgghhBBCCCGEEEL8ILRaLWq1mlGjRjF69GhWrlxJ+/btk31L+/379zg7O7N//35OnjyZ7Fvf8q1uIURapa+T3bp1Y+nSpezbt49atWqh0WiSLIFy7do1WrRowZUrV7h+/Tp58+ZNcpzvUScN/4zHjx+TJUuWT+63Zs0apkyZwu3bt4mOjsbJyYmff/6ZyZMnkyNHjmTnJoT4PvT1BuDIkSO0atWK1q1b07t3bwoXLvxFx7h79y6urq4cO3aMsLAwChYsmJJDTrMGDx7M9OnTOXnyJGXKlFF+HhoaypQpU9izZw/t2rWjevXqFClShJUrV7Jx40ZMTU05fPgwuXLlSvJ6in/uzz//5MWLF0leD/1zfOnSJXr27MnJkydJSEgAPiyhVb58eYKCgpQvJ8i9TYj/FolQCiGEEEIIIYQQQoh/rY8nEfS/Ll++PAARERG0b98e+P9JYJ1OR/r06SldujS7d+/myZMnyY4rwR0hRFrxuTpZtWpVli5dyuHDh6lVqxZGRkZJ6mTBggX55ZdfuHLlCjExMcmO+z3qpH4sKpVKCe4YBnr0v27bti3Vq1cnJiaGP//8k0KFCmFtbU26dOlkclOIVKSvN3fu3OHkyZOYmprSt2/frwrgODo60rNnT0JCQvjjjz8kvPM3TZs2je7duytLLenrZ61atTAzMyNLliwsW7aMoKAgAKytralUqRJz584lZ86cUku/MX2XHf3SvvrXQ61Wo9VqKVq0KJs2beLq1atERkZibGxM8eLFKVu2LBkzZpTXQ4j/KOm8I4QQQgghhBBCCCH+lQw/tI6MjCQqKopffvkF+DBJ1LJlS06fPs26detwdnYGkk76/v777+zevZuwsDAKFSqUOichhBApyLBOXrhwgdjYWMqVKwfAyZMnqVmzJu/fv2fdunW0aNECgISEBExMTABwdnbm6NGjnDlzhqxZs6bOSfwPf9UBSLqoCZH6xowZw9y5cylfvjyWlpYEBgZ+cfDA8BpesGABXbp0UeqT+PsMg5qGNfLw4cM8fPiQ169fU6FCBZycnLC0tJSgSCr4q/uXdEAS4r9LrnwhhBBCCCGEEEII8a+j1WqVSYRx48bRrl07OnfuzNGjRwFwcnKiX79+ALRq1YrAwEDev3+vfAgeEhLC/v37KVu2LDly5EidkxBCiBRkONk6ceJEWrRogaurK5GRkQD89NNPTJs2DZ1Ox+DBg1m9ejWAMjG+adMmwsPDqVChAtbW1qlyDl/ir8I5EtwRInXFx8fj4OAAwJYtWzh9+jQvX7784iCIPmAC0LNnT0xMTEhMTEyx8f5X6GvjxzWyatWqtGnThh49elC8eHEsLS3R6XQS3EkFhh3m9PS/luCOEP9d0nlHCCGEEEIIIYQQQvyrGH4TdejQoQQEBNC4cWNcXV2pWrVqku0BAQEMGzaMhIQE6tSpQ6lSpXj06BGhoaGoVCqOHTuGo6OjdGcQQqQphjVtyJAhzJo1i7p16+Lq6kqdOnWSbJ88eTLu7u4AdOzYkUKFCnHnzh22b98OQHh4ODlz5pQ6KYT4W968eUNISAjjxo3j9u3b+Pv706VLF8zNzVN7aEIIIcQPRcI7QgghhBBCCCGEEOJfadmyZfTp04fu3bszePBgcufOrWwz7DgRHBxMcHAwO3fuJC4uDgcHB8qWLcvcuXNxdHSUpQCEEGnWwoUL6d+/P71798bV1RUnJ6dP7rdq1SpGjx7NgwcPePfuHfb29pQsWZLFixdLnRRCfJG/Cvi9efOGTZs2MWLECDJkyIC/vz9169aVJbCEEEKIryDhHSGEEEIIIYQQQgjxr9SqVSuOHTvGnj17KFq0aLLtWq1WaSv/7t07/vzzTx49ekTOnDnJnj07GTJkkAlpIUSaFRcXh7OzM+fPn2fXrl0UKVIk2T6Gk+23bt3i9evXXL16laJFi5IrVy6srKykTgoh/ifDOqHVann9+jUZMmRIEs7RB3jc3Nyws7Nj8uTJEuARQgghvoKEd4QQQgghhBBCCCHEd2cYvElMTMTY2DjJ9hcvXpA/f34qV67M1q1bk+z/pWQJGCHEj+x/1b0HDx5QvHhx6tWrx5o1az6731/Vwr9TW1PyOEKIfx/D4M78+fPZvn07kZGRFC9enPr16zN48GBl37dv37Jx40YJ8Ig0Qe5tQojvTSqOEEIIIYQQQgghhPju9B+Ed+rUiZCQEBISEpJsNzY2xtTUlFu3bvH06dNkH5xrtVoAHj9+zLx58z75Z0hwRwjxI9PXvW7dunHlypVk22NjY4mPj+fu3bu8fPky2XZ9nYyKiiI4OPgv/4xvNdZ27dpx/Pjxb3JMIUTq02q1SnBnyJAhuLi4cObMGcqUKcOff/7J0KFD6devH7GxsQBYWFjQokULJk2axIsXLxg+fDjbtm0jMTExNU/jh6Cv2XoajSaVRiL09Pe2VatW8fr161QejRDiv0DCO0IIIYQQQgghhBAiVVy6dIlNmzbh6enJ7t27kwR4MmbMSPny5bl58yZ79uxJMulj+C3YCRMmMH36dG7fvv3dxy+EEClt165dLF26lJYtW3L9+vUk2/LkyUOlSpW4ceMGN2/eBFBqpWGdHDx4MEuWLOHJkycpOtawsDC2bNlC69atOXXqVIr+WUKI78Pw/dbs2bPp3bs3W7duZfv27SxfvpwMGTIwd+5c+vbtmyzAM3nyZC5evMjMmTMlvPMF9M/15MmTiYqKwsjIKFmgR3x/kyZNokOHDsyePZu3b9+m9nCEEGmchHeEEEIIIYQQQgghRKooUqQIO3fuBGDYsGFs376d+Ph4ZXu3bt2wsLBg6tSpnDp1iri4OOD/JzfWrl3L5s2bKVOmDFmyZPn+JyCEECmsRo0aLF68mOjoaFq0aMG1a9eUbSqVinr16vH8+XM6d+5MdHS0sgShvk6uW7eOAwcOkD17djJmzJiiY61SpQpLly7F1NQUZ2dnTpw48dl9dTpdio5FCPHtHDlyhOXLl+Ps7IyLiwulSpXi9evXdO/enQwZMlC2bFmWLVvGgAEDiImJAT4EeJo2bUpwcDCBgYGYm5un8ln8GEaPHo27uztDhgzh5cuXqNVq6cCTyn799Vc6deqEt7c3s2bN4s2bN6k9JCFEGqbSybtkIYQQQgghhBBCCJFKdDod4eHhtGzZEiMjIyIjI5UgzsuXLxk3bhzTp08nX758dO3alcaNG2NnZ8eKFSuYO3cuWq2WQ4cOkT17dnQ6nSyVJYRIM/Q1LS4ujtWrV9O/f38yZcrEhQsXSJ8+PSqVipiYGDp37sy6desoWLAg06ZNo0iRIuTMmZM5c+YoHS8OHz5MtmzZUqxOajQajIyM0Gg0bNiwgf79+6PVarlx48YnQ0P6cVy5cgVLS0ty5MjxzcckhPg2pk+fzuDBgzl69CgVK1bk3bt3/Pzzzzx//pzZs2fj6OhI+/btuXnzJj179sTf35906dIlOYa+Roi/lpCQQPv27dm2bRvt27dnypQp2NjY/OVj9PXUsOOa+LauX7/OuHHjWL58OT4+Pnh5eX3y77P+tXj+/DmWlpaYmZmlwmiFED8yCe8IIYQQQgghhBBCiFSl0+k4cuQIsbGx1K1bV/mZSqXi2bNnzJ49m2XLlnH37l3MzMwwMjIiISGBggULsmXLFpycnGRSSAiRJulrYWxsLIGBgWTPnp2GDRsC/z8Z/u7dO/r27cuaNWtISEggQ4YMmJiY8OrVKwoWLMiOHTu+aZ38eII4Pj4eU1NT5fcajYbg4GDs7e2pV6/eZ49z9OhRqlatStu2bVm+fDkmJib/eGxCiG/vxYsX7Nmzh3bt2hEXF0ebNm04fPgw48ePp1OnTpiZmREQEICrqysAjRs3Zu3atRJc+EoJCQmYmJiQmJhI+/btWb9+Pe3atWP58uVKVzVD+vvDo0ePyJo1a5Kfia/38XOnfz30rl+/jre3N61bt6Z58+afPc7evXsZNGgQnp6etGvXTkJVQoivIuEdIYQQQgghhBBCCPGvov/wXP//t2/fcuvWLZYuXcrjx48xNTWlatWqNG3alEyZMklwRwiRpn1cEw1/pq9/sbGx7Ny5k/3793P16lXs7e2pVKkSbdu2xcHBIUXq5NatW6latSrW1tYA9OvXj0KFCtG/f/9Pjv9ju3fvpkePHty/f59169bh7Oz8TccnhPg6hrVGp9OhVqtJTEzE2NhYCSCEh4dTv3592rRpw+zZs5VwQ1BQEEOHDqVw4cJcuHCBa9euYWtrm8pn9O/1cV38uEbHx8fj7OxM9+7dadq06WePs3PnTho1asTs2bPp06dPio45LTN8Pe7fv5+kG9ySJUuoXr06efPm5d27d2TIkOGzx4mLi6N///4sWrSIGjVqEBoamuJjF0KkLcmjmkIIIYQQQgghhBBCpCL9h+f6/1tYWFCiRAn8/f2T7avVaiW4I4RI0z6uiYa/NjIyQqvVYm5uTvPmzWnevDkxMTFJlqxJiTrZpUsXli9fzoIFC+jevTvDhg1j7ty5DBo0KNnk5ue6QPzyyy/Mnj2bpk2bsnPnTpydnaVDgRCpxDA8olKpeP/+PRkyZEhWOy5evMibN2/47bffknQlOXLkCKVKlWLOnDlYWVlha2sr1/NnGAZF3r59i4WFhfI8T5kyhdKlS1O7dm22bt36P4917tw5APz9/albty758uVLuYGnYfrXo0aNGhgbG7Nw4UJy586Ni4sLc+bMYfny5eTOnfsvgzsAZmZmeHl58fbtW9asWcO6deto1arV9zgFIUQaIXdNIYQQQgghhBBCCPGv97nm0TIpJIT4r/u4DhoGdz61/Z/S6XS0bt2aQoUK4ePjQ82aNZk6dSqenp4MHDjwf05u6o8BH5bXcXNzY+nSpTx79kxquhCpwDC4s3jxYlq3bk3x4sVp0KABfn5+vHjxArVajU6nI1u2bAAEBwcrj1+3bh27d+/G0dGR3LlzY2dnJ8Gdv6APitSqVYtatWrx7t07AFxcXHBzc+PSpUvExcV99r2vIQ8PDzw9Pblx4wa3bt1K0XGndQkJCVhYWBAaGoqvry8dO3Zkzpw5DB48mOrVq3/R32edToejoyOenp5YWVlx4MCB7zByIURaIstmCSGEEEIIIYQQQgghhBDiq5w/f54qVaoQExNDzZo1mT17Nvnz5//iSXt994lz586xcOFCZsyYIZ3UhPjODLvADBkyhICAAHLkyEGePHl48OABf/zxB2XLlmXjxo3kzJmT27dv07x5c86fP0/9+vVRq9UcP34cS0tLDh8+nGS5IfF5z58/p0+fPmzYsIFmzZqRKVMmFi5cyODBgxk0aBDZs2f/4mNdvXqVDh064ODgwLp1674oQCk+TavV4uLiwrx58wDo2rUr/v7+WFpafnYJyE8dQ61WM27cOPbt2ydLZwkhvorEXoUQQgghhBBCCCGEEEII8VUiIiJ4+/Yt6dOn58yZMxw+fJh3794pHTr+F/0kaMmSJZk1axZGRkZoNJqUHrYQwoD+Opw+fToBAQH07t2bPXv2sH//fiWgc+rUKTp16oRGoyF37tysWLGCRo0aERERQUREBGXKlCEsLIwcOXLINfyF7O3tmTNnDgMHDiQkJISFCxcqnXe+JrgDUKhQIWrWrIm9vT3p06dPoRH/N6jV6iThp6dPnxIdHf3VxwDo27cv+/btAz7fQVQIIT4mnXeEEEIIIYQQQgghhBBCCPGXPu6oExkZyfnz58mYMSNeXl5ER0czatQoOnXqRPr06ZXJyi/pVCCESD0vX76kQYMGxMXFERQUROHChYmJiWHfvn307dsXS0tLwsLCyJQpk7LEVnR0NG/evEGn05EpUybSp0+fZPkt8df0XVy6devG0qVLAahRowY7d+7EzMzsi7u8GO6nr9Ff+ljxgeG97f3798ycOZPXr19z7do1Nm7cSKtWrfD19aVQoUJ/6/jyegghvoZxag9ACCGEEEIIIYQQQvz4Pp7U/dJlU4QQ4r/i4wm8H2lCz3BSfs+ePVy/fp0OHTpQrlw5ADJlykTPnj0ZNWoUOp2OTp06JelecPfuXRwdHVNl7EIIuHTpErly5cLCwiLZtidPnnDixAm8vb0pXLgwiYmJbNmyBTc3N4yNjZXgjv44Tk5O2NjYYGNjoxxDp9NJcOcr6Gt/7ty56dOnD69evWLVqlU0b96clStXYmtrq+z7V/cKlUqlvOdWq9Xy/vsrGd7bLl68iJWVFe7u7rx//5706dPTqVMnAgMDARg9ejQFCxZMFvb5X92OfpT7vBDi30EquBBCCCGEEEIIIYT4x/QfYh8/flz5vTR8FkKIDwwnX2/cuAH8OBN6Wq1WmdzUd9YZPXq0shwIQJUqVVi4cCHW1tb4+vqyfPlyEhMTAdiyZQt16tRRuksIIb6vq1evUrx4cVq1asXbt2+TbX///j06nQ6tVotGo2HdunW4u7ujVqs5ceKEEtyJiYlhwIABbNq0KdkxfpR6lpq0Wm2yn3l5eTFz5kwCAgLo0aMHu3btokOHDspSTRqNRnluP7d8k2FYR4I7X84wuDNx4kRatGhByZIluXfvnhLImT9/Ph07dmTdunV4e3tz+fJl5d84mzdvZuTIkdy6dSs1T0MIkcZIFRdCCCGEEEIIIYQQ30TdunX59ddf2bZtG/D/3wYWQoj/Ov3k66+//krbtm05c+aMsu3fHnTUTwa7u7szduxY6taty/bt23F2dlb2MTIyUgI8NjY2jBw5khEjRjBp0iRGjBjBw4cPqV69emqdghD/aWZmZrRs2ZLdu3fTuXPnZAGe/PnzkydPHvbv38/69esZMWIEKpUqSXAHYMyYMZw8eZJs2bJ971P44Wk0GqWWPnr0iLt37/LixQvgQ421tbVl5MiRSoDnt99+48WLF0q4ZMeOHfTq1UsJyYt/xrBT1NChQ/H29qZYsWIsX76cnDlzKvuZm5uzYMECJcDj6elJZGQkGzduxMPDg9mzZ3+ym5UQQvxdKt2//V8GQgghhBBCCCGEEOKHEBISQsuWLSlUqBATJkzg119/Te0hCSHEv0ZCQgLz589n+PDhVKlShXHjxlG6dOnUHtYX2bhxI507d6Zt27Z4enqSK1euT+6n1Wo5ceIEffv25ezZsxgZGZE/f3527NiBk5MTiYmJGBsbf+fRCyHu3LnDqFGjWLFiBS1atGDZsmVYWFgo3XY8PDzw9/cnQ4YM2Nvbc+HChSShhKCgIEaOHEnRokUJCgrCysoqFc/mx2K4zNLYsWMJCgri3r17lCpVCmdnZwYNGqTs++DBA0aPHs3ChQupX78+Y8eO5cKFC0yaNImoqChOnTol4alvaP78+fTv359+/frh4uJC3rx5P7lfXFwc/fr1Y8mSJZiammJsbIyDgwP79+8nd+7cslyZEOKbkfCOEEIIIYQQQgghhPjH9B9a79q1iyZNmpAtWzamT59Os2bNPrm/fgmZuLg4zMzM5ENvIcR/QlxcHKtWrWLQoEGUKFECf39/ypYt+8l9P66TqWno0KHMnTuX0NBQfv755/+5//v371mxYgU2NjbUrFkTBweHJEuUCCG+v9u3b+Pr65sswAMflvNr06YNZ86coXHjxmzevJn379+TPn16Zs2axfTp09HpdISFhZEjRw553/Y3jBgxggkTJlCgQAGcnJw4cuQI79+/p3///syYMUPZ78GDB4wfP57FixcTFxeHsbEx2bJl48CBAxIU+UZ0Oh3x8fE0atSIP//8k507d5IvX77/+bgJEyZw/vx5bGxsGDFiBNmzZ5d7mxDim5LwjhBCCCGEEEIIIYT4JvQTzdu2baNJkybs2bOHOnXqfHb/Xbt2MXfuXGbOnImjo+N3HKkQQqSeuLg4li1bRt++fTl8+DCVKlX67L779+9n9+7d9O3bFycnp+83SAPx8fFUrVqVqKgorl+/DpBs8lhf/z83qSyTzUL8O9y6dYvRo0crAZ6lS5diaWkJwMWLF+nZsyfHjx/HwcGBHDlyEBUVxcOHD8mbNy/bt2/HyclJwgpfSP886XQ6rl+/Tt26dWncuDGDBg0ib968HD9+nP79+3Pq1Cn69u3LrFmzlMc+ffqU0NBQDhw4gLW1Na6urmTNmlWe+2/o7t275M6dm99//52lS5d+VWc4/T1NXg8hxLcm4R0hhBBCCCGEEEII8c3oP8x++vQpDg4On93v1atX1K5dm9OnTzNkyBDGjRuHsbExKpXqO45WCCFSR1xcHI8fP/7s8lMADx8+pHr16ty8eZPFixfTpUuXVAnBJCYmUqdOHY4cOcLBgwepUqVKku36Mb18+ZLhw4czYcIEMmbM+F3HKIRIzrBeJCYmYmRkhEql+ssAz507d9i6dStbtmzh2bNnZMuWjZo1a9KpUyfpoPU3hYaGYmtrS5s2bVi/fj3FixdXXpvz58/Tr18/jh49mizAo6d/zuW5/7aePHlCnjx5qFu3LiEhIcm261+j+/fv8+jRI8qVK/f9BymE+M+RqLsQQgghhBBCCCGE+Fu0Wm2yn+m/J2ZjY5Pk9x+ztLQkICCA4sWLs3PnTmVCSQgh0hJ9Dfy4FpqZmSnBnc/VSXt7e9zc3MiWLRuzZs0iLi4uVbrXGBsb06RJE7RaLVu2bOH169fKtsTERGVM/v7+bN26latXr373MQohktJoNMq1uWnTJsaPH09kZCQajYY8efLg4+PD77//zsaNG+nSpQtv3rwBwMnJif79+7Nnzx7CwsLYsWMHw4YNw8HBAa1WK+GRrzRt2jTq1KlD586dyfh/7N1lYBTX+/bx78YJDknwENwdWtyhWKE4tFhxtwQIEAoEKBoguBaX4Fbc3d1KcXeHENnd5wXPzj9BKr8CgXB93iTZkZzZzZzZ7LnmPnHjGsEdm+zZszNu3DgKFSrEuHHjaNu2rbEsLCwMwHjO9dx/WM7Oznh4eLBx40ZWr15tPG61WrFarcb506VLFxo0aMCjR4+iqqki8hVReEdERERERERERP61iINCly9f5tChQxw9epTr168D4OjoaEyj8i52dnbkzZuXZs2acfHiRWbOnPnJ2i4i8imYzWajD7xx4wZ//PEHJ06c4MWLF+9cJyKr1YqTkxP169enevXqnDt3jiNHjny0tr4ZxgwPD4/0c6lSpcibNy9jx45l/vz53L17F8CYYmTx4sXMnTuX7Nmzkzlz5o/WThH5exFDNt27d6d58+YMGjSI+/fvG+d6qlSp3hvgsVgsmEymtypoaeq7f69UqVKkT5+eU6dO8eDBA548eYKdnV2kYGe2bNkYN24chQsXZty4cTRo0AB4/V5a/pt33WhgEy9ePHr06MGrV6/47bffOHHiBAAmkwmTyYTVamXOnDns27ePwoUL4+rq+qmaLSJfMU2bJSIiIiIiIiIi/0rEaRgGDBjAjBkzOH/+PACenp40bdoUPz+/f7SvGzdukD9/fho3bkzfvn0/WptFRD6liP3kkCFDmDdvHufOncNqtfLtt99Ss2ZNWrdu/da6EdkCkE+ePCFBggT4+/vTs2fPD97WiFOxzJ07lz179nDo0CGyZs1KiRIlqFu3rrHMz8+PW7duUbNmTSpVqkTWrFmZM2cOc+fOxWKxsHPnTlKkSPGX4U0R+TT8/PwYOHAgLVu2pGnTpuTKleutdS5dukTfvn2NKbSmT59OrFixoqC10Y+tbz116hS1atXizJkzNG7cmClTpkRabusvT548SZ06dbh48SI3btwwqljK/ybitW3Pnj1cvnyZp0+f4uHhQdWqVYHX/4d0796d2bNnU7ZsWerVq0eNGjUICwtj9uzZBAYGYrVa2bJlC0mTJtW1TUQ+OoV3RERERERERETkf9K1a1eGDRtG2bJlKV++PE5OTowdO5bTp09Tt25dpk2bhpOT03u3jzhYkTFjRqOCg4hIdNGlSxcCAgIoWLAgRYsWxd7entGjR/Pq1Sujn/wrtsHHffv2kSdPng/eT0YMDvn4+DB27FhcXV1JliwZV65c4dmzZ3To0IEePXrg7u7OwoULmTp1KuvXrzf24ejoSO7cuZk/fz4pU6aMNGAqIlFj06ZN1KhRg0qVKtGvXz+8vLzeu27EAE+pUqVYtmwZMWPG/HSNjQbeF8K0OX36NDVr1uTMmTN06tSJgIAAgLf6y7NnzxI/fnwSJUqkoMh/EPH16NmzJ+PGjePJkyfG8uLFizNgwAAKFCjA2bNnGTlyJL/99hvh4eFkyJCBly9fcvfuXVKmTMnatWvx8vLStU1EPgmFd0RERERERERE5F9btGgRjRo1okGDBnTp0oVUqVIBMHz4cHx8fPDw8ODChQt/O/gTcWBCH4qLSHQyd+5cGjduTOPGjfH29iZNmjQABAYG0qlTJ9KlS8fhw4f/1SB5eHj4Rwk6DhkyhO7du0eq0LFjxw66devG3r178fPzw9/fH4CHDx+ybds2zp07R1hYGN9++y158+Ylfvz46sdFPhNDhw6lW7du7Nq1iwIFCrx3Pdv7sIsXL+Lj48OuXbs4deoUbm5un7C1X7aI/d6+ffuM4KO7uzuVK1c21jt58iS1atXi7NmzdOzYkeHDh7+1vc3fhYHkn+nVqxcDBgygXr16NGzYkOTJkzNz5kwCAwNxc3Nj+vTpFC9enAcPHrB3715GjRrFo0ePSJgwIYUKFaJZs2YkSpRI1zYR+WR0O5OIiIiIiIiIiPxru3fvxmQy0aRJE1KlSkVoaChLlixh9OjRpEmThj179hAzZkxCQkJwdnYGeOcdxBF/1ofiIhId2Pq6rVu3Ejt2bFq1akWaNGkIDw8nKCiI0aNHkypVKnbv3v1WP/l3PkZw5+bNm0yfPp1ixYrRsWNH0qVLh9ls5tGjR1y6dImUKVPSvn17Y/0ECRIYU45EZLFY1I+LRDFbyGDnzp3Y2dnh4eGB7R7+iO+5bOs9efKE2LFjkzp1akaOHEmsWLFIkCCBwiP/UMR+r2fPnowfP57Hjx8by8uWLUvv3r3JkSMHWbNmZeHChdSqVYuRI0diMpkICAjA3t7+rXCInvv/bvv27UyaNIk6derQu3dvI0CbPn16AMLCwsiRIwcACRMmpGLFipQvX/6t517BHRH5lNT7i4iIiIiIiIjIX3qzcHNoaCgHDhzAy8uLPHnyYLFYWLJkCd26dQNeB3tsd2wfO3aMxYsXA6j0v4h8FUwmE8+fP+fAgQOkSZOGbNmyERYWxuLFi+nRowdms5m9e/eSMGFCAC5cuMDWrVs/Wfve7NOvX7/O2bNn+fHHH0mXLh1hYWEsWLCA9u3b4+LiwoEDB3BzcyMkJISrV6++d78abBaJeraQQebMmbFYLFy/fh2TyRTpvLdardjb2xMaGkqfPn04c+YMAJ6engru/Eu258nPz4+BAwdSsWJF1q9fz4kTJ+jWrRs7duygQYMG7Nu3D7PZTJYsWVi4cCGZM2dmxIgRNGvWDFCA/WP4448/uHfvHo0aNTICtPPnz6d3794kSpSIY8eOET9+fEJCQoxtzGaz8b3tnNFrIyKfkq6+IiIiIiIiIiJisFgskX4ODQ01QjehoaHGOo6Ojjx69IiLFy+yaNEiunXrhp2dHfv378fd3d3Y3sfHh5kzZ/LixYtPdxAiIp9QxME+GwcHB5ycnIx+c+nSpXTt2vWtfjI8PJx69eqxcOFCwsLCPklbbX36kydPAIzf6+LiAsCSJUvw9fXFZDKxf/9+I4z56tUrqlSpwqZNmz56O0Xkv8mUKRPw+n3YrVu3sLOzw2w2Y7FYjD5gxIgRjB8/nhs3bkTaVsGd93sz/AiwdetWJk2aRN26denbty+lS5cmS5YsZMiQAYDg4GBy586Nvb09FouFzJkzs3DhQjw8PFi2bFmkSj3yv3nXdfjEiRM4OTlRsGBBABYuXEi3bt0wmUzs3bs30o0G3t7eADg6Ohrb66YDEYkKugKLiIiIiIiIiIjBNmCzZcsWAJycnAD45Zdf6Nu3LyEhIbi4uFC1alVu3bqFv78/vr6+7wzujBo1itOnT1O8eHFixIjx6Q9GROQTsN2VHxgYyPXr14HXQZgSJUpw9OhRWrdubQQc9+zZE6mfDAwM5OrVq2TNmvWjTIkVka3aBrzu0/v06cOtW7eIHz8+ABs2bOC3334zQkb79u2L1NZevXpx7do1XF1dP2o7ReS/a9CgAeXLl+fQoUN06NCBGzduYG9vb7zPW7RoETNmzKBAgQJ8++23Udzaz9/Tp0+Bdwc6zp49y/3792nUqBGpU6c2Krz06dOHxIkTc/ToUeLGjUtISIjx/GfKlIkdO3Zw/Phx4sWL985QkPwzEacuGz9+PCtWrAAgceLEhIaGsnTpUhYvXvze/1eGDh3KrFmzjOu3iEhUUnhHREREREREREQiqV27NqVKlWLRokUAtGvXjv79+xMzZkyjtHzBggXJnj07s2bN4smTJ28N8gYFBTFq1CjSpk1L/fr1dRe3iERrK1eupFOnTowePdp4rGzZssSIEYMJEybw6tUrLl26ROLEiYHXQZqFCxcyfvx4MmfOTK1atT76Xf4Rq230798fq9VqVIGoX78+c+bMwcfHBzs7Ow4dOoSHh4fR1tmzZ7Nq1SpKly5NtmzZPmo7ReS/sVUhmTVrFoUKFWLRokWULFmSUaNGsWDBAlq3bk2nTp14/vw5M2fOJF68eG9VXpT/s2PHDqpVq8a6deveufzkyZO4uLhQuHBh4P0VXo4ePYqvr6+xXbp06UiSJEmkimjy79n+x+jRowdt27Zl9+7dWK1WKlSoQIwYMRg4cCAdO3bEzs6Ow4cPR/p/ZfLkyezevZv69esb1zwRkaikT01ERERERERERCSScuXKkSxZMmrVqkW5cuUYO3Ys3t7e1KtXjzhx4gCQJ08e2rVrh6enJ48ePWLOnDmsWbOGq1ev0q1bN7p27UpoaCgLFizAzc1Ng0IiEq3lypWLRIkSsXbtWq5duwZAyZIlGTFiBAB37txh5syZnD59mjt37tCrVy+6du1KSEgIc+bMIWHChB+tn3xzOpFVq1ZRuXJl2rdvT7JkyQD48ccfyZ07N48fP6Zhw4bEjBkTeB3cmTx5Mn369MHBwYGAgABixYqlKhEiUeTN8/ld/Ya9vT1Wq5UECRKwdu1aateuzbVr1+jYsSN16tRhzpw5ZMiQgZ07d+Lp6YnZbFbI+j0sFgu7d+9m8+bN7N27N9Lzb/vew8ODV69esWzZsr+s8DJkyBCmTZv21jRltqox8u9EfC3Onj3LsmXLaNq0KS1btsRkMpEtWzY6d+7M5cuXuXXrFuPHjzcqzcHrcNvQoUNxc3OjS5cuODk56domIlHOZFVPJCIiIiIiIiIivB6ktd35u3HjRqpXr87z58+pUKECy5cvx87ODovFEmnqlblz5zJ69Gj27dtn7MfJyYkCBQowY8YMY1BIAxMiEl1ZLBbs7OwYNWoUHTt2ZPz48bRo0cJYPm3aNLp06cLDhw+NxxwcHMiXLx9z584lZcqUn6Sf7N69O9myZWPy5Ml07NiRKlWqRPq9M2bMYPjw4Zw5c4ZMmTKRM2dOzp07x+nTp0mUKBHr16/Hy8tLfbrIZ2Dx4sVUqlQJZ2fn965j65sA9uzZw9WrV3ny5AnZs2cnc+bMxIkTR+fzP/DkyRM2b95MiRIliBcvHpcvX8bLy8tYfujQIQoXLkyaNGl48uQJDg4OHD58OFJQZNKkSfTt25cff/yRX3/9FUdHxyg4kuhp27ZtODk5UalSJVavXh1pGrhTp04RGBjItGnTyJcvH8WKFSNPnjwsW7aMdevW4erqyvbt2z/ZdVhE5O8ovCMiIiIiIiIiIgbbQM/w4cPx8fEhduzYPHv2jAULFlCjRg3jjlSr1WoMCF27do2TJ09y/Phx7O3tKVCgAFmzZiVu3Lj6IFxEvhoHDhzgu+++I1GiRKxatYo0adIYy44cOcLx48c5dOgQ8eLFo0CBAuTPn5/48eN/tH4y4n6PHz9Ozpw5AYgZMyYzZsygWrVqQOQB/l27drFixQp+++03QkNDSZs2LaVLl6ZTp04kTpxYfbrIZ8D2Hm3nzp0ULFgw0jn8pr86Z/9qO3m37t27s27dOkaOHEnRokUBCA0NpXfv3owYMQKz2czvv/9O2bJljW1mzZpFv379cHV1Zd26dSRKlChSYF7+nYh/t1OmTKF58+Zkz54de3t7Dh06BET+u//zzz9ZunQpQ4cO5cGDBwAkSpSIokWLEhAQQPLkyXVtE5HPhsI7IiIiIiIiIiLylsePHzNnzhxMJhMDBgzg1q1bzJo1i59++gl4Hd4xm804ODgY27w5EKFBIRGJLt6sOhZRxEG/Hj16MGjQICPwGBoaipOT01/u92P0kxH3u3jxYn744QdmzZpF7969uX79On369KFXr15Gv/1m//3o0SMsFosxnZednZ0GN0U+E+vWraNixYr4+vrSv3//qG7OVyMkJIT+/fszYMAAypQpQ8+ePY0Az9GjRxk9ejQzZsygYMGCFCtWjNy5c7N06VLWrFkTqcKL3h//7yI+d/fu3ePFixe0bNmS3bt3YzabWb16NcWKFQPe/r/kxo0bXLlyhUePHpErVy7ixYuHq6urrm0i8llReEdERERERERE5Cv35uDtmx92L1y4kA4dOnD79m0jwBPxw/Nz586RPHlyXF1do+oQREQ+uMOHD3Pr1i0qVqwY6XFvb2+cnJyoU6cO2bJlw87OjvDwcBwcHDhx4gRly5YladKk7Ny5kxgxYkTqU9/Xz34sXbt2ZdiwYQQEBNCpUyfGjBnDL7/8wpMnT1i1ahXly5d/50Dyu9osIp+Hy5cvU7ZsWe7fv8/mzZuNqlryYb2rb3z8+DGTJk2iR48eFC9enF69ehlhkdOnT7Ns2TKGDRvG48ePAfDw8KBo0aIMHz5cFV4+oI4dOzJnzhyOHTvG7du36dOnD6tWraJBgwaMGzfurf9J3heY0vVNRD43Dn+/ioiIiIiIiIiIRFcRBxFevnzJs2fPSJgwIfb29saH3DVr1sRkMtG+fXvq16+P1WqlXr16APz+++8EBARQpkwZunfvHmXHISLyId25c4dixYrh4uLC2rVryZMnD1arlWPHjjF27FhCQ0OZMWMGZcuWpXfv3ri5uRE7dmwyZsxI8eLFCQoKYunSpfz444+R9msbJPxYg4URByj37NnD3LlzadeuHRUqVACgbdu2ODg40KVLF77//ns2btxI8eLF3xrYjNg+DWyKfF68vLzo3LkzrVu35siRI+TMmVPVXD6wiO+PT58+zYsXL8iXLx/x4sWjVatWWCwW/Pz8jPWLFStG5syZyZw5M/Xq1TMqvOTOnZv48eMTM2ZMBXf+g4ghm1mzZjFt2jSqVq3K8+fPyZ07N7169SIsLIyZM2fi5ubGsGHDIm3/vnND1zcR+dyo8o6IiIiIiIiIyFcq4iDC2LFjWbBgAceOHSNFihTUrFmTunXrki5dOmP9RYsW0bFjR27evMmgQYOwWCxMnz6dO3fucPz4cVKkSBFVhyIi8kG9evWK8ePHc/DgQSZMmEDs2LGNZadOneKPP/6gb9++nDhxAnd3d0qVKkWTJk0oVaoU586do1ixYnzzzTcsX748Stp//fp11qxZQ8+ePdm8eTNZs2aN1OdPmDCBLl26EBwczIYNGyhRooQG/0U+I+8KelgsFuB1EOHcuXOUKVPGmI7J3d09KpoZLUV87ocMGcL06dOJESMGU6ZMIVeuXAA8e/aMsWPH4ufn91YFnndRhZf/XcTn7tmzZ/Tu3ZvDhw8zY8YMUqZMaax38OBB/Pz8WL9+PZ07d2bo0KF6zkXki6PwjoiIiIiIiIjIVyjiIK2Pjw/Dhw8nXbp05MqViwcPHrBp0ybKlCnDwIEDyZ07t7Hd0qVL6du3L8ePH8dkMpExY0ZWrVpFqlSpdEexiEQrYWFhADg6OjJq1CgSJ05MrVq1jOVPnz5l8eLFLF++nBUrVgBQv3598ufPz4EDB5g5cyazZ8+mTp06n7TdAwcOpE+fPlSoUIEYMWIwd+5cbMMAVqvV6PsjBng2bdpEsWLFNMAs8pmZMmUKsWLFemc/0rJlSyZNmkRQUBA1a9bU+7APIGIf6O3tzdixYylRogQ+Pj6UKlUq0rpvBnh69+5NkSJF3tqPfBjdu3fn7t27HD9+nCpVquDn54fZbMZkMhnXtYMHD9KrVy/WrVtH586d36rAIyLyuVN4R0RERERERETkKzZ48GD69u1LkyZNaNOmDRkzZiQkJITEiRPz/Plzvv32W8aMGUPOnDmNbY4cOcK5c+d4/Pgx1apVw93dXQNGIhJtHThwgG+//ZYcOXLQv39/Klas+NY68+fPZ8WKFSxZsoTw8HDixYvHw4cP6dixI0OGDMHBweGTtNVisTB58mQCAgI4f/48yZMnZ/v27Xh5eUVaJ2KAp3v37jx58oSdO3dSsGDBT9JOEXm3iO+ntm7dSsmSJQEoV64clSpVombNmkaVncuXL5M3b14KFSoUZVW+oquJEyfSoUMHWrZsSfv27UmdOvU714sY4CldujRdunR5K+Qj/93du3dp0KAB69evB14HeQYMGGAsjxiWihjgad68ORMmTIiSNouI/C8U3hERERERERER+UodPHiQevXqkS9fPvz8/MiQIQOPHz+mYMGCPH36lEyZMrFp0yYKFizI6NGjjakC3qSpVkTkS/ZmHxYaGoqTkxMA4eHh2NnZ8dtvv+Hj40OaNGno27cvlSpVMpbbgjmhoaEcPXqUoUOHsmvXLu7du8fFixc/2ZSCtsHL0NBQ5s2bx6hRozhz5gzDhg2jcePGuLi4vPOYR44cyfjx49mwYQOenp6fpK0i8raIAYRffvkFT09Pvv32WwIDA1m/fj3Xr1/H09OTDh06kD9/fgoUKMD333/P6tWrWbp0KZUrV47iI4geQkJCqFy5MhcuXGDVqlVkzJjxL9d/9uwZEyZMoFu3btSqVYuZM2ca1xD5cE6cOMHo0aOZMmUKOXLk4Lfffov0v0nE8+fQoUO0adOGCxcu8OeffxIvXrwoarWIyL+j8I6IiIiIiIiIyFfizQHqBQsW0KpVK4KCgihdujQvXrygQIEC3Llzh8DAQKpVq0bDhg0JCgqiUKFCjBo1ily5cmkqABGJNiL2Z7t3745UeWbgwIGkTp2amjVrEhYWxpw5c2jfvj0ZMmSIFOCxVcqw9bFPnz7l5s2bJEyY8KNWJvurvtgW4Onfvz/Pnz9nypQplCtXLlI7Il4TXrx4QcyYMVVFTeQz0KdPH/z9/alZsyZBQUEEBwcTHBzM8OHD2bBhAwcOHMDBwYFevXrx7Nkzhg0bhp+fH/7+/lHd9C/G8ePHcXBwIHPmzG8tu3btGunSpaNevXpMmTLlH4XUHz16xNy5c/n+++8VgvzAIl7rjh07xvDhw5k1axYtW7bE19c30vMdcd3jx4+TJEkS3N3d9b+LiHwxFN4REREREREREfkKRBx42L59Ozly5ODBgwecOXOGihUrEhoaSv369dmwYQMDBw6kYcOGuLi4MH78eNq0aYOjoyNp0qRh0aJF7xzoEBH5khUpUoR9+/axbNkyKlSoQMeOHRk1ahRDhw6lTZs2uLi48OrVK+bOnfvOAM/7BgY/VmWyiCGbhw8fcv/+fSwWC+nTpzd+X0hICPPnz6dPnz6EhoYyadIkvvvuu0hTeEVsnwY3RaJGxPP57t27VK5cmZw5c9KlSxfSpEkT6Tx9/vw5K1euZMaMGWzbto2wsDAsFguxYsVi586dZM+ePSoP5Ytw5coV0qdPT4oUKdi6dSvJkyePtPzSpUukS5eOYsWKsWzZMmLHjh1pue31uH37Nrt27aJ69eqRlisE+b+L+LdusVgICQnBZDJFqhx3/PhxBg0aRFBQEB06dKBjx47vDfC8uU8Rkc+deisRERERERERka+A7UPrDh06ULduXVatWkXq1KkpXbo0AEePHmXLli2UL1+eBg0aGB+Sf/PNN+TNm5eiRYty8eJFEiZMGGXHICLysdSoUYPw8HDatm1LpUqVGDVqFF27dqVGjRpGf+ji4sKPP/7IqFGj+OOPP+jduzerVq0CwGQy8a77ZD/GgKHFYjEGhocNG0aZMmXImDEjmTNnJl++fEydOpVr167h7OxMnTp16NOnD05OTjRv3px169ZhNpvf2T4Fd0Sihu18Xrx4MevWrePw4cPUqlWLNGnSAK/PU1v/EitWLOrWrcuCBQs4fPgwdevWpUCBAjx//pzVq1cDRDrHJbK7d++SMmVKmjdvTr58+d45nZKXlxe5c+fm3LlznDp1KtKyiEEQPz8/xo8fz507dyKto+DO/8ZsNhvP7axZs2jWrBklS5akXLlyTJ06lWPHjgGQPXt2unfvTq1atQgMDGTkyJFcvXrV2M+b1zIFd0TkS6LKOyIiIiIiIiIi0VjEQYZVq1bRpEkTqlatSrdu3UiVKpWx3pIlS6hRowbTp0+nQYMGwOsP0du1a8fBgwfZv38/T58+JU6cOLqDVUSijYh36Nv6QZPJROXKlZk1axaxYsV6q897swJPv379qFChwidvr7e3N4GBgeTIkYMiRYpw4sQJjh07RnBwMPXq1aNr166kSZOG0NBQ5s+fT+/evbFYLIwYMYIqVapogFnkM7Jy5UqqVKlC3rx5CQ4OZu/eve+cys7WB9i+hoSEcOnSJX788UdCQ0M5evRopOpa8n/WrFlDxYoV2bNnD9mzZzcqukycOJH8+fOTI0cOY92AgAC6dOlCmTJlmDNnDm5ubpH2NX/+fHx8fChfvjxjxozB2dn5Ux9OtPLmtW306NG4uLiQMGFCrly5gp2dHd988w09evQwKt6dOHGCX3/9lYULF9KpUydat24d6X8bEZEvkT5lERERERERERGJpqxWa6TS86GhocSIEYP27du/9eG2rbLEsmXLuHDhAgCLFi1iy5YtpE+fnrCwMGLFihVpnyIiXzqTyYTFYgHg9u3bwOu+c//+/ezcuROIXPUCIlfguXjxIq1bt2bjxo2frL3wuirBqFGjaNu2LQsXLmTkyJEsX76c6dOn88033zB58mRGjRrFnTt3cHJyom7duvTr14+HDx8yYMAAwsLCPkl7ReSfyZIlCx07duT06dOcOnWKBQsWAG9XcbH1AbYAj7OzM6lTp6ZYsWKcPn2alStXfvK2fwn27NnDjz/+SKFChYz3wy4uLmzcuJFWrVrRrl07Tp8+baxfq1YtfvjhBzZs2ECNGjVYvnw5d+/eJTQ0lPHjx9O7d29ixIhB3759cXZ2fmflNfnnbH/XI0aMYMSIEbRr1449e/Zw6dIlNm3aRIsWLdi7dy+dO3dm7dq1AGTLlg0/Pz/q1KlDQEAAM2fONK7nIiJfKlXeERERERERERGJ5nx8fBg+fDiVKlUiSZIkTJw48Z3rNWrUiJkzZ5I2bVoSJUrEkSNHSJAgAbt27SJFihSfuNUiIh/XmxV1duzYwdmzZ3n8+DHdunUjefLkjBo1ih9++AF4HeqJGGAMDQ1l0qRJBAYGsmPHDhInTvzJ2t64cWNWrFjBnj17SJcunVGdw2KxcObMGZo1a8a5c+eYP3++MT1iaGgoK1eupECBAiRNmvSTtVVE3i9iZZ2LFy8yefJkAgICKFSoEAEBAeTOnfsvt7f1Y5s2baJMmTJMnTqVn3/++VM0/YsRHBxMrVq1OHfuHFOnTqVw4cIAPH78GDs7OwICAhg+fDh58uRh7NixZMqUCTs7Oy5evEivXr1YsWIFL168wMPDA5PJxIMHD0iTJg1r1qzBy8vrrepI8u9ZrVZu375NlSpVCA4OZuXKlXh5eRnLw8PDGTx4ML169aJcuXJMnDjR+N/k+PHjTJw4kW7duuHp6RlFRyAi8mHoNikRERERERERkWjOYrHg4uLC6tWr+eOPP3j27FmkO4Rt3w8bNgwfHx8eP37Mw4cPKVeuHLt37yZFihSYzeaoar6IyAdnNpuNEM6OHTtYu3YtRYoUoVGjRnTp0oUpU6Zw/fp12rdvz/Lly4HXlQFs29iq9LRp04ajR4+SOHHiT3LHv8Vi4eXLl+zYsYNYsWKRLFkyLBaLMXBsZ2dHxowZadGiBQ8fPmT06NHA637eycmJ6tWrkzRpUvXpIlHkzX4iYugjderUNG3alHbt2rFjxw6GDRsWqRrMu9jZ2fH48WNWr14NgJOT04dv9BfK9v42PDycy5cv4+TkRL58+QDo3LkzPXv2xNHRkbZt2+Lt7c2+ffto06YNZ86cwWKxkDp1agICApgzZw41a9Ykffr0fPvttwwYMICtW7cquPMBmUwmnj9/ztmzZ8mTJ48R3LG9hg4ODrRr144ff/yRdevWcf78eWPb7NmzExgYiKenp65tIvLF08SXIiIiIiIiIiLRyLsGEYYPH07MmDEZPXo0R48e5fDhwxQrVsxY11aq3s3NjSFDhtCqVSsSJEiAo6Mjrq6uGpgQkWglYtjF39+fyZMnc+PGDfbt22dUuWjcuDGAMZBusVioWrUqACtWrGDq1Kk0btyYKlWqEDNmTICPMqWg1Wo1+mjb73B1dSVHjhxs2LCBW7dukSZNmkj9tL29PZUrVyZ58uRcvXqVFy9eGG20UZ8u8ulFPE9Xr17N8ePHOXPmDIUKFSJXrlzky5ePNGnS0Lp1aywWC6NGjQLAz8+PzJkzA2/3CVarlbNnz7JkyRIqV67MTz/99OkP7DN17do1PD09iR07NhkyZDCmFjx06BBTpkyhR48ehISE4O7uTqtWrQAYPHgwbdq0MSrwJE6cmMqVK1O5cmVCQkJwdnY29q/3xx9WWFgYZrOZmzdvYjabsVqtODj83zB2nDhxKFu2LHPnzmXt2rWUKFHCqDxlW0+vh4h86VR5R0REREREREQkGrF9aL148WKePHliPN6vXz/at2/P06dPqVWrFhcvXsTe3j7SHaq2u1u9vLyIGzcurq6uWK1WfRAuItFGxGmvunTpQr9+/ShVqhRbtmwhX7582NvbG31h48aNjQo8LVu2ZMqUKUydOhVfX1/Wr19P3rx5P2pbLRaLMUh/7949Hj16ZBxDtmzZePbsGd7e3rx8+dLoz21tjxs3Ls7OzsZXEYlaEUOD3bp1o1atWvj7+7Ns2TJatmxJtWrVGDZsGABp0qShY8eOtG/fnqCgIPr378+ZM2cAIgV3bD+nTJmSgQMHsmzZMuN3fe3WrVuHl5cXCxYsAGDMmDHkyZOHTp06MWXKFNq1a0fnzp2JFy8eVquVRIkS0apVK7p16xapAk9Eb/alen/8v4n49xkSEmJ8nzRpUr799lu2b9/OwYMHcXBwMNYNDw8HoGTJksD/VZj6GKFZEZGopF5NRERERERERCSaGTNmDDVr1qRz5848e/bMeNzf359evXpx7949ChYsyIULFyIFeGwDQhEHht4cJBIR+ZLZ+rR58+YxZswYmjdvTu/evSlWrFikdSIGeKZPn87jx49p3rw5rVq1wmKxcPr0aZIlS/bRpuiIOK3X+PHjqV+/Pn5+fty7dw+TyYS3tzfZs2dnxYoVdO3a1QjwRDy+mzdv8u2332pwU+QzYDsP+/Xrx9ChQ6lduzbbt2/n6tWrLF++nBcvXtC1a1dGjhwJQMqUKY0Az5IlS/Dx8eHcuXPv3HeSJEmoU6cOgFGJ5Gu2d+9e6tatS9GiRUmSJAkAiRMnJmXKlLx69QpnZ2fixIlDrFixgNfP2fsCPH83bZn8OxGvbQsXLqR3794sXboUgHjx4vH9998TGhpK3bp1OXnyJHZ2doSHh+Pg4IDZbGb+/PkAkSpRiYhEJyarejYRERERERERkWglODiYwoULc+TIEZo0acLw4cOJHTu2sbx3797069cPDw8Pdu3aRZo0aYwPxkVEojPblDONGzdmxYoV7Nixg0yZMv3lugCbNm3i0KFDADRo0IDEiRN/tClTIg6++/j4MHHiRFKlSkX//v35/vvvjQoe586do1KlSpw/f55ixYrRpUsXEidOzKZNm5gyZQqhoaHs2rWLpEmTfvA2isi/d+LECSpUqED27NkZMWIE6dOnJzQ0lO3bt1O7dm3c3d3ZtWsXCRMmNLa5fPky/fv3Z/369Rw5ciTSMnlbaGgoP/30E4cPH2batGkULVoUgFmzZtGqVSuqVavGkSNHuHbtGn5+fjRp0oT48eMbIRCTycSdO3cYP348w4cPJ1WqVCxcuJD06dNH5WFFCxGvbT169GD8+PG4ubnh7+9PzZo1jf9DmjZtym+//Ya7uzszZ84kR44cJE6cmFmzZjFw4EAcHBzYvHkzbm5uUXk4IiIfhcI7IiIiIiIiIiLRiC2E8+rVK4oUKcKhQ4f+MsCTLFkyNm3apEEJEfkqWCwWXrx4QYYMGUiaNCkHDx58Z3jRFtx5XxWLjxXciWjIkCH06NGDdu3a0apVq0j9tK19V69e5ccff2T37t3GMmdnZzJkyMCyZcvw8vL6JG0Vkb+3fPlyqlatyrJly6hcuTLh4eEsWrSIbt26YWdnx4EDB3BzcyM0NJR79+6RLFkyAK5fv06sWLGIFy+eKuv8jZCQEAoXLsyjR484ePAg8eLFo3PnztjZ2fHNN99QqFAhHj16RIMGDbh48SJ+fn40bdrUmD4LXgd47t69y9ChQ1myZAl79uzBw8Mjio8s+kqEurkAAQAASURBVOjVqxcDBgygZcuWNG/enJw5cwKRr6tt27Zl3LhxwOvKUi4uLly/fp2kSZOyZcsWvLy8dC6ISLSk8I6IiIiIiIiIyBfqzQFZ24fY/zTA4+/vT58+fciRIwcHDx7Ezs5O02SJyFfh22+/5f79+xw7doxYsWJFqrJj60vv3r3LihUraNq06Sdv35UrV/juu+9IlCgR06dPJ1WqVADvbOerV69YtmwZp06d4vnz5+TMmZOKFSvi5uam4I5IFIkYLLCdtyNGjMDb25sDBw6QI0cOFi5ciK+vL3Z2duzfvx93d3cAbt26hb+/P+3bt49UGSzi+S/v16pVKyZOnMjAgQM5d+4c06ZNo0ePHvj4+BghnYMHD9KyZUvOnz+Pn58fzZo1eyvAc//+fZycnIgTJ46CIh/Itm3bqF69Ot999x0DBw7E09Mz0vKIYdq5c+eyY8cOtmzZQsqUKcmRIwedOnUiSZIkuraJSLSl8I6IiIiIiIiIyBdu9erVfPPNN7i5uRkDO7YPv213IB86dIjGjRszfPhw4sSJY2wbEBBAzZo13/rwXEQkOrJYLADUrl2bxYsX07t3b3x9fXF2do40aAvQsWNHJk6cyLFjxz55dbIdO3ZQrFgxxo8fT4sWLf6nCkAabBaJGhFDNk+ePCFu3LgArFu3jvLlyxMYGEjGjBlp2rTpW8EdgPr167NhwwZ27txJ2rRpo+QYvkS2Pu/Zs2fUrl2bTZs2ERYWRuvWrfH39ydBggTGa2O1Wjl06BAtWrR4K8DzZt+p0NSHM3bsWNq1a8eGDRsoVarUO9d58/l/+vQpceLEMa53Cu6ISHSmd+4iIiIiIiIiIl+wcePGUalSJQYOHMjDhw+NAQkHBwfCw8NxdnZmzZo1ZMmShd9++42OHTvy9OlTY3tvb288PT0JDw+PwqMQEfmwbCGdN9nZ2WFnZ4e/vz+JEiVi5syZLFy4kJCQEEwmkzFAu2DBAtasWUP58uWNqWs+pdu3bwOvB/5t7Y7IbDYDcOfOHS5duhRpmS2EpOCOSNSw9SOdO3emZs2axuOpU6cmVapUdO7cmYYNG+Lo6Mi+ffsiBXemTJnCjh07qFSpEsmTJ//kbf+S2fq82LFjEy9ePMLDw7G3t8fR0THS1Ii2ME6ePHmYOHEiadOmpX///vz22288fPjwrb5TwZ0P58yZMwBGKO3Na7XZbMbOzo5Hjx4Zj8WMGRP4v9dXwR0Ric707l1ERERERERE5AtWqVIl8uXLx+jRo98Z4LFYLLi5udG4cWMAFi1aRJMmTXj+/Hmk/UQc1BAR+ZLZBv8AVqxYQf/+/WnevDndu3fn4sWLvHz5kkyZMtGnTx8ePXpEly5d8Pb25uTJk5w7d45ff/2Vnj17EhoayqhRo4gZMyafuoC9bWDz6NGjxuCmrQ1Wq9UYvOzcuTPTp08nNDTU2FYDzSJR78mTJ+zYsYONGzeyceNGANKlS0fHjh0xm83cvn0bf39/PDw8jG1mzZrFkCFDiB07Nv369cPFxeWT9z1fOtuUWEuXLqVBgwZ88803TJgwgWHDhnH79m0jpPlmgCdjxoz4+PiwYMECPecfUYwYMQDYvHkzwFsVjmyVddq1a2ecN7brna5tIvI1UHhHREREREREROQLEfHuVFulHE9PT5YsWUKePHkICAiIFOCJuH7WrFnJkycPzs7OrF27NtJAr4hIdGGxWIyBvq5du1KrVi38/f2ZN28egwcPplChQkyYMIHbt2/TqFEjAgMDcXZ2Zty4ceTPn5/s2bPj7+9P7Nix2bJlC8mTJ8dsNn/yQUNPT09KlizJ/PnzmTJlCvB/A5e2r9OmTWPdunW4uLioEoHIZyZu3Lj069cPOzs7Nm3aZDzerl07evfuDUC9evVo1KgRfn5+lC9fnvbt2xMWFsbKlStJkiRJlPQ9XzqTyUTevHnZsWMHAwYMYPbs2RQoUIAhQ4Ywfvx47ty5Y6xnC/DkzZuXwMBAvvvuOypVqqTn/COqUaMGcePGZc6cOTx48MB4PDw83HhNBg8ezMaNGwkODo7CloqIRA2TVRFSEREREREREZHPntlsNgZnFyxYwMaNG+nQoQNZsmQB4MaNG9SoUYN9+/bh7e2Nr68vCRMmBF7fydqyZUtevHjB7NmzuXv3Lh4eHlgsFk2rIiLRwpv92YABA+jVqxfNmjWjRYsW5M6dmxUrVtCtWzf++OMPhg8fTseOHYHXU1RNnDiR27dvY7VaKViwIBUrViRhwoSR+t5PbcmSJfz000+EhIQwfPhw6tWrh5ubGwBz586lX79+ODo6smHDBhIlShQlbRSRd7NarTx8+JBq1aqxd+9e9uzZQ+7cuY3lU6dOZfbs2ezfv5+QkBBSp05NsWLF6Nu3L0mTJo3SvudL8mbf/+bzZrFYOHPmDO3atWP37t1069aN1q1bv7PPDAsLw9HRUc/9R3Tnzh06derE/PnzqV27Nr/++iuenp7G87148WL8/Pxwd3dn2bJlJEiQIIpbLCLyaSm8IyIiIiIiIiLymYs4MNG9e3emTJlCSEgIgYGB/Pzzz8Z6N2/epEaNGuzdu5emTZvSp08fkiZNyvz58+nTpw/FihVjwoQJwOtBJQV3RORLtnHjRrZu3Ur//v0jPX7q1CmqVKlCunTpGD16tDEF1fz58+nYsSNOTk4cPXqUBAkS/OUgbVQFHG3VIABmzpxJx44defz4MdmzZyd9+vTcuXOHo0ePEj9+fLZu3YqXl5fCmCJRxNaHRDxvI34/ZswY2rdvT5cuXRgwYAAWiwUnJyfg9dRa9+7d49GjR6RNm5aYMWPi5OSk8Mg/FPF5Wrp0KYcOHeLKlSu4ubnx888/kzJlSuLGjYvFYuHs2bO0bdv2bwM88vGdPn2a9u3bs3nzZvLly0fx4sUpWrQoK1eu5Pfff8dqtbJ79248PT11bRORr47COyIiIiIiIiIiX4hffvmFAQMG0KxZM9q3b0/mzJnfWufmzZvUqVOHnTt3kjBhQpImTcrp06dJnDgxe/bsIXny5FHQchGRD2vLli2ULl2a7777jgkTJuDp6WksW7duHeXLl2f69Ok0aNAAs9nMwoUL8fX1xc7Ojv379+Pm5kZYWBihoaHEjBkz0iBwxIH3D+3NQfn3/a6IA5Zr1qxh+fLlLF26lBcvXpAyZUoKFy7ML7/8QrJkyTTQL/KJ/FWVl5MnT5I1a1ZjWcQqLsWKFeP27dscOnSIuHHjEh4ejoODwzt/x8fsf6KTiK9Fly5dGDVqFPb29sSMGZMHDx6QMGFCmjdvTrNmzfDy8gLgzJkztGnThj179uDr60uzZs1ImjRpFB5F9PFvr0N//PEHY8aMYenSpdy8eROAGDFiUKhQIaZMmYKnp6eubSLyVVJcUURERERERETkC7BlyxYmTJhA9erV8fX1fWdwByBp0qRs2bKFDh064OnpSXh4OJUrV2b37t0kT54cs9n8iVsuIvJhXbhwgebNm1OkSBG6d+8eKbgDr6cRBEiXLh0AQUFBdOvWDZPJxL59+4ypp65cuUL16tW5d+9epAHCjzlwbvs9PXr0YOPGje/9XXZ2dlgsFgDKly/PhAkTOHHiBEePHmXv3r2MGTNGwR2RT8wWFjl+/Djwf+ezt7c32bNnp379+sydOxcAR0dH4HXIpHz58ly8eJEBAwZgtVrfG9yBj9v/RCe212LgwIEEBATQuHFj9u7dy71799ixYweJEydm4MCBTJs2zehLM2XKxJgxYyhSpAh9+/Zl7ty5xjL5b2znwsqVK//R+hkyZGDw4MEcPnyYJUuWsGjRIg4ePMjixYsV3BGRr5rCOyIiIiIiIiIiX4BTp07x+PFjmjRpYtxB/C7h4eHY29szYsQINmzYwI4dO5gzZw4pUqTQB+EiEi1cv36dK1euULJkSYoUKQLArFmzOHr0KADJkiUDYO7cucyfP5/u3bsbFXfc3d2N/QQEBLBjxw7jrv9PZfv27QwaNIj58+cTFhbG+4rjR6zwYbVa8fDwIG3atMSOHdsIBqhPF/m0ypcvT+3atdm2bZvxWPr06alWrRqLFy+mXr16lCpViqVLl3Ljxg0cHR1p0aIFXl5erF27ltu3bwO897yXf+7s2bNMnz6dkiVL0rlzZ7Jnzw68rkL58OFDUqZMSceOHbGzszOe78yZMzNs2DBq1KhB7dq1NSXTBzR69GiqVKnCqlWr/tH6MWLEwMPDgx9++IFq1aqRKVMmYseOjdVq1bVNRL5auiqJiIiIiIiIiHzGbIMNu3fvxmQykSJFCoC3KujY7hy2fbVarSRIkIAECRLg4uKiD8JFJNoIDg4mPDychw8fYjabad26NQ0bNuTcuXMAFClShMyZMzNp0iQ6deqEyWTixIkTRnDHarUyc+ZMfv/9d2rWrGlU6PlUihYtSvHixdm0aROPHj3CZDL97UC+qnGIfB5q167N5cuX+eWXX9i6dSsALVq0YM6cOWzfvp2qVaty4sQJqlevTrly5ZgzZw5WqxVfX19OnTrFokWLAJ3TH8LNmze5cOECtWvXJl26dISHhzN//nx8fHxwdnbmwIEDxI8fH7PZzIMHD4z3yNmzZ2fu3LlGsF0+DFtwdtOmTcDb/6u86X3ngM4NEfmaKbwjIiIiIiIiIvIZeXMA1/YBdrp06QgLC2P37t3A62oLtnWtVit2dnY8f/6cSpUq8eeff771wbc+CBeRL9mGDRto06YNAOXKlaNRo0aMGzeOggULMmHCBLy9vSlUqBAArq6uDBs2jMSJE3Pnzh3atm1LrFixjH1NnTqV/v374+rqyq+//oqrq+snq4JhGzyuUqUKV65cYeTIkYD6aJEvRaNGjZgxYwYHDx7Ex8eHjRs3Aq+nycqbNy8zZsxg27ZtNGvWjJs3b1K/fn3Kly/PunXrSJQoEWPHjjWChvLfXL9+HYvFQqpUqYD/myLRzs4u0hSJ169fp0mTJly6dMnY1jZ1mYLtH07VqlUpV64cs2fP5urVq3puRUT+BwrviIiIiIiIiIh8JsxmszGAe+XKFe7du2csK1CgAADjxo3jwIEDwOvB3tDQUGOb6dOns3XrVo4cOfKJWy4i8vFs2bKF8uXLc/78ec6fPw9Av3798PT05MCBA+TPn5+6desad/0DFCxYkF69epE4cWJ69OhB6dKl6datG6VLl6Zz586YzWbWrl1L0qRJI/W9H1LEQJDte9sULbVq1SJx4sRs2LCBp0+fvrW+iHy+atWqxeTJkzl8+DBOTk4AxtRMsWLFIlOmTEycOJHVq1fz66+/cuHCBTZs2MDt27exs7MjUaJEUXwE0UPatGlxcHBg9uzZLF68mB49erxzisShQ4eyceNGnjx5EoWtjf5MJhMlSpTgwYMHjB8/HrPZrOuaiMi/ZLKq5xQRERERERERiXJms9m4Q3XEiBFMmzaNrFmzMnjwYGOqrM6dOxMYGEjNmjVp06YNRYoUMba3DVq4u7uzYsUKEiRIECXHISLyIV25coWyZcuSKFEiBgwYYPR7HTp0YPTo0aRNm5bLly/Tq1cvmjRpQtKkSY1tX7x4wYkTJ/D19eX06dPcv3+fLFmyULRoUfz8/EiSJEmkvve/slgsRjjnr/YbHh6Og4MDgwYNokePHsyYMYP69et/kDaIyKdz48aNSKFBm4h9AcDJkyfZuHEja9euZdy4caROnfqtdeTfe/bsGYUKFeLkyZN4eHjg4uLCqVOniBkzprHOzJkz8fPzo1ixYkycOBFXV9cobPGXzWq1YrFYIl3brFYrJpPJuOaFhoaSJ08enJ2d2bNnD46OjsY6IiLy9xTeERERERERERGJYhE/1Pbx8WHcuHEUKFCAli1bUrNmTWO9s2fP0r9/f+bOnUuiRIn4+eefSZ06NXv27OH333/H0dGRXbt24enpqUEhEYkW9u3bR9GiRenUqRODBg0CYMGCBVy4cIHHjx9TvHhxJk2axO+//063bt1o3bo1SZIkibSP8PBwHj58yL1790ifPj1WqxUnJ6cPGtzZuXMnf/zxBzVr1iROnDjG4+3bt+f48eP88ssvpE+fnuTJkxvLtm3bRpkyZShSpAgLFiwgYcKEH6QtIvJp/dNwgi24Z/sq/zvb+9w9e/ZQrVo17ty5g5+fH/7+/sY6kydPZujQodjZ2bF582aSJk2qIMm/dObMGcLDw8mWLVukxwcPHkzOnDnJkSMHiRMnBjAq7fj7+9O/f39GjRpF27Zto6LZIiJfLIV3REREREREREQ+E6NGjcLHx4fWrVvToUMHUqVK9dY6ly9fZvbs2fj7+xMeHg5AwoQJyZs3LxMnTsTT0/ODDkiLiESlffv2UaBAARo2bMjYsWPx8fFhwoQJLF26lO+++w4XFxcOHz5M7969WbduHb6+vrRq1coI8ETsDyMO2n7IAdxHjx5RokQJTp06xaRJk6hRowaxY8fm4cOHFC1alEuXLhEaGkrmzJlp3749JUqUIHXq1AC0bt2aKVOmsGvXLvLly6fgpYjIv2A2m1m+fDmtWrXi3r17FCxYkKxZs3Lq1CkOHz5MkiRJ2LhxI15eXnp//C/9+eefZMiQgSpVqtCvXz+yZs0KvK72WbNmTUwmE3ny5KFWrVo0bdoUV1dXnJycOHLkCAULFqRIkSIsX74cFxcXBaZERP4hhXdERERERERERD4Dt27domrVqjx//pylS5eSLl26v1z/yJEjPH78mGvXrpEnTx48PT2JHTu2BiZEJNqxTRmYPXt2jh07hre3Nx07dow0Xc2RI0fo3bs3a9eufSvA87GFh4ezfv16fvnlFy5fvsygQYOoWbMmcePGJTg4mNOnTzN//nymT5/OgwcPSJUqFRUqVMDHx4d9+/bRunVrChcuTFBQEM7Ozp+kzSIin6v/5b3syZMn8fb25vz589y4ccOYIrFr164ffIrEr8Xdu3fx9fVl9uzZ1KxZk+7duxsBnrVr13Lo0CGGDx/Oo0ePyJAhA0WKFKFr166kTZuWPn360L9/f9avX0/JkiWj+EhERL4cCu+IiIiIiIiIiHwGTpw4wbfffkvr1q0ZNmzYO9exWq1Yrdb3VmXQVAAiEh09fPiQIkWKcObMGXLlysXYsWPJnz8/EHmQN6oDPJs3b6Zr165cv36dQYMGUb16deLHj2+sc+zYMY4ePcrAgQM5d+4cSZIkIU+ePGzduhVPT08juKnqOyIiMHDgQDJlysQPP/zwl+vZ3v8GBwcTFhbG9evXjSkSHR0dFdz5D+7du0efPn0YP348devWxcfHh1y5chnLz5w5w86dO5k8eTIHDx4kXrx4VKpUCZPJxNKlSylXrhxTp04lduzYUXgUIiJfDv0HICIiIiIiIiLyGbh//z6vXr3i5cuXAISFhUVabrFYMJlMPH36lNOnT79zHwruiEh0YrvvdMiQIZw5c4asWbNy9OhRFi5cyKVLlwCwt7fHYrEAkCtXLvr27Uu5cuUICAhg6NCh3L1796O302Kx4ODgQMmSJRkyZAjJkiXD19eXxYsX8/TpU2O9HDly0LBhQ7Zv386aNWvIly8f+/fv5/nz55w+fZrZs2cDKLgjIl+9Q4cO0bNnTw4cOPC365pMJqxWKzFixCBOnDhkzJgRBwcHHB0dARTc+Q/c3d3p3bs3LVu2ZN68eQwbNozjx48byzNlykSzZs3Yt28fc+fO5fvvv2fhwoXMnz+f58+fc/ToUR49egRgXKtFROT9VHlHREREREREROQzcOnSJYoWLYqrqyt79+4lfvz4RvUF2x3FVquVcuXKkTp1akaOHKnpVUTkq3DixAkWLVpEsWLFmDlzJrNmzaJNmzZ07twZLy8vgEjVao4ePUr79u25evUqx44dI27cuB+sLW9WxQkJCTH6YtvA5MaNGyNV4Kldu7ZRdeDN7Tds2GBM95IqVSrWr19PmjRpPlh7RUS+RLdv36ZSpUpcuXKFrVu3kiVLlqhuUrT2ZvXO8PBwHBwcjJ8fPXpEz549mTBhAnXr1sXX15ds2bIBr284sAWlADZt2sS+ffuYNWsWf/zxB+3atSMwMPDTHYyIyBdMEX4RERERERERkU/kr+6h8vT0pHDhwvz555906NCBZ8+eYWdnR2hoqBHcmT9/PidPnsTV1VWVGUQkWnrzznyLxUK2bNno1asXJUuWpEuXLtStW5exY8cyYsQILl++DLyuVmPbNmfOnIwbN44DBw4QN27cv+x7/y1b3zt//nyePXtmBHc6duxoTHloq8CTPHlyfH19CQoKMirw2LY3m80AlClThk6dOhEYGMilS5c4cuTIB2uriLzfm32N7nP/vCROnJiaNWvy4MEDtm7dCvxfvykfVsTgzo0bNwCM4M6UKVOMmwr69u1Lq1atmDdvHoMGDeLEiRMAODo6GlP7ApQqVYoePXqwefNmvLy82LhxI3fu3ImCIxMR+fLoUx4RERERERERkU/AbDYbH4yHhoZy69YtLBaL8UG3vb0948aNI2vWrMyePZsGDRpw8+ZNY6B3+vTp+Pv7EydOHHx8fCLd4SoiEh2YzWajz7t8+TJ79uxh79693Lp1yxhIzJIlC927d6du3bqMGTPmrQCPbXA3a9asuLu7G1MOfkgNGzbkxx9/ZOLEiQB07dqVUaNG8fDhQ168eBFpCi1bgGfBggWRptCyTeNiG/AsWrQoLi4uTJ8+nfDwcAUJRD4yW1/j7e3NwYMHjaC0fFpvPucRQyBNmzYlQ4YMTJo0iZCQEE1/9ZHYrpHffvst33//PVeuXAGgTZs2NG/enD/++AOz2WxMofWuAI/JZHqrck+SJEno0KEDZ86cYfv27Z/+wEREvkAK74iIiIiIiIiIfGRms9kYcBgzZgxly5YlZcqU5MuXD29vb169egVA/PjxWbp0KTly5GD58uVky5aNb775hkyZMtG8eXNCQ0NZs2YNSZIk0d3HIhKtWCwWo5/s168fxYsXp1ChQhQuXJiMGTMyePBg/vzzT+B1gKdnz56RAjy2wcY3B3c/RpWytm3bkj59evz9/SlUqBDDhg3Dz8+PVq1aETt2bKxW63sDPM+ePYu0L9uAZ9KkSfHw8MDe3h4HB4cPHjgSkbfNnDmTESNG0KVLF44dO6YAzycWseKLrW+09YlWq5U4ceJQpEgRTpw4wbx584xt5ONImDAhR48epV27djRo0IDx48fTqVMnSpYsaVxbPTw83hvgASLdlACvK4sC3Lp16xMfjYjIl0nhHRERERERERGRjyjigLS3tzcdOnTg9u3b1KtXD2dnZ0aOHEmZMmV4+fIlAGnSpGHnzp106dKFPHnycPv2bZIkSULnzp3ZuXMnXl5ekcJAIiLRgS1k061bN3r37k3GjBmZOXMmo0ePplChQvTt25cePXpw6tQpADJlymQEeCZOnEjfvn25fv36R2+n1WolX758bN68GavVyoEDByhcuDANGzYkZcqUkaqsvSvAs3DhQp48eRJpn8+fP2fy5MlcvXqVHDlyfPRjEJHXGjRoQO/evdm3bx+dO3fmyJEj7w3OvRkaUYj6v4lYFc3Pz49GjRqxcuVK43k2mUw4Ojri4+ND7NixWbNmjfG4fFi2KeRWr15N8+bNWbVqFbNnz6ZJkyb069ePFClSRKqI9K4Az8mTJ4H/e31MJhNPnjzh8OHD2NnZES9evCg5NhGRL41DVDdARERERERERCQ6sw1IDxkyhPHjx9O6dWtatmxJlixZuHXrFtmyZWPXrl0UKVKEHTt24OrqSsyYMRk0aBAmk4mbN2+SNGlSwsPDcXBwUHBHRKKtpUuXMn78eH7++Wd8fX1Jly6dMcC7du1azp07h5eXl7F+pkyZ8PPz4+nTp2zcuJGAgICP3kbbwOS2bdt48eIFsWLF4tChQyxbtow2bdrg4uKCxWIx+v6IAZ4ePXrQtGlTYsSIQd26dY19hoeH88cff1C2bFn8/f2ByBUpROTDCw0NxcnJid69e2MymejTpw+tW7dm3bp1xI4d+73n37lz50ifPj329vaRznX55yK+l925cyenT59m2bJlLF26lHLlylG2bFnatGmDvb096dOnp0KFCgQFBdGwYUMqVKgQxa2PfmxTTtrb25MwYULj8XPnzvHo0SNcXV2xWq2R/tZtAR6ASZMm8eTJE4YNG0bGjBmNda5evcqAAQP4/vvvadCgwac7IBGRL5jJqhpzIiIiIiIiIiIf1f79+2nUqBG5c+fGz8+PjBkz8ujRI4oUKcK9e/fInDkz27ZtI1++fGzdupUYMWIQFhaGo6OjMTCkgVwRie58fHyYOnUqGzduJE+ePISFhbF06VK6dOmCo6Mje/fuxc3NzQgz2pw/f5748eOTMGHCj9ZXvjlIf/r0aU6ePEmCBAlo3bo1N27coGfPnnh7e+Ps7GyEjmxtMZvNrFmzhsDAQKZNm0by5Mkj7f/27dskTpz4nb9LRD6sdwWhe/bsyTfffEOVKlXeu92mTZsoU6YMzZo1Y+LEiR+7mdFSxD66R48ebN++nRUrVnD06FF+//13Zs6cyYMHD8iYMSO1atWiUaNGXLhwgTJlytC5c2eGDRumPvIDivh6hIWF8dtvv3Hnzh2OHTvG0qVLKV26NGPGjCF9+vTGuhGf//v379OpUyc2b97M8ePHI4V/ANavX0/ZsmUBXdtERP4JhXdERERERERERD6yoKAg6taty6ZNmyhRogQvXrygQIEC3L17l7Fjx1KsWDFq1KjB9u3byZs3L1u3bsXV1VVVdkTkq2CxWLBYLOTPn5+wsDCOHTtGaGgoS5YsoVu3btjZ2bF//37c3d0B2Lt3L2azmUKFCr21n48xMBixL96xYwdPnjyhQoUKxu86ceIEVatW5datW/j5+dG5c2ecnZ2N7S9cuECKFClwcHAgLCwMZ2fn9/bvCmqKfDotWrTg/v37BAUFRQoEvs+qVauoXLmyMY1TwYIFdc7+jyZMmED79u1p1KgRPXr0MKqqXbt2jWnTprFu3Tr27NlDrFixaNSoEdOnT8fJyYmDBw9GqsAm/7uI16Hz58+TIEECEiRIYPxNN2jQgNmzZ1O6dGnGjh1LunTpIoVnnz59Spw4cXjy5AkWi4X48eO/9zqs4I6IyD+jnlJERERERERE5CMrUaIEa9eupUSJEoSGhtKwYUNu3LhB3759KV++PG5ubrRt2xZXV1cOHjxIxowZCQ4OVnBHRL4KdnZ2ODg4kDZtWu7du8eff/7J+vXr3xncAWjTpg3e3t4EBwe/tZ8PLeLg5oABA6hTpw7du3dn//79xjrZsmVj2bJlJEmShP79+0eavmvFihX88MMPTJ48GTs7OyPU877+XSEAkY8n4r3so0ePZubMmZjNZm7fvv2Ptq9UqRKzZ8/mxYsX7N69G9A5+09ZLBbj+5CQEHbs2EGZMmXo2bOnEcaxWCykSJECPz8/du3axeTJk6lQoQLjxo3j+fPnPHz4kKlTp2I2m1Fdgv8m4rVt5MiR1K1bl7p16/L06VPMZjMAM2fOpH79+mzcuJE2bdpw9uxZHBwcsFgsrFy5kp49e3Lo0CHixo1L/Pjx35paKyIFd0RE/hn1liIiIiIiIiIiH5mHhwclSpQAXk+1snnzZipUqEDDhg1xdXUFIF68eMSIEYOSJUvy5MkTnjx5EpVNFhH55LJmzcrt27fp1KkTrVu3xt7enn379kUK7owYMYIbN25QpUoVnJycPmp7rFarMbjp4+ND3759yZ8/P+PGjSN//vyR1suaNSvLli0jadKk9O7dm5YtWzJo0CB8fX25dOkSFSpU+KhtFZG/ZpvKzubkyZOUKVOG4cOHvzWN3V8pVaoU1apVY8aMGdy8efNjNDVasoU3fvnlF0aMGMGZM2eoWrUqKVOmNII9bwY8mjRpwvz589m5cycdOnTA09OTtWvXvvVayr9jsVgiXdt69OiBq6srbdu2JU6cODg4OBgBnhkzZhgBnubNm3PkyBEWLlxI165dmT59OilSpDD2q9dEROS/+/s6gCIiIiIiIiIi8p85OjoCcPHiRR4/fkzNmjVxcXExlq9atYrs2bOzcuVKQkJCiBcvnqbNEpGvgm2Kji5durBhwwZWr15NvHjx2L59Ox4eHsZ6QUFBTJgwAS8vL5o2bfrR+0fbQOTo0aMZP348bdu2pV27dqRKleqd62XNmpXff/+d6tWrM2nSJBwcHEifPj0nT57Ey8tLfbpIFIoYHnny5AnLly+nV69epE6d+l/tJ1GiRHz33XfY29uTOHHij9HUaOvcuXPMmTOHS5cuARAeHg68HdqJ+LPVaiV//vxkyZKF2LFj079/f2bMmEHTpk0/XcOjGdvz++uvvzJq1Chat25Nq1atyJAhg7FOxGvVjBkzsLe3Z/r06XzzzTc4ODiQNGlSjh07hoeHh6bEEhH5gExW1ZYTEREREREREflkNm7cSNmyZalYsSLz588nZsyYLFy4kJ49e5InTx5mz56Nvb29MZgtIvI1sPV569ev55dffuHYsWM0bNiQH374gYQJEzJ37lyCgoJwcHBgx44dRrWGjz1g+PDhQ3744QcePHjAokWLyJQpk7FswYIFnD17lrt37+Lj42NM/fL8+XMWL15MzJgxKVasGO7u7gruiHwGbt26Rbp06bC3tydWrFiMGDGCWrVqERYWZoSs/0rE92a27xVc+Hd+//13xowZw7p166hZsybDhw8nWbJk/2jbs2fPkjlzZlq3bs2YMWM+ckujt1OnTlGuXDly5cpFYGBgpFDq4cOHefjwIR4eHmTIkMGY7jEgIIDz58/j6OiIr68vSZMm1bVNROQDU3hHREREREREROQ/ePND67/7ENtsNvPdd9+xefNmMmfOTIIECThy5AgJEiRg586dkcrPi4hEB28Obv9VODE0NJQ9e/bQr18/Nm/ejMlkwmq1EjduXPLmzcvUqVPx9PT8ZAOG169fJ0eOHJQuXZqgoCDCw8M5dOgQ48aNY9asWTg4OBAeHk7atGn5/fffSZcu3Vv70OC+yOfj2LFj1K1bl7Nnz1K+fHmWLVuGo6PjPz5P3xXgkbfZnps3v8LrapNDhgxh3759DB48mBYtWhAjRoy/3de9e/fIli0bhQoVYv78+f8ocCXvtnbtWipUqMBvv/1Go0aNMJvNXLt2jdGjRzNhwgSCg4NxdHTE39+fVq1aESdOHGNb27mi4I6IyIen/xhERERERERERP4D24fWs2fP/kfBHXt7e9auXUvDhg0JDQ3l1q1blC1b1gjumM3mT9V0EZFPwjYgvmPHDkJCQv5ysNvJyYlixYqxceNG5syZQ2BgIL/++isrVqxg0aJFnzS4A68HjZMnT87ChQsJCAigZcuWVK9enZUrV+Lr68vKlSvp1KkT58+fZ9CgQcY2ESm4I/J5sFqt5MiRg6CgIDJkyMCaNWv49ddfCQ0Nxc7ODovF8rf7iNh/Kbjzbmaz2XhuwsLCeP78Oa9evTKWV6pUiW7dupErVy66d+/OzJkzCQ4Ofu/+TCYTL168YP78+dy9e5fcuXMruPMfOTg4ALBr1y6uXbvGsGHDqFKlCuPGjaNSpUp4e3uTNm1a+vbty9mzZyNta7umKbgjIvLhqfKOiIiIiIiIiMh/FBgYSKdOnVi6dClVqlT5yzuxw8PDcXBwwGq1cuvWLezt7YkTJw4xYsTQHawiEm0NHTqUwYMHc+XKFWLGjPmX/eRfVcD4WFVs3lUdwmbGjBl0796d27dvEzNmTL799lvGjh2Ll5cXzs7O3LlzBy8vL5o0aaKpXEQ+A3/VT9jO8ePHj1O1alXu3r2Ln58fnTp1wsnJSZWy/qOI72UnT57MunXrOHv2LDFixOCnn36iaNGi5M6dG4DVq1fTt29fjh8/zogRI2jYsOF7K/DcuHGDVq1a4ezszMKFCwFVPvo3rFYrVqvV+Nu+du0aTZo0YePGjcY6GTNmZNKkSWTLlo24ceMa/9+MGzeOli1bRlXTRUS+Kg5R3QARERERERERkS9dgQIFcHFxYcuWLVSpUuUvBxIcHByMgaGkSZMaj1utVgV3RCRaCg8PJzQ0lIcPHzJ58mQ6duz4l/3kXw2cf4xB9YiDzY8ePeLly5eEhoaSMmVK7O3tadiwIRkyZODx48fEjRuXXLly4eLiYmw7d+5cTCYTuXLlAjSgLBKVIp7PW7Zs4dSpUzx69IgsWbJQrVo149zMnj07S5YsoWrVqvTv3x9AAZ7/KOJ7WW9vb0aOHImnpyepU6fmwYMHdO7cmYIFC9K5c2eqVatGhQoVsLOzo3fv3nTu3Bl7e3t++uknXF1d39p3smTJGDx4MJkyZQI0HeE/EfE5MplMhIeHY2dnh9VqJUWKFIwePZoNGzZw8+ZNMmbMSNWqVYkdO7ax/eXLl3F3dydv3rxRdQgiIl8dVd4REREREREREfkPrFYrd+/epXr16uzfv5+tW7dSsGDBqG6WiMhn5fTp0xQoUICcOXOybNky4sWL91kEXCIO9I8ePZrFixdz5swZQkNDqVChAqVKlaJx48bv3NZqtRIUFES/fv1wdXVlzZo1uLm5fcrmi0gEEcMKPXv2JDAwkJcvXxrL69ati4+PjxG0Azh27BhVq1bl3r179O7dm3bt2uHs7PzJ2x6djBkzho4dO9K6dWvatm1L+vTpCQ4OplmzZsydO5dKlSqxcOFC43leu3Yt/fr1Y8+ePcycOZN69epF2t+bgUgFJP9exGtbUFAQO3fu5NChQ2TOnJlvv/2WWrVqETduXMLCwt45BdmCBQvw9fUlXbp0LFy4kDhx4nzqQxAR+SoplioiIiIiIiIi8h+YTCYSJUpE06ZNCQ8PZ/v27cDrD81FROT1QGvmzJlp3bo1O3fu5ODBg5/FwOubVSI6derEjRs3KFSoEMmTJ2fx4sU0a9YMX1/fSNtYrVaCg4Pp2bMnvr6+PH/+nEWLFuHm5obFYomqwxH5qkWcEqhHjx4MGjSIypUrs3XrVq5cuUKHDh2YN28e/fr1Y+/evcZ2OXLkYOnSpSRJkoSuXbsyderUqDqEL57VauXhw4csWrSITJky0bp1a9KnT094eDgrVqxg3759eHl5MW3aNJydnQkNDQWgXLlydO3alQoVKlCsWLG39vvm9eJzuH58ziJe23x8fKhfvz6//fYbJ0+eZNq0abRo0YIffviB+/fv4+jo+Nb/LAEBAXTv3h2r1cqUKVOIEyeOrm0iIp+IwjsiIiIiIiIiIv/A+8I4tserVq1Knjx5mDhxIg8ePNAUWCLy1bD1g+/qJy0WizHQWqRIEaxWKwEBATx+/PhTNvGdbO367bffCAwMpGPHjqxdu5YlS5awceNGgoKCiBUrFkOGDKF3797GNufPnydz5syMHDmSzJkzs3PnTlKmTInZbNY0LiJRxHY+z5s3j99++42WLVvi7+9P0aJFSZw4MWvXrsXV1ZXly5fTu3fvtwI88+bNI3/+/Hz//fdRdQhfPJPJxKNHjzh8+DAlS5YkY8aMhIeHs3jxYrp27Up4eDj79u0jYcKEAFy7do3bt28DUKVKFRYtWkSKFCkUgP+PbOfC4MGDGTlyJC1btuTAgQOcP3+e7du3U6BAAbZt20bx4sV5+PAh9vb2hIWFsW3bNgoVKkTfvn1xc3Nj69atxuuha5uIyKeh3lZERERERERE5B1Onz7NtWvXjJ9tYZyRI0cyefJkrFarUZLeYrEQN25cihUrxpUrV5g7dy6A7lIVkWjt0qVLwOv+MTw83OgnV61axYEDBwAiDfhVqFCB6tWrc/DgQe7fvw9EbT9ptVoJDQ1lzZo1xI8fn+bNm5MmTRoAPDw8qFKlClu3biVWrFiMGjWKdevWAZAuXTr69+/PuHHjmDt3rjG4qdCmSNR68uQJy5YtI1myZDRr1oy0adPy9OlTsmfPzuPHjxk8eDDt27dnw4YNDB48mN27dxvb5smTh+3btys88i+863kKDg7GbDYTO3Zs4PX0S127dsXOzo79+/fj7u4OvH6tateuzdatW7FarQC4uLgAqC/9AK5fv86sWbP45ptv6NSpE5kzZ8bDw4NChQqxdetWqlevzunTp2ncuDEvX77E0dGRuHHjkjBhQnx9fVm1apURStXrISLy6Si8IyIiIiIiIiLyhj///JNs2bLRpk2bSAGejRs30rlzZ1q0aEHRokXx9/c3BqABunfvTvLkyVm+fDmA7lIVkWhr3759pEmThs6dOwPg4OAAwOTJk6lcuTKlS5emXr16HDx4kAcPHhjb1alTh4cPHzJ48GAgavtJk8lESEgIp0+fJmnSpKRPnx54HeoxmUxYLBZy5crF6NGjefLkCTt37jS2/emnn2jYsCHx4sXDYrFocFPkMxArVizMZjMtW7YkZ86cBAcHU7FiRR48eMCgQYNo06YNbdq0wcPDg+XLlzN48GB27NhhbG/rx3Q+/zO252njxo3GY4kSJSJt2rTMmDGDCRMm0KNHj7eCOwDDhw/n7NmzxI4dW9NgfQT379/n9OnTlC1bllSpUhlB2fDwcBwdHZk9eza5cuVi06ZN7N+/H4CcOXMyd+5cunTpgru7u65tIiJRQJ8giYiIiIiIiIi8wc7OjgYNGrBu3Tq6du1qBHhKly7N9u3b8fPz4/bt2/Tr14/cuXPTrVs3Nm/eTMKECSlXrhybN29m9uzZUXwUIiIfj6OjI+7u7owcOZKePXsaj+fNm5dFixaRLVs2FixYQPHixalYsSIrVqzg7t27fP/99+TIkYP169fzxx9/ABhVF6KCvb09Li4uXL16lbNnz0ZaZmdnh9VqJV++fDg5ObF582ZevHhhVJuwDTgrqCkS9WxBg6CgIBo3bgxAYGAghw8fpn379tSsWROAtGnTUqpUKXLmzMnKlSsZM2YMYWFhUdn0L9qgQYMoW7YsmzZtAsDd3Z3vvvuOGzdu0K1bN6xWK3/++acR3LFarcybN4+ZM2dSvHhxChcuHJXNj7aeP38OvK4kGhwcbFynHBwcCA8Px9nZmebNm/PixQsOHjxobBcrViwcHR0BXdtERKKCel4RERERERERkTekSZOGXr160bRpU4KCgujSpQtXr14FoHDhwvj7+3P06FGGDBlC5syZCQgIoHTp0nTs2BGTyYTJZGLPnj1A1A5Ki4h8DBaLhdy5c7N27VrSpEnDwIED6d69OwC5cuWiWrVqrFixwpia4+zZs/zwww+UKFGCyZMnU6dOHa5du2ZUa/gUVRci9sW2781mM66urlStWpUnT54wf/58oz22qRFNJhOZM2cmXrx4JEmShJgxY6oSgUgUe9d0e7aggclkMs7Rffv2ETduXFq2bImrqyvw+vw/ePAg+fLlY/z48QwbNswIK8i/lyxZMgAjvAMwZMgQvvvuO549e0bq1KkJDg4mPDwceD39bK9evQCYMGECcePG1TSzH0HOnDnJkCEDR48e5c8//4y0zHauZM6cGXg91ZmIiHweFN4REREREREREXmH1KlT07lzZ1q2bMmCBQvo1q0bV65cAV4P/MSMGRNvb2/Wrl3LokWLaNCgAb/99htz587FarUybdo0jh07pqkARCTasbOzM6aUWrBgAWnSpGHw4MH06NHDWCdBggQULFiQGTNmsGXLFoYPH05wcDDt2rUzgj7jxo3j0qVLH729thCOje172wB/iRIlSJEiBf7+/owbN85Yz97envDwcKZNm8a9e/fIli0bFotFoUyRKGQ2m43wwfbt25kxYwZDhw5l0aJFkSqMmM1mrl+/jqOjY6RwwqxZswgJCaFatWq0aNGCFClSGNW05N+rVasWpUuXZsqUKZGmmp0zZw7ly5dn27ZteHh4kC9fPjw9PenWrRsxYsRgy5YtJE+ePNLrKf/O+0JPFouFGDFiULduXc6fP0+vXr148uSJsdxWVW7r1q04OjqSKVMmQDcciIh8DkxW9cYiIiIiIiIi8hWzWq2RBnVDQ0NxcnIyfj5//jzDhw9nwoQJ1K5dmyFDhpAiRQrg9YfjtgEHi8XChQsXGDt2LCdOnGDLli20b9+egIAATCaTBiZE5Iv1Zj9pNpsjVZ/Zv38/P/30ExcuXKB79+4MGDAAgJCQEJydnY31Ll68yPHjxwkMDOTMmTM8efKE5cuXU7Zs2Uj96YcUcb/jx49n3759mM1mKlasSK1atYxltql2goOD6dChA2XLliV//vzMnj2bCRMmEBISws6dO0mcOPEHb6OI/DMRz+cePXowYcIEHj9+bCzPkiUL06ZNI1u2bDg7OzN06FC6detGnTp1+PHHHzl69CjTp0/HycmJbdu2GVM5yX9je57bt2/P4MGDcXJyMq4Zw4cPZ//+/Zw5c4Y0adJQpEgR6tWrh7u7+1vXEvnnIj53K1as4NmzZwBUr14dZ2dnTCYTFy9epH379qxevZqyZcvStWtX8ufPj6urK0FBQfTq1YvYsWOzYcMGEiRIEJWHIyIi/5/COyIiIiIiIiLy1Yo4IP3nn3+SLl06Y1lAQADVq1fHy8uLCxcuMHz4cMaPH//eAI/ta1hYGC9evOD777/nzp07HDlyhJgxY0bJ8YmI/FdvhmoiDhjOmDGDChUq4O7uzuHDh6ldu/ZbAZ53Dc5arVYWLlxI06ZNyZUrF+vXr48U8vkYunXrxtChQ3FxceHVq1cAtGzZkrZt2xpThyxevBh/f39OnDgBgIODA+Hh4WTMmJHVq1fj5eWlwWaRz8Avv/xC//79adCgAT/99BOZMmVi/PjxjBw5EkdHR1auXEmRIkX4888/6d+/v1GVB14HfFatWkXKlCk/Wmgwunmz33vzva/ZbCZv3ryEhoaybds23Nzc3grDP3v2jNixY7+1D/lvbNc2m5IlS+Lt7U3x4sWJESMGZ8+exc/Pj1WrVmEymUidOjWOjo6cP38eNzc3tm7dipeXl14PEZHPhHpiEREREREREflq2YI7ZcqUoXHjxhw/fhyATp060aVLF1auXInFYiFNmjR07tyZVq1aERQURNeuXY2pAWwfdNu+Ojg4EDt2bL777jvOnz/PvHnzouDIREQ+DFvfVqJECbp3724M4LZr144WLVqwceNGwsPDyZ07N0FBQaRJk4aBAwcaU2jZ29tHmpLGNoVV9erVKV26NAcOHIg01crHsHnzZmbMmEGLFi04ePAgmzdvpkGDBkyaNIlevXpx5MgR4HXFgrlz5zJv3jwaNGhAq1atGDduHNu2bVNwRyQKvOve8927dzNx4kTq1KnDL7/8QpkyZUiePDlZsmTBzs4OFxcXsmbNCkC6dOn49ddfWbt2Lf3792fevHls3ryZlClTarqm93jXVEy2fm/MmDHs37/f6NPt7OwIDQ3F3t6ehg0bcubMGWPqQScnp0ivny3IbntMz/1/N3HiRMaNG0f16tWZNGkSrVq14vjx47Rp04a5c+fy8uVLMmbMyPDhw5k8eTKFChUiODiYGDFi0LhxY3bv3m1c2/R6iIh8HhyiugEiIiIiIiIiIlHp2bNnpE6dmqlTpzJw4ECsVisLFiygW7duVK5c2fgw2xbggddTrwCRKvBEZG9vT9GiRbGzszPu9BYR+VIdO3aM69evM3jwYJInT86ff/7J2LFj8fb2plixYjg4OGCxWIwAT+3atRk0aBAmk4kBAwYYAR57e3vs7e0JDw/HwcGBatWqsWzZMs6ePUvatGk/WHttVdVsX8+fP4+rqyve3t6kTZuWzJkzkypVKhIlSsTw4cOxWq306tWLXLlykSVLFrJkyULt2rUj7dNisSi4I/KJPH36lNixY0ears/m3Llz3Lt3j59//pnUqVNjNptZsGABPXv2xN3dnf379xM/fnxevnyJi4sLyZIlI1myZBQpUsTYh87n97O97/3tt98oW7YsyZMnB2DWrFm0b98eR0dH6tSpY0w9aKuuU7ZsWeLHj8/cuXOpV68eqVOnjvT62fb7rtdU/jdHjhyhZMmSDBkyhFSpUvHs2TMqVKhAmzZt6N27N1arlR9//BFPT0/q169PvXr1uHv3LnHjxsXBwQEHBweFUkVEPjOKUoqIiIiIiIjIVy127NgEBATQp08fgoKCWLBgAfXq1cPb29uYUsF2l/BfVeCxMZlMPH78mN9//x2LxYKLi0tUHJaIyAeTI0cOZsyYQbFixWjXrh2jRo2ia9eudOzYkaRJkwKv+76IAR5bBZ6ePXsCr0ONtooODg4OPH36lL1792IymXB1df1gbbVV9oHXIR6z2UyCBAkoVaoUadOmJTQ0FICUKVPSvn17fHx8WLlyJf369ePYsWOR9mPbB6hKhMinsn37dipWrMi2bdveWXnn4sWLAOTJkweABQsW4OvrC8C+fftwc3MD4PLly7Ro0YKwsLC39qHz+a+NHDmSpk2bMmHCBG7dugVA/fr1mTJlCrVq1WLWrFnUqVOH6tWrM3v2bJ48eULmzJnp1asX586dY+fOncC7KyfJ/yZiBTubU6dOkT9/flKlSoXVaiV27NiUK1eOyZMn4+DgQJ8+fZg3b54xVaTJZCJRokS4uLjg4PC6toOCOyIinxdV3hERERERERGRr16sWLF48eKF8fO9e/e4c+cObm5u2NnZRRp8iFiBZ8qUKTx58oQpU6YYA9gA165dY9GiRVSsWJFmzZp9ugMREfnAbNVrChYsSJw4cSJVtEmWLJmxnslkeivAU7t2bQYOHMizZ88YNWqUMWButVrZsmULs2fPpmbNmpQsWfKDtDViNY0RI0awdu1abt++TUhICO7u7litVpycnIxKA8mSJaNt27YADBs2DAcHB3x9fcmdO7exH1WJEPl0wsPDOXz4MLt27aJPnz7079+fQoUKGX2LnZ0dsWLFAmDNmjXGOWtnZ8f+/ftxd3c39tW7d28OHTrErVu38PT0jKpD+iLVqVOHs2fPMnjwYEwmE02bNiVlypQ0btyYxo0b8/PPPzNhwgQ2bdrE0qVLyZ49O3369MHd3Z306dMzaNAgihUrRsqUKaP6UKKFiNe2efPmcenSJcLDw0mYMKHx/4fZbMbBwQF7e3tKlizJ5MmTadasGb1798bOzo66devqhgIRkS+Ayaroq4iIiIiIiIh8hWyDzwAhISGMHj2ap0+fcu/ePSZOnEiVKlXo2bMnefPmNda3Wq3G4POFCxfo06cPW7du5ejRoyRMmNDY98OHD9m/fz/lypUDMAacRES+JLZ+0mw2Y7FY+P7774kVKxaXL1/m8OHD/Prrr0bFCxuLxWIEeQ4fPkypUqVwcnLi3LlzxI0b11jvypUrzJkzhx49ehjbfah+snv37gwePBgPDw9MJhNPnz7FYrEwevRoGjdujJ2dXaSpQm7cuMG4ceMYOHAgTZo0Yfz48UZVAhH5tB48eMCcOXPo2bMnOXLkYNCgQUaAB+DSpUsUKFCAuHHj8vLlSxwcHDh06BAJEiQw9jF16lR++eUXatWqxeDBg42pneSfu3v3Ln369GHChAn4+PjQt29fYsSIYSx/9uwZN2/epH///qxbt45Hjx6RM2dOrl+/zp07d1iwYAE1atSIwiOIfrp27cqwYcMiPVagQAGWLVuGu7t7pOuo2Wxm8+bNtG7dmmvXrjF9+nTq1KkTFc0WEZF/QeEdEREREREREfnqRBy0vXDhAnHjxsXNzY3g4GAA+vTpw9ChQ98K8MDrweyQkBBcXFy4fPkycePGJX78+O8deFZwR0S+RBH7rjt37pAoUSLCw8Mxm80cPXqUzp07s2fPHgYMGED37t3f2iYkJARnZ2eOHTuGu7s7SZMmNcJAb/aL/7WfjNinnzx5krJly1K1alV8fHwwm838/vvv+Pv74+HhwYABA6hataoRSrJtd/XqVebMmcNPP/2kKh0iUezhw4fMmDGDX3755a0Az7Nnzxg4cCDjxo3jxYsXrFu3LlL1rtmzZ+Pv74+Liwvr168nceLEkQLb8s/duXOH7t27U7lyZX744Yd3rmO1Wjl58iRz585l9uzZ3LhxA3t7e65du0bixIk/bYOjmYjXxlmzZtG2bVuqVatG1apVuX37NjNmzGDPnj20a9eOXr164ebm9laAZ+3atfTr14/FixdHqpYnIiKfJ4V3REREREREROSrEnGwdsSIEcyaNYusWbMyaNAgo/T8gwcPGDp0KEOGDKFKlSr06NGDfPnyAbB06VKCgoIYOnQoKVKkANCgkIhEKxH7yfHjxzNjxgzq1q1Lu3btjEHBrVu30rNnz7cCPABr165l06ZN+Pr6GlXJIu7zY9m5cyc3btzAz8+PlStXkjFjRgBevnzJsmXLaNOmDUmTJsXf359q1aq9FeCxDXp+iraKyF978OABM2fONAI8AwcOpFChQtjZ2fHHH38wcOBA5syZQ758+ShQoAD58+dn6dKlbNq0CRcXF7Zv307KlCl1Pv9HoaGh761c9Gbw8uDBgxw9epTy5cuTLFkyPff/wZsVQqdOncqcOXOYNWsWqVOnxmq1cu3aNX766Sd27dpFhw4d6NGjx1sVeCwWC2FhYTg7O+v1EBH5Aii8IyIiIiIiIiJfjYgfZvv4+DBu3DiyZ89Onz59jCmubB4+fMiQIUMYMmQIFSpUoE2bNjx9+pT+/ftz6tQprl+/boR9RESii4j9pK+vL+PHjydFihT4+vpSr169SAOKEQM8/fv3p0mTJuzcuZNevXrx4sULDh48iLu7+0dp25uGDRtG165dKVmyJHZ2dqxfvz7SoHNoaCiLFy+mdevWJE2alH79+hkVeFQhTSTqves8vHfvHrNnzzYCPL/++iuFCxfGzs6OCxcusHDhQiZPnsylS5cAcHd3p0SJEgwbNozkyZMrrPCJvCvEruf+n/ura1Dbtm3ZtWsXVquVMmXKMHTo0EjTU96+fZtatWqxc+fO9wZ4RETky6HwjoiIiIiIiIh8dQYMGIC/vz+tWrWibdu2pE2b9p3r3b9/n8DAQAYMGACAi4sLSZIkYePGjaRKlUofjItItPXLL78wYMAAWrRoQceOHUmfPv0719uyZQt9+/Zl+/btJEqUiBcvXhAnThy2b99O6tSpP0o/6e/vT4MGDfDy8jIeO378ONWqVePixYukSpWKY8eOEStWrEi/P2KAJ2XKlHTt2pUff/zxg7ZNRP69iEGP48eP8/LlS7799ltMJtNbU2hFDPCYzWZevXrF0aNHCQ0NJUuWLMSOHZsYMWIoPCJfnB07dpA3b15ixIhhBKKqVavGsmXLiB8/Ps2bN2fgwIGEh4fj4OBgbBcxwNO5c2e6du2Kh4dHFB6JiIj8rxTeEREREREREZGvyvHjx/n+++/JlCkTY8aMiRTc2b17N8HBwYSFhRmVeMLCwliyZAkrVqzA09OTdu3akTRpUg0KiUi0tXXrVurWrUuJEiXo378/qVOnNpadO3cOs9mMs7Oz8fj+/ftZvnw5O3fuJFWqVPTv3/+jVb1YsGABderUoUaNGgQEBBjTFwKcOXOGunXrcvz4cTp06ED//v2JGTNmpHaEhoaydOlS6tatS/Hixfn999+JESPGB22jiPxzEc/PQYMGMWXKFNzd3QkMDOSbb74BeCvAE3EKrXfRdKbypWnevDnTpk0jKCiIChUq4OLiYixr0aIFkydPJn78+Gzfvp0sWbK89Td++/ZtfvzxR7Zu3covv/xC7969dQ6IiHyBFN4RERERERERka/Kxo0bKVu2LBMnTqRZs2aEhoZy/fp1xo4dy9ixY7FYLISHh9OjRw/69+9vbBexRL2COyISnU2ZMoUWLVowf/58atasidls5tGjR4waNYrJkyfz6NEjsmbNStOmTWnZsiXwerA8NDQUAGdn54/WT967d88YxK9cuTKDBw8mZcqUxvIzZ85QrVo1Ll26RK9evejcufNbVThCQ0NZs2YNefLkIXny5B+8jSLyz0QMIHh7ezNmzBjKli1Lhw4dKF26dKR13wzwDBo0iEKFCmEymRTWkS/e7t27ad26NY8ePWLEiBFvBXhatmzJpEmTKFq0KBMnTiRDhgxv/d3fvHmTtm3bMnLkSDw9PaPiMERE5D9SXWcRERERERER+ao8evQIgOXLl/PHH38wdOhQfvjhByZMmECFChXo3r07Hh4e/Prrr6xatcrYzs7OzviAXMEdEYnObty4gdVq5caNG1y+fJkpU6ZQuXJlBg0aRJo0aahRowZ//PEHo0aN4tKlS8Z2zs7OODs7Y7VaP1o/6e7uzs8//0y/fv1YsGAB7du3Jzg42FieKVMmFi9eTMqUKRkwYAABAQEEBwdjb2+P2WwGwMnJiSpVqhjVgUQkatjeV02ZMoWxY8fSsmVLRo8e/VZwByBBggQ0bNgQf39/jh07hp+fH1u2bFFwR6KFggULMmXKFBImTEjTpk1ZsWIFVquV8PBwACZMmMDPP//M9u3badOmDWfPnjWCazZJkyZl0aJFeHp66tomIvKFUuUdEREREREREfmqWCwWypQpw5YtW4zHsmbNyvjx48mUKRMJEiRgzpw51K9fn5kzZ1KvXr0obK2IyKdjGwT/888/qV27NqdOnSJOnDg8evSIVKlSMXLkSPLkyUPixInp2bMnAwcOZMeOHRQqVOiTt/X+/ftMnjyZzJkzU6VKlbeWnz59mqpVq3Lt2jV69uyJt7c3Li4uWCyW9061IyKfXkhICDVr1uTIkSOsW7eOzJkz/+X6Dx48YM6cOXTs2JEqVaoQFBSEk5PTJ2rtlydiuElBp8/fvn37aNCgAYGBgcYUvhErxzVu3Jjp06dTsmRJxowZQ8aMGfW6iohEIwrviIiIiIiIiEi08+bgbFhYGHZ2dsYH3xaLhX79+mG1WkmePDl16tQhVqxYxvre3t5MmTKFtWvXUqBAgU/efhGRj+2vQiwhISEcPXqUwMBAQkJCyJkzJx06dCBOnDjGOj/99BObNm3i8OHDJE2a9FM1O5KwsDAcHR3fu9wW4Ll9+zZt2rThl19+iTQNiYhEvZs3b5I9e3ZKlizJggUL/tE2d+7cYcWKFZQrV44UKVJ85BZ+uSL28y9fvsTV1VUBxi/A48ePiRcvXqTH3hXgKVu2LAEBAWTJkiUKWikiIh+DwjsiIiIiIiIiEq1E/HA7KCiInTt38scffxAzZkwaNGhA5syZyZAhwzu3tVqtLFmyhB49epAyZUoWLVoUabBaRCQ6iNhPrl69mlOnTnH69GnSpElDpUqVyJkz53u3tVgsLF68mK5du5IvXz6mT5+Oq6vrJ2r5v3fmzBkKFy5M/PjxOXz4sPp0kc/MxYsXyZEjB1myZGHNmjXEjx8/0nJb2OTBgwesX7+eOnXqGNMFmUymSP2ZvFuZMmXImDEjAwYMIE6cOArwfCHerKgT8W+9WbNmTJ06lVq1ajF79mwcHByiqpkiIvIBKbwjIiIiIiIiItFGxA+5fXx8GD16NCaTiVixYvHw4UOcnJwoUaIEfn5+75zmZciQIUycOJGwsDB27dpFihQpNMAhItFKxD7N19eX8ePH8/z5c0wmExaLBRcXF/r370/NmjWNihYR+9YxY8YQGBhIaGgou3btInny5J/9lB3nzp0jduzYJEmS5LNvq8jXqHz58hw8eJDVq1eTL18+wsPDcXBwiNRfNWzYkFu3bjFjxgySJEkSxS3+smTIkIELFy7g6+tL165dFeD5gkUM8Hh7e9O2bVtSpUoVxa0SEZEPRVdmEREREREREYk2bAOyQ4cOZfjw4bRu3Zr9+/dz69Ytli1bxg8//MC6devo2LEj+/fvN7Y7cOAAadOmZdCgQSRKlIgdO3aQIkUKzGazBjZEJFqx9Wl9+/ZlyJAh1KhRg7Vr13L8+HH8/Pxwd3ene/fujBs3jnv37gGvp6c6d+4cRYoUoW/fvsSOHZvt27eTPHlyzGbzZx+GSZ8+PUmSJPki2iryNSpTpgwPHjygYcOG3L9/36giYuuvFi5cyJYtW0iWLNlblXnk/SwWCwBnz56lQIECDBkyhH79+vHkyZN3vr+1Wq28eb+/bR/yebC3tyc8PByAgIAAUqVKZfwsIiJfPlXeEREREREREZFo5cqVK1SuXBlHR0cWLVqEl5eXsezFixd0796dMWPGULt2bcaMGUPChAl59OgRTZo0IUeOHLRp0wY3NzdNwyAi0dbJkycpX748uXLlYuzYsUaFHYA1a9YwaNAg9u7dy6RJk2jYsCGvXr1i+fLlTJgwgdy5c9O1a1cSJUqkflJEPoiQkBB+/vln5s+fT9q0aRk+fDhZsmTB09OTcePGMWbMGMLDw9m+fTvJkiVTBa1/yBa8sbOzw2q1kitXLo4fP87JkyfJnDnze7fbsWMHt27dolatWp+qqSIiIoLCOyIiIiIiIiISzRw/fpz8+fPTvHlzRo4cCUSe8uXevXs0atSIbdu2sXPnTnLmzAm8LkNvtVrfmqZBRCS6WbduHeXLlycwMJB27dphtVoxm81GtYslS5ZQv359YsaMyf79+/Hy8iI0NJRHjx4RP358nJyc1E+KyN+K2E+8L3BjCwEGBwfTpk0b5s6dS2hoKK6urjg6OvL06VMyZMjA6tWr8fLyUmjwPd7sk4ODg4kRIwYAFy5cIE2aNFitVrZv306xYsXeu59z586RMWNGYsaMycqVKylevPjHbrqIiIj8f/rvSkRERERERESilRcvXvDq1Stu3rwJQHh4eKTBooQJE1K2bFlevnzJ6tWrgdcDHvb29m9N0yAiEh29evUKINKguoODgzFdSrVq1fjpp5+4f/8+ly9fBsDJyYlEiRLh5OQUadv/QveVikRfEcMktvdi75qCyd7eHrPZTIwYMZgwYQJBQUG0b9+eQoUKUb58eUaOHMnWrVsV3Pkbtufa398fwAjueHt706lTJy5duoTJZDKCO+/rf+PFi0e7du0IDg5mx44dgKbO+i90nRMRkX9Dn0SJiIiIiIiISLSSLl06cufOzZYtW7hw4QIODg6YzWbg9eCRnZ0dZcuWBVBYR0S+SvHjxwdg0qRJXL582egDTSYTISEhABQuXBiA69evf5Q2RKzC8bF+h4hEHVu/0qpVK6pWrRrpsTfZ29tjsVhwcnKiSpUqjBw5kmXLljF37lzatWuHh4eHEbSW9+vVqxd9+vShbt26AHTp0oURI0aQLVs24saNG2nd90075uHhQceOHSlevDhTpkzh/v37ep/8L7wZdHpfaE1ERORddMUVERERERERkS/O+z4Et1gsuLm58d133/HgwQNq167NzZs3sbe3JywszAjyLFmyBID06dMDuitWRKKfvxosLFq0KHXq1OHEiRNMnjyZ27dvAxAWFoazszMAhw8fJmbMmGTJkuWjtM82cFyqVCnat2/PuXPnPsrvEZGoYbVaCQ4OZvXq1Rw7dsyo4vW+91xvBkRslWPet1zeVq9ePapUqUJQUBDp0qUjICCAnj170qxZMxIkSPCP95MqVSrq16/PrVu3OHny5EdscfRj+ztt2LAhs2bNMh5TgEdERP4JvdsRERERERERkS+K2Ww2PhhfsWIF06ZNY968eTx+/Nh4fMCAAVSoUIHDhw9TqlQpDh06xMuXLwFYsGABs2fPJmfOnBQtWhR4/93HIiJfooj95JEjR9i8eTN37tzh2bNnxjpdu3Ylb968jBo1il9//ZXz58/j6OgIwKJFi1i2bBm5cuUiderUH7Wt6dKlY9myZQwZMkQBHpFoJkaMGPTu3Zvr16+zfPlyQO+5Phar1UqGDBmYM2cOKVOm5OrVq6RPn55OnTrh5eVFWFjYP94PvA6fdOrUyajCJv/ciRMnmDVrFv369WPRokWAAjwiIvLPmKy6tUxEREREREREvkDdunVj6NChxs9p06ZlxowZ5MmTBycnJwCqV6/O0qVLcXZ2xtPTEycnJ86fP4+Hhwfbtm3Dy8sLi8Wiu7lFJNqIOB1V9+7dGT58OGFhYbi5uVGuXDl69epFunTpCAkJYdu2bfTt25c9e/aQJEkSKlSowPXr1zl8+DCOjo7s2rWLlClTfvR+0tfXl6FDh1KnTh169epFxowZ//LYbF/Vf4t8/o4fP06ZMmWIFy8ea9as+eiBwK9ZeHg4W7Zs4bvvvsPLy4vLly9Tq1Yt5s+fbyy3TRn7V8xmc6Qpyt78Wd7Pdn3auXMnpUuXxsvLi969extTmb1v/ff9LCIiXxf9ZyMiIiIiIiIiX5wpU6YwduxYatSowaJFi2jZsiWPHz+mSpUqLF++nBcvXgCwePFiRo0aRZUqVXj+/Dnx48enWbNm7NmzBy8vr0jVKUREogPboN+IESMICAigVKlS9OjRg1y5cjF79mxq167NsWPHcHZ2pnjx4sybN49WrVphtVqZOnUqZ86coVixYuzdu5eUKVN+tH4yPDzc+H7QoEG0bNmSefPmGVXS3ndsW7duZdCgQYSEhKj/FolitnvDzWZzpJ8tFovxffbs2WnevDnnz583qmupAsnH4eDgQJkyZdi0aROzZ8+mcuXKLFiwgBo1ahjLw8PD/3a62DeDOgru/HNWqxWLxULhwoVZv349586do2vXrty8efMvtzty5AgXLlwwAqoiIvJ1UuUdEREREREREfnitGzZkqtXrzJu3Di8vLwIDw9nwYIF+Pv7c+/ePSZMmEDFihVxdXU1trlz5w6JEiUiLCwMR0dH3UUsItGSxWLBbDZTpUoVYsaMyYgRI0iePDkAHTp0YPTo0aRPn56goCBy5MhhbHf37l2ePHlCkiRJcHBwwMXF5aP1kxEr5gQFBVG7dm0ATp48SdasWd+73dWrV8mZMyfPnj1j7ty51KxZU1UKRKLA8+fPiRUrFhD5fH7zHA4NDcXJyYkDBw5QqlQpsmfPzvr16yO9P5P/3d9VH7t8+TLt2rXj999/p1q1asYUTjaHDx/G2dmZLFmyfOymRktvPv+2v3eAFy9eEDNmTLZs2cKTJ0/44Ycf3rufAwcO8O2331KgQAEWLVpEkiRJPnbTRUTkM6VbE0RERERERETks2a7mzuip0+fGlMChIWF4eDgQJ06dejXrx8eHh60bNmS33//nZCQEGMbNzc3AGO6AAV3RCS6iFjFws7OjvDwcK5du8bPP/9M8uTJCQ0NBSAwMBBvb2/OnTtH7dq1OX78uLGdm5sb6dKlI1asWLi4uGC1Wj9aP2kb7PT19aVu3brGFIi2Qf/33W8aM2ZMevbsiaurK8uXLwdQcEfkE9u2bRs1atRg7969wP+dzx07diR79uw0atSIJUuWRAoy5MuXjxIlSnDw4EEOHjwIqPrOfxWxKtrq1asZO3YsM2bM4NChQ8Y6Xl5ejBkzhooVK7JkyRKqV69uLFu9ejVNmzZlyJAhxjVC/h3b879+/XoA4++9V69e9P1/7N1nQBTX28bh3y5VsCBFxQLYI/Zu7L33buwVFbAiTRRFURREFEWNlVgi9t47VuwlxliiUWPvCihseT/47mSxJCZ/EctzfUncnRnOzO6cmd1z73NGjyY+Pp4aNWoowZ33XdvUajWlSpXixIkTyuv3rs8/Qgghvn4S3hFCCCGEEEIIIcRny7jqw5IlSxgzZgze3t78+uuvyoCtoYqOWq2mVatWBAUFKQGe9evXKwMShu3IQK8Q4mtiPIC7d+9eli5dypEjR5Tn4K9+EiA0NDRFgOfcuXPv3G5q9JXGg/VHjhxh4cKFeHp60qRJkw/623Z2dvzwww80adKELVu2pAgfCSFSX3JyMrGxsWzbto2xY8cqQRyAQoUKUa9ePVatWkXr1q2pWLEiixcv5pdffgFeh/VMTExYtGgRgEx79z8y3Nd6e3vTuHFjPD096d69O82aNWPSpEnKcs7OzkybNo3GjRuzevVqateujYeHBx4eHly7do3Ro0croRPx7/3www/Ur1+fJUuWADBgwACCg4PJlCnTWwGc913bSpUqRWhoKJkzZ2b+/PmA/MhACCG+VXJ3JIQQQgghhBBCiM+W8cBEp06dCAwMJCwsjNOnT7N582YuXbqkLPdmgCd79uy0bdtW+TWsEEJ8bYyr4/j4+FCjRg1++OEHatasyS+//MKvv/6qLKtWq1MEeLy8vPjtt9+oUaMGFy5c+CQD6Ya/cfnyZY4ePQq8ngbxu+++++BtODo64ubmxqNHj1LsnxAi9ZmZmdGrVy/Gjh3Ljh07CAgIUM5lNzc3YmJiOHToEF26dOHx48d07tyZevXqMXHiRB49ekTp0qVZu3YtsbGxabwnXy7j6i1TpkxhypQpdOjQgblz5zJlyhQePnzIsGHDGDlypLKcIcDTrVs3Tpw4wbx587C3t+fkyZO4uLhIlZf/QYMGDXBycqJTp07UqVOHadOm4eXlRefOncmYMeMHbUOlUlG2bFnat2/P4cOHlc83Qgghvj0q/fvqtAkhhBBCCCGEEEKkEb1er/w6dfny5fTs2ZNWrVrRo0cPTp48ydq1a9m3bx9Dhw5lwIABZM+eHfirUo9Op2Px4sXMnTuXRYsWkTNnzrTcHSGESFWzZs1i8ODBNGnShIYNG3L27FnCw8MBWLVqFc2bN1cGfHU6nRL46devHytXruT06dM4Ojp+kraOHz+eiRMnUr16dczNzYmJiUlRZe1DLVmyhDZt2mBmZpZKLRVCvM/du3f58ccfCQ4Opnr16gQFBVGuXDnlea1Wy/3794mOjmb58uWcOHGCvHnzcv/+fRISEpg6dSp9+/ZNcb8n/tmbfWWfPn24efMmUVFRuLi4AHD06FHat2/P1atXCQgIICgoSFn+8ePH/PHHHzx69IgSJUpga2v7n/pfkfKzyt69e2nWrBkvXrygbt26bNiwAbVajU6n+1fB2PPnz+Pn58fq1aulMpUQQnyjJLwjhBBCCCGEEEKIz4rxl+H3799n3rx5bNy4kejoaHLnzg1AXFwcwcHBbNq0CR8fH/r37/9WgEev1/Py5UvSpUsnAxNCiK/KmwOCXbp04c6dO8yePRtnZ2cA5syZg4+PD48fP2bdunU0btz4nQGeJ0+eYGNj80n6SY1Gw5IlSxg1ahTXrl3DxcWFQ4cOkTVr1g/expuD/RqNBlNT09RorhDib7wZ4BkzZgxly5Z953KnTp0iNDSUa9eu8fvvv5M9e3YOHTpErly50qDlXz4fHx+SkpLYvn07Xl5edOvWDb1er/Tt7wvwvNl//ttwiUjJcPwiIiIYMmQImTJl4unTpyxevJgOHToAbx/zf9rW+/4thBDi2yCfaoQQQgghhBBCCJFm3vXFtOELbl9fX44cOUJiYiI1atQgd+7cJCUlYW5uTrly5Rg9ejQAEyZMAFACPIbKO2q1mnTp0gFIcEcI8cV6Vz9p+PfQoUOB19UUOnbsiLOzs9JP9urVC3NzcwYPHkzTpk1Zu3YtTZo0Qa/Xp6gIYGNjk2L6rdRkampK69atSZcuHePGjePs2bMsXrwYNzc3rK2tP2gbbw6CSnBHiLSRNWtW+vTpA0BwcDAjRoxIEeAxBAKzZs1KvXr1KF++PPfu3SM4OJiFCxeyb98+OnbsKCGFf0Gv13P37l1CQ0OxtrbGxsaGDBkyAK+DjGZmZuj1esqWLcvSpUtp3749Y8eORa1WM2rUqLf6Tznu/43hPWs4fr169SJdunTo9XrGjRtHx44defXqFd26dUOlUinBWePj/2ao533XeSGEEN8W6f2FEEIIIYQQQgiRZgxfTB85ciTF44mJicTHx7N3717i4uJ4/vw5AObm5mi1WgBKlCjB6NGjadiwIRMnTmTWrFncvHkzxXaFEOJLZgjaAFy4cCHF49euXWPLli1MnjyZLVu2cP/+fSBlP9mlSxcmT55M5syZadasGRs3blQGC437ydSYtkan073zcSsrKxo0aICfnx/Ozs5MmTKFrVu38vLly4/eBiHEx/G+CRyyZs1Kr169GD58OHv27GHEiBEcPXoUQKmCaFjfxsaGAgUKEBISgoODA4sXLwbknu3fypYtG6dOncLS0pI///yTtWvXAmBmZoZOp1PCImXLliUmJoYCBQoQFBREaGhoGrf866DVapX37LZt25gzZw7x8fG4ubnRt29fpkyZQs6cOenRowfz588HUgZ1Ll68yIsXL2S6OCGEEO8kd0VCCCGEEEIIIYRIUx4eHnz//fds3LhReSxdunSMHj1aKfO/du1adu3aBbweDHozwNOkSRPGjBnDkiVL3jtgLIQQXxrD4F6jRo1o1KgRcXFxyuMuLi5MmzaNevXqAXDq1ClevHgBpOwnDQGeLFmy0KRJE7Zv357qg4bGg5tnz55lz549rFixguvXr5OQkED69Olp0KAB48ePB8DLy4uNGzdKgEeIz5BWq1X6jAcPHnDjxg2uXLmiPO/o6EjPnj3fGeAxrGf4r16vx9HRkaJFixIXF8eNGzc+8d58Wd4MTalUKrRaLcWKFWPv3r1kzpyZRYsWMXbsWAClqpohwFOmTBkWLFjA999/T9u2bdNiF74qxlNOjhgxgs6dOzNw4EB+/fVX5frVokULpk6dSs6cOenZsyfz5s1TrocbN27Ezc2NiIiI9wbihBBCfNukpqgQQgghhBBCCCHSVN26dTl27Bjt27cnJiaGhg0bAmBra0u/fv1ITk5mzJgxhIeHkyFDBsqWLasMTJuYmFCiRAl8fX3JlCkT7du3l19wCyG+OqVKlWLv3r0MGjSIiIgIypUrB0CNGjVQqVQkJiayZMkS8uXLx6hRowBS9JNdunQhMTGRyMhIChQokKptNR7cHD16NAsWLODGjRvodDocHR1p2rQpI0aMIHv27Ep/P2zYMIYNG4ZKpaJRo0ZYWFikahuFEB/G0IcAhIeHs3LlSi5cuIBOp6NBgwY0b96ctm3bkj17dnr27An8NYXW2LFjKVOmTIrtqVQqXr16hbW1NRYWFhJg+BvGxx7gxYsXpE+fXnnM1dWVvXv3UrlyZQIDAzE1NcXX1zfFtIg6nY4KFSqwZ88ezMzM0Gg0MtXg/8DwGcPX15fQ0FB69epF165dqVixIvBXhZ3mzZsDMHDgQHr16sXt27dRq9VER0dz9+5doqOjpfKOEEKId1Lp5e5ICCGEEEIIIYQQaWzbtm0EBgZy5MgR1qxZQ9OmTZXnHj9+TFhYGOPHj6dJkyYEBARQtmxZIOXARnJyMmZmZm8NdgghxJfKeKqN0NBQxo4di7OzMz/++CMVKlRQltu3bx8BAQHs37+foKAgAgIClOcMg7gACQkJWFlZfZIBXF9fXyZOnEjDhg1p3rw5mTNnZurUqcTGxlKwYEF27dqFo6MjL1++ZN26dQwbNgxLS0sCAwNp1aqVBHiESGPG/Y+Xlxfh4eGUKFGC77//nvj4eFauXEnGjBnp3LkzISEhANy7d49Zs2YRHBxM7dq1GT58ON9//72yTY1Gw08//USvXr3o27cvUVFRabJvnzvje9nZs2ezbds2Dh06RMGCBSlfvjxjx45FpVKhUqk4e/YsVapU4fnz5wQHB+Pr6/vWNoxfS/G/Wbt2LR07duSHH35g+PDhODs7p3je+Jq7fv16Ro0axcmTJ1GpVBQqVIgNGzbg4uIin1eEEEK8k0RshRBCCCGEEEIIkWYMgwl169ZFo9Hg5+f31jKZM2fGy8sLvV6vDA4ZAjwmJibKl+RmZmYA8kW4EOKroVKplD5u2LBhvHr1ipEjR/Lq1Svgrz60atWqBAcHM3z4cEaOHAmgBHiMqzBYWVkBpHpwZ+PGjUybNo3u3bvj5+dHvnz5lOdiY2NJTk5W2mJpaUnjxo1Rq9V07dqVadOm0aJFi1RtnxDinxnCHgsWLCAyMpL+/fszcOBA8ufPD0CBAgUICAggNjZWCQZmyZKF3r17Y2JiQkBAAJkyZaJMmTLKPZpKpcLe3p6ePXsqwR0JlqSk1+uVe9mhQ4cSGRlJjhw5KFmyJJcuXSIkJIQTJ04wduxYihcvTtGiRdm/fz+VK1dm+PDhqNVqvL29U9wPy/H93xnep/v37yc5ORl3d/e3gjvw+pprWLZJkybkypWLy5cv8+TJE5o1a4aDg4MEd4QQQryXVN4RQgghhBBCCCFEmjIetLl37x5ZsmR553KPHz8mNDSUkJAQWrRowdChQ5Uy9UII8TUz/iX/pUuXlMFzSNmHxsbGMnz4cPbv38/YsWPx9/dPk/YGBgYyYcIEDh48SKlSpdBoNMTExDBixAj0ej3Hjh3Dzs6OxMRETExMMDc3Jz4+nt27d1OqVCmyZ8+eJu0WQvxFr9ej1Wrp2LEjBw8eZOvWrbi6uqLVaomJiSEwMBCNRqOcz69evVIqZt26dYvly5fTsmVLcuXKlWK7xpW/jPs2kdKUKVPw8vKib9++9OvXD1dXV65du8bQoUNZvXo1Xbp0Ye7cucDr4Pq5c+eoUaMGDx8+JDIyEnd39zTeg6+LTqdDo9FQtWpVrl69yrVr17C0tHwrGGUI5jx79oyMGTO+czvynhdCCPE+coUQQgghhBBCCCFEmlKpVBh+W/S+4A68rsAzbNgwhg8fzurVq5kzZw7JycmfqplCCJFmDNVzACW4Y+g3jfvQKlWqEBwcTLVq1QgICGDKlCmftJ2Gwf6TJ09ia2tLqVKlSEpKYuXKlfj7+6PX64mLi8POzg6AGzdusHTpUl6+fIm1tTWNGzcme/bsaLXaT9puIcTbVCoVz54948iRIxQrVgxXV1devXrFihUr8PPzQ6PRpDifr127xi+//AJA9uzZ8fT0JFeuXG+dz8aVvyTE8Nqbv7F//vw5P//8MyVKlMDDwwNXV1eSkpI4c+YMR44cIV++fISGhmJiYqJUcClSpAjbtm3ju+++o0mTJmmxG181tVqNubk5uXLlIiEhgQcPHqBSqVK8v3U6HSYmJrx8+ZKZM2dy+/btd25HCCGEeB+5SgghhBBCCCGEECLNfWg5/8yZMzN48GDGjx/PqFGjlGkYhBDia/fmgJ9xv/lmgCcgIIDmzZvTvHnzT9lEVCoVJiYm2NvbEx8fz6NHj9i8eTPe3t6o1Wri4uJwcHBQlu/WrRs///wzGo0mxXZkOhEhUp8hEGiQlJSk/L+hP7GwsMDS0lL599q1a995PiclJdGiRQtiYmKU7Rr6LDmf3+3ChQucP38eePs++O7du8TFxdGiRQsKFixIUlISq1atwtPTEzMzMw4ePKhMv3Tu3DllvZIlS3LmzBmcnJwkBPkRGIeqNBoNOp2OwoULEx8fr0xRaWJiglarTVFRJzAwkODgYG7dupUm7RZCCPHlkvCOEEIIIYQQQgghvii2trZ4e3vLwIQQQhgxDvDUqlWLpUuX4uzs/FYw5mMx/K03BzcBqlevzvPnz2nTpg0DBgzAxMREGWw2iIyM5Nq1a1SqVIl06dKlShuFEO+m1+uVoMGJEycAMDc3B2DixInExsai0WgwMzOjRIkSbNmyBW9vb4YNG4Zarebw4cMpzufw8HDu3LlDnjx5pLLIB/j9998pUqQIHh4e3Lx5863nDYGnhIQE9Ho9a9euxcfHRwlN2dvbA68DWG3btmXWrFlvrSuhqf/GONRmXFnH1NQUtVpNz549yZEjB9HR0SkCPGq1Gr1ez4oVK1i3bh0VK1ZMMcWlEEII8SHkLkoIIYQQQgghhBBfHMMvlGVgQggh/mIc4DEMxBtPUfOxaLVapR82Du8Y/lbNmjUpUqQIu3fv5sWLF5w6dQpHR0dl+eXLlzN16lScnJzo06eP9OVCfGKG87dWrVq0aNGCvXv3AjB48GB8fX05efIker0ec3Nz2rVrB0BYWBgajYaLFy+SNWtW4HXQYfny5cyePZuSJUvStGnTtNmhL4yJiQnt2rXDzs4OW1vbt563sbHBwcGBvXv3Mn36dIYOHYparebIkSMpQlMjRozgzp07uLi4KI99aDVL8TatVquEz37++Wfc3d1p0KABXbt2ZfPmzdy+fZtcuXIRExODnZ0dY8eOpWXLlmzcuJFjx44xfPhwvL29SUhI4McffyRjxoxvVbgSQggh/o5K/+ZkmkIIIYQQQgghhBBCCCHE/0tOTlamKdRqtUrYZvbs2ezevZukpCRKlCiBn5+f8tzZs2epXr06jx8/pmvXrrRt2xY7OzsWLlzImjVr0Ov1HDx4ECcnpxTTjQghPp3Ro0czevRoKlasiIODg1LhpW/fvjg7OyvLTZgwAT8/P7JkycLcuXMpW7Ys5ubmTJ06lfnz56PRaDh48CC5cuWS8/kDPXjwAGtra9KlS8fSpUtxdXWlWLFiyvPBwcEEBgZibm6Og4MDx44dSxHcWbJkCSNHjsTV1ZVFixaRMWPGtNiNr4Zer1eCT15eXkyZMoWMGTNia2vLvXv3ePnyJe3bt8ff35+CBQty+vRpOnbsqEx9BmBmZkapUqWUynfG10shhBDiQ0h4RwghhBBCCCGEEEIIIcQ77d69mxEjRhATE0OOHDmUx728vAgPD8fU1FSZLqt27dpMnjyZggULYmpqyi+//IKbmxuHDh1SKvRYWVlRqVIlZs+erUx/KIObQqSdefPm0atXL1QqFa1ateLHH3/ExsZGqRhiCOKEhYXh7e0NQObMmXn58iUajYbixYuzfPlyCSv8R+vXr6dZs2a0a9eOkSNHUqhQIQBOnz7NiBEj2LJlC23btmX8+PFky5YNgJkzZxIREQHA3r17yZkzp4SmPpLQ0FB8fX3p27cvHh4eFCpUiOvXr9O2bVvi4uLo1asX06dPx8zMjAcPHrB3717Onz+PSqWiRIkSVK5cGRsbGzkXhBBC/CcS3hFCCCGEEEIIIcRHYTxoIF9YCyHE294cXNVoNKkyrdXHotFo8PHxYfLkyVStWpUlS5aQPXt21q5dS/fu3fnhhx/o27cv5ubmTJ48mQULFlCqVCmioqIoXLgwpqam3L9/n0uXLnHq1ClMTEwoU6YMBQoUIEOGDHKtEOIzMHHiRHx9fQEoVqwYkZGRVKlSBXi7z9q9ezcHDhzgzJkzZMmShUqVKlGvXj1sbW3lfP6PLl++TGRkJDNnzqR169b4+/tTuHBh4PXxnjx5Mhs3bsTW1pYCBQrw8OFDbt68iYuLCxs2bMDFxUWO/Udy7do1mjVrRvr06Zk3bx4FCxZEo9Gwfv16hgwZgomJCXFxcdja2v7t9VuCVEIIIf4rCe8IIYQQQgghhBDio+rWrRtNmjShadOmyjQrQggh/jJgwAAmT56MiYnJZx/gefDgARMnTiQsLIwKFSqwZcsW5s2bR1RUFOvXr6dgwYIA3Lt3j9mzZzN+/HiKFStGVFQURYsWfe+AsvEUJUKItJGcnMzq1au5ffs2jx49YsyYMZQuXZrg4GDq1q0LvA4i6PX6vw2HSFjhwxgfp6SkJMzNzYHXoZGIiAgiIyNp3749fn5+FClSBICLFy9y8OBBoqKiSEhIIHv27NSsWZMePXqQJUsWCe78D968DsXFxVG5cmVCQkIYMmQIGo2GlStXMmzYMCW44+DggF6v58aNGzg4OJAuXbr3bk8IIYT4tz7fT4VCCCGEEEIIIYT44pw7d44VK1Zw+PBh0qVLR506dSTAI4QQRqKjo5k2bRrHjx9n3759yrRTn2OAR6fTYW9vj4+PD1qtlsmTJ9OgQQPy5MlD69atKViwoDK1TpYsWejbty8qlYpx48bRv39/ZsyYQfHixYG3BzVlgFOIT+/N89DMzIxmzZqh0+lIly4dmTNnZsiQIQwfPhyAunXrpgjlPH78mPTp0yv3dobtSXDnnxmHbFauXMkvv/xCyZIladKkCS4uLgwePBiAyMhIACXAU6BAAQoUKEDHjh0BUtxX63Q6Ce78R+8KPf35559oNBplerKYmBj8/f0xMTHh6NGj2NvbA3D//n3c3NwYMWIEFStWVNaX65oQQoj/ldxRCSGEEEIIIYQQ4qMpXLgwW7ZsQavV4uXlxaZNm0hOTv7g9aVAsBDia9eiRQtGjBjBkSNHqF69Olqt9r3BnbTuEw1/387ODj8/PwYPHsyZM2dYvHgxZ86cITExEbVarQzc29nZ4ebmhr+/P2fOnMHDw4Pjx48DMqgpRFrTarXKeajRaHj58iWJiYlYWFgo1UMGDRrE5MmTOX78OMOHD2fr1q3K+mvWrKFXr17KOQ1yXn8o45CNv78/vXv3Zs6cOWi1WuU+2dnZmcGDB+Pp6cnSpUsZP348v/zyi7INExOTFKEpQEJT/5FxJanevXvj5uYGQJEiRcicOTMLFy5k5cqVDB8+HLVaTVxcnBLcAZgwYQL79u2T978QQoiP7vP7OYcQQgghhBBCCCG+SIZfX1esWJH58+fTqlUrBgwYQIUKFciaNet7lz979ixJSUmULl1avgQXQnzVdDodGTNmxMvLC5VKRVBQEGXLluXEiRN/u96NGzfIlStXqrftzYFgw+Dm2bNnKVq0KL6+vuh0OhYtWsTFixe5fPkyRYsWTbGOIcCjVqvx9/cnKCiIlStXfpaVhYT4VhhXGYmOjmb79u1cuXKFzJkz4+3tTbly5bCysgJg4MCBAAwePJhhw4bx4MEDdDodISEhXLx4kalTp6bZfnypDH1rQEAAISEh9O3bl549e1K6dOkUyxkCPPBXBZ6AgAAKFSqUon+W++X/jeH4TZo0iZ9++olq1apx9+5dHB0dKVu2LFu3buX48eNYWVlx/vz5FFNjGYI9jRo1euv6J4QQQvyvVPq0/vmGEEIIIYQQQgghvjhvDvK+evUKCwsLABISErCysmLv3r28evWKunXrvnc7x44do1y5ctSvX585c+aQPXv2VG+7EEKkJUP/+fz5c8aNG0e5cuVo0aLFe5ffunUrDRo0ICQkBG9v71RvX69evahSpQpdu3YFoGfPnvz8888cO3YMV1dXHjx4wPjx45kyZQply5Zl5cqV7+y779+/z9KlS2nevHmqB4+EEO9nfM82bNgwJk+eTIYMGXBxceHPP//kxYsXjB49mg4dOpAzZ05lvaioKDw8PIDXUzU5OTmxbds2cufO/c4ph8Tf27NnDy1btqRx48aMHTsWJycn4O2pzAD++OMPJk+ezMyZM6lduzaTJ08mf/78adHsr4rxuZCcnEynTp0wMTFh3LhxuLi4APD7779TuXJl7ty5g5ubGzNmzFDWnzlzJpMmTQJev545cuR45+snhBBC/FfycwchhBBCCCGEEEL8a4YvvmNiYmjXrp0S3PH19SVz5swMGDCAatWqKcu/74vtx48fU758eXbu3MmpU6ckvCOE+Oqp1Wp0Oh0ZMmRg7Nix/zgAfvnyZQBGjx5N7dq1KVWqVKq1bc+ePcybN4+4uDhy5MjB2rVrWbBgAf369cPGxgYAe3t7/P39AZg8eTJt2rRh+fLlKfpvvV6Pg4MDHh4eqFQqGegXIg0Z7tnGjh3LlClT6Nu3L71796Z48eJs27aNhg0bEhQUxIsXL+jTpw85cuQAoH///uTJk4edO3eSKVMmevbsiaOjo5zPf+Pvjs3p06d58uQJvXv3VoI78O4qOs7OzgwcOJDnz5+zfft2bG1tU63N3xLDuRAaGopGo2Hz5s1MmzZNCe5oNBry5MnDmjVraNGiBbNmzWLPnj0ULFiQP/74gwsXLuDk5MTWrVvJkSOHnAtCCCE+OpkQUwghhBBCCCGEEP9J8+bN6dChA+Hh4QB4eXkxceJEEhIS0Gq1KZZ93y9S69Spg5+fH+nTp+fHH38kOTk51dsthBCfkk6ne+sxwwDih/xa393dnbCwMBITEzl8+PB7t/kxlCtXjuXLl3P79m06dOjA9OnTGTJkCKNGjVLCOTqdDjs7O/z9/Rk8eDCHDh2iTZs23Lp1S9mOYb8M/5XBTSHS1q5du4iOjqZDhw4MGjSI4sWL8/LlSwYOHIi9vT3Ozs6EhITw448/cvPmTWW9+vXrExoaSkBAgAR3/sGePXto0aIFjx49eus5vV7P2bNngdfBHMNjxgz3zg8fPgQgd+7cjB49mjNnzmBnZ5dq/f63wPhzya+//oqvry+RkZHY2NiQL18+4HVwxzC9Y7ly5Th8+DA9evQgffr0HDp0iAwZMjB06FD27t2Li4uLnAtCCCFShYR3hBBCCCGEEEII8Z/4+/uTOXNmfHx8qFSpEuHh4fj7+9O9e3fSp0//j+sbBi2aNm3KDz/8wKZNm3j+/HlqN1sIIT4ZrVarBHU2b97MpEmTGDp0KCEhIdy9exeNRvO36xsGa4cMGUKDBg2IjIx8a9rC/8XevXsJCgpS/m1lZUWrVq0oUaIEDx8+xN7envz582Nvb6+0R61Wo9fr3wrwdOjQIcWgvxAi7RgHPTQaDfv27ePFixf079+ffPny8eLFC0qXLs3jx48JDQ0lPDycPHnyEB4ezvz58/nzzz/fuV0JK7zfkiVL2LBhA7NmzXrrOZVKRaZMmQA4efIkkPI10uv1mJiYoNPpGDFiBHv37gUgZ86c2NjYoNfrP1q//63R6XTK+3b79u2kT5+eNWvWYG1tzc2bN5k7d64S3DG8Jlqtlly5chEVFcXBgwc5cuQIe/fuZfTo0WTLlk2CO0IIIVKNXO2FEEIIIYQQQgjxr2k0GsqVK8fZs2dRq9XExcVRpUoVPD09lV+j/hOVSqV8ST527Fjatm2rDGwIIcSXznjA0MfHh1atWjFs2DCmTp2Kv78/lSpVYsGCBdy7d++92zAEZQDat29P+fLlP1r7nj17RmBgIKNGjWL58uXK42fOnCExMZH69euTlJTE1KlTWbFiRYrgjqH/NgR4vLy8iI2NxcPDQ6pDCJHGjAN+d+7cwdTUlOrVqzNixAjKly/Py5cv6dChA3fu3GHs2LF07tyZunXrUr16deLj4wkLCyMsLIy7d++m8Z58Gfbu3cvOnTsZP348UVFR9O3bF4CXL18Cf4V06tevj4WFBZGRkUpYR6/Xo9VqlSplwcHBLFu2TFnX4EOqtIl3M5wLQ4cOpV69euzfv58aNWowefJknJycWLFiBdHR0Wg0GuUaZ7h2m5ubY2ZmhrOzMyqVSnlcgjtCCCFSi4R3hBBCCCGEEEII8a8ZysofPHiQ5ORk1Go1+/fvZ8mSJQDKr4f/ieFL8kyZMrFo0SJMTEw+KPgjhBCfO8OA4ZgxYwgNDaVjx47ExcXx7NkzlixZglarxdPTk9WrV//tdgyDtu3bt2f+/Pmo1eqPEpDJmDEjw4cPJyAggNq1ayuPFytWjFmzZjF79mzmz5/PrVu3CAwMZPXq1UpwxzDIaQjweHl5MWrUKKZOnSrVIYRIY4ZzsG/fvpQuXZqLFy9SvXp1JVSya9cuYmNj6dChA127dlXWq1KlCsWKFaNw4cKsWrUKS0vLNGn/l2Tnzp3UqFGDmTNnYm1tjZubG5kzZ8bLy4vatWvz5MkT5fUoXrw4NWvWZNeuXbRp00apNmkIgqxcuZKff/6ZIkWKUK5cuTTbp6+F8eeJAwcOsHTpUnr27EmZMmVInz499erVY+rUqVhZWTFu3DhiYmLQaDSoVKq3pjST8JQQQohPxTStGyCEEEIIIYQQQogvV/HixVmzZg2Wlpa0bdsWLy8vXr16ha+vrzKwq1Kp/vZL7zefk1+zCiG+FqdOnWLevHk0atQIHx8f8uXLB7yeIuXFixdkzZqVtm3b/uN29Ho9ZmZmyv//rwEZQwinTp061KxZExMTE7y8vNDr9UyaNInChQsD0LBhQ6ZPn467uzsjR44EoFmzZpiamqLVatmxYwcWFhZKVQ+VSiXTiQiRRgznNcBPP/3EsmXLqFOnjtJfGP578uRJnj17RrNmzZR+BWD9+vWkT5+eGTNm4OjoSKZMmVJsU6T0yy+/0L17d2rUqIGHh4cSdoqPj+f69escPHiQbt26sWDBAmxsbMiSJQtTp06lffv2rFq1ilu3btGgQQPKlCnDhg0bWL9+PXq9ni1btpA5c+aPOkXit8hwHTp//jxPnz7FysoKb29v8ufPD4CZmRkNGzZk1qxZ9OnTR7nGtWvXDlNTU3nvCyGESBNy5RdCCCGEEEIIIcQHeVelh3z58lG/fn3q1q3Lvn37yJAhA8OHD2fChAnA64EiwxffV69e5eHDh5+0zUIIkZauX7/OH3/8QceOHcmXLx8ajYalS5fi5+dHhgwZOHHiBJkzZyY5OVmZJuXNX/xDypDjxxhMNN6GiYkJ165dY8uWLUyePJkxY8Yoz1lYWNCqVSuioqK4ffs2I0eOZPXq1Wi1WrZt20avXr3o3r07iYmJyjYluCPEp2ccNEhMTOTWrVsULlyYCRMmKKFBg2zZsgGvQw0GMTExxMXFUb16dYoWLYq9vb0SwBbvdvLkSe7du0e7du2oVq0aAFu3bkWtVhMREUGfPn1Yt24dnTt35smTJwDkzZuXZcuW0bFjR65evUpgYCCNGjVi/vz5FChQgAMHDuDk5IRWq5Xgzkfg6+tLkSJFGDVqFDly5CB//vwpKvKYmprSsGFDfvzxR+Lj4xk5ciTLly8nOTlZ3vtCCCHShFz9hRBCCCGEEEII8Y+MBxEOHjzIpk2b2LdvH/Hx8ZibmwOvp1qJjY0lQ4YM+Pv7ExISoqy/bt06OnbsyNKlSz/KdC9CCPG5MQwI6vV6JYBz7do1AFxdXQFYtmwZPj4+qFQqjhw5gr29PQCXL1+mc+fOPH/+PE0GDF1cXJgxYwbVq1cnMDCQoKAg5Tlzc3NatmzJjBkzuHv3Ln379qV69er07t0bgO3bt5MuXbpP3mYhxF8M/YaXlxctW7Zk/vz5lCtXDhcXl7cCgSVLlqR48eL4+PjQu3dv2rZty+DBg9Hr9crUWoCER/7BvXv3SEpKUo69m5sbbdq04dChQ2TPnp3hw4fTs2dPNm7cmCLAkzt3bqKiojh06BBLlixhyZIlHDx4kNWrVyvBHQlBfhyVK1cG4NixY0pA9s2pfQ0BntmzZ5OcnEzfvn1Zv359mrRXCCGEUOnf9VMOIYQQQgghhBBCiP9n/Gvu4cOHExERQWJiIgAtW7akZ8+eNGjQQFn+7NmzVKlShWfPnuHu7o6zszNz587l+vXrnD9/Hmdn5zTZDyGE+FiMpzN5c2qTy5cvK5UuVq9eTatWrfD19aVkyZJ4eXmhVquJi4vDwcFBWadXr14sXbqUuLg4JeiTWt6cCsS4/bGxsQQEBBAbG8uoUaOUaUQANBoN+/btw83NjaSkJPLnz8+8efNwcnJCo9Fgamqaqu0WQvw9jUZDkyZN2Lp1KzY2NvTv35+xY8e+c/ql1atXM2fOHDZv3kz69OkpUaIEixYtkvDIP9i9ezdXrlyhV69e3L9/nwYNGnDv3j1cXV3Ztm0bgwYNYujQoeTIkQOAmzdvEhQUxJw5c2jYsCELFy4kc+bM792+TNX08Rje9zt37qROnToABAcH4+fnl+J5A41Gw5o1axg3bhwbNmwge/bsadJuIYQQ3zYJ7wghhBBCCCGEEOKDjB07lsDAQGrWrEmFChX47bffWL16NQUKFGD06NG0bt1aWfbcuXO0bNmSy5cvY2pqSoECBdiwYQMuLi4yKCSE+Gq4u7vToEEDGjduDLwO4cybN49Lly6RN29eHjx4QOnSpXn06BEZMmTA3Nycs2fPkiFDBmUbCxYsYMSIEdSrV4/IyMhUrWJj3P/q9XoSEhJQqVRYWVkpy+zbt48RI0a8M8ADkJCQwPPnz8mQIQNWVlbSpwvxGUlISMDT05P58+eTJUsWtm/fTtGiRZXn35xe6+LFi2TKlAk7OzsyZMgg5/PfMIRAmjdvzrRp08iePTsHDhygadOmPH78mEaNGhEeHk7+/PmVyi5qtfqtAM+iRYuwsbGRoM5HZDiWxsfU+L28e/duatWqhampKVFRUfTq1Qt4O8Cj1WpJTk7G0tJSzgUhhBBpQsI7QgghhBBCCCGEeCfjL601Gg3NmzfHzs6OoKAgnJ2diY+PZ+XKlfTs2ZPcuXMTHBxMmzZtlPWvX79ObGwser2eunXrkiVLFvkiXAjx1Vi/fj3NmjWjXLlyzJgxg3nz5jFjxgz69euHr6+vUnlhxYoV9O/fnwcPHhAZGYm7u7uyjXnz5jFhwgRMTEzYuXMnjo6OqTaga9z/zpkzh127dnHmzBkyZsxIhw4daNy4Mblz5wbeH+B5sw+XwWch0sa7wgqGIMKLFy8YOHAg8+fPp0WLFkyYMEGpBma87vu2Kd524cIF6tevT548eRg1ahRVq1YFoEePHixYsABbW1vMzMyYNm0a9evXx9raWpmuTKVSpQjwNG3alPnz5/9tBR7x4YyvS3fu3OHJkyeYmJiQKVMmsmTJoiy3fft26tWrR4YMGZg0adJ7AzxCCCFEWpLwjhBCCCGEEEIIIf5WVFQUOp0Of39/Fi9eTJMmTYC/Bnl+/vlnunTp8s4AjzH5clwI8TV58OABa9aswdfXFzMzM+7evcuQIUPw8fFJMSXWw4cPWbRoEaNHj0aj0VCrVi1KlizJoUOHOHjwIHZ2duzatStVK5MZD8oPHTqUqVOnkjVrVvLnz8/Nmzf5/fffad68Od26dVP6+P379zN8+HBiY2MJCgoiICDgo7dLCPHvGfcT8fHxPH78GDs7O7RaLenTp1ce79OnDz///DM//PADo0aNShHgEf/OqlWr6NChA6GhoQwYMACAXbt2sXDhQtKlS0eZMmWYOHEiL168YOLEiTRv3hwrK6u3AjzBwcHMmjWLzp07s2DBAglL/Y+MP1uEh4ezZMkSzp07h5mZGQUKFKBz584MGjRIWV4CPEIIIT53MhGxEEIIIYQQQggh3uvIkSN4eHjg6upK9uzZlakXNBoNpqavv1bo0KEDAF26dCEgIAC1Wk2rVq3e2pZ8KS6E+JrY29vTq1cvFi1axL59+8iWLRtly5ZVgjuGAXY7Ozu6d++Oq6srPj4+7N69m7Vr11KwYEHatWvHqFGjyJ49e6pWJjMMEE+dOpUpU6bQr18/+vbtS+HChbl48SKBgYHExMRgb29P3bp1sbCwoHLlygQHBxMYGMjIkSOxtrZm8ODBqdI+IcSHMe4nIiIiiImJ4dixY2TJkoVixYoxcuRIvv/+e6ytrZk9ezZ6vZ4lS5ag1+sZPXq0BHj+o2fPnpGcnExycjIAvXv3ZvHixWzbto1SpUphZWWFnZ0dw4YNw9vbGyBFgEev15MzZ058fX3JkCED7u7uEtz5CAyfLYYOHcrkyZMpWrQo7du35/Hjx6xfv56TJ09y6dIlpkyZgqmpKXXq1GHr1q3Uq1cPX19fEhMT8fT0lM8oQgghPhtSeUcIIYQQQgghhBDvlZCQwMKFC/H39+fx48cEBwfj5+cHvP0r1Z9//pmePXuSLl06oqOjady4cVo1WwghPonjx4/To0cPsmTJQlxcHN999x3jxo2jWrVqmJqavtVPxsfH8+jRI+7evYurqysmJiZYWFik+pSCer2ex48f07hxY5KSkvj555/Jnz8/Op2OlStXMnToUNRqNcePH1cqeBjas2vXLqKiopg0aRLOzs6p1kYhxN97s4JWREQEpUqVonLlyty/f5+VK1eiVquZOXMmnTt3BiAxMZGePXuydOlSOnfujJ+fH999911a7sYX6dmzZ7Ro0YLTp09TvHhxdu/ezcCBAxk2bBjZs2cH4OXLl2zdupVhw4aRkJDwVgUevV6PWq1WrgvGQXjx3y1cuJBevXrh4eHBwIEDcXJyAl5fuwYNGsS5c+cYOHAgkydPVtbZsWMHdevWpUCBAhw/fhxra+u0ar4QQgiRgoR3hBBCCCGEEEII8bfi4+NZtmwZAwcOJFu2bISHhyvBnDcHpufPn8/o0aM5ePCgMpghhBBfs1OnTpE+fXr27dvH0KFDyZcvHxMmTKBatWqYmJgoA7TG/aVxOMZ4QD41Xbx4kUKFCuHv78+YMWNISkpi1apV+Pr6olKpOHr0KPb29mi1Wv78809lABQgKSkJc3NzGWwW4jMwd+5cPDw86NWrF4MGDSJv3rwADBw4kMjISFxdXTlx4gTm5ubA6yC2m5sbixcvxt3dnYiIiFQNC35tDH306dOnqV+/Pnfv3qVOnTpERERQqFAh4K/74aSkJDZv3vzeAI9U2/n4+vTpQ0xMDLGxsRQrVixFUCouLo727dtz7do1FixYQJcuXZTXat++fbi4uKS41gkhhBBpTWrBCSGEEEIIIYQQAp1O997nrK2tadeuHZMmTeLmzZsEBQWxefNmAOUXxAbdu3fn/PnzyhQwQgjxtXhfP1miRAny5ctHy5YtCQ4O5vLly/j4+LB3716SkpKU4M6WLVuIi4sDSDFw/qkGcw1/08LCAoDVq1fj4+ODSqUiLi4Oe3t74HVgs1KlSqxevVpZ1xACkOCOEJ/G392Xbdq0iSxZstCnTx/y5s3Ly5cvWblyJWvWrKFAgQLs2bMHc3Nz5T7MysqKmTNn0r9/f7y8vCS48x9NnTqVu3fv4ujoSFxcHEeOHOHp06fA6/thvV6Pubk5DRo0ICwsDCsrK/z9/Vm6dCmJiYkS3EkFL1++5MyZM9jb21OsWDEAJbgDUK5cOaZOnQq8rrZjeF6v11O1alWcnJzk84oQQojPinzaEkIIIYQQQgghvnHGFSDOnDnDtWvXAMiTJw9FihQBXg/8dOzYEb1ez6BBgxg5ciQADRo0SDEFgGFZQAaHhBBfDeN+8tSpU/zxxx+oVCpy5MhB6dKlAbCxseGHH35ApVLh7++Pt7c3ISEhVKlShT179tC3b19evXrF9evXMTc3/6QDuXq9HlNTUxwcHFi+fDnW1tZERESgVqs5cuQIDg4OynITJkzgyZMnSshHCPFpHThwgCtXrtCiRQsyZMiQ4rmHDx+yf/9+atWqRdGiRdFoNKxduxZvb2/UajWxsbFKEO/o0aPY2dmRP39+rK2tmTZtGoBU0PqXDH11mTJlsLS0pHLlykyaNIkhQ4bw6tUr2rdvT6ZMmVCpVEqAp379+qhUKrp160ZUVBTt27dP47348r1ZucgQcEuXLh1Xr15ly5Yt1K9fX/k8otfr0Wq1VK1aldy5c7N//35evHiBlZVViu3I5xUhhBCfE7lDE0IIIYQQQgghvmE6nU750nrUqFFERUXx4MEDAMzMzJgwYQK9evUiffr0WFlZ0alTJwAGDRpEYGAgKpUqxRflQgjxtfm7ftLKygofHx9GjBgBvA7wdOjQAZVKRUBAAD179iRfvnxcvHgRgIMHD6ZqKObNqQwNVCoVzs7O9OnTh7FjxzJy5EgyZ87MuXPnsLa2VtZdtmwZP//8MzVr1qRy5cqp1k4hxLs9evSIPn368Pvvv6NWq2nevDnp06dPsYxarebJkyc8efKEbdu2KcGduLi4FEG8gQMHUqlSJSZOnJgirCPBnX/2rimu+vXrR2JiIunSpcPe3h4fHx/8/PwA3hngqVu3Lj///DNFihRRgu3ivzEO0N65c4ds2bKhVquxtLSka9euxMbGsmrVKsqUKaOE17RaLaampmTMmBETExNy5Mjx1rkkhBBCfG7kmzUhhBBCCCGEEOIbZVxW3sfHh6CgIMqXL8+KFStYt24dFSpUYMiQIYSGhqYYqO7UqRMRERH89ttvuLu7s3v37rTcDSGESDXG/aSvry9BQUF8//33xMTEsGPHDooVK0ZgYCDDhg1Do9EAf1XgmTVrFubm5pw7d478+fOzf/9+8ubNqyz3sWm1WqWtR48eZf369Rw/fpxbt24py/Tu3ZvWrVsTHx/P999/z8OHD4HXwZ3IyEgCAgIAmD59OhkzZvzbqXuEEB9fhgwZGDduHAULFsTLy4tVq1bx4sUL5Xk7OzsqVarE8ePHiYqKwsfH563gDkBISAgXLlygdOnSEtb5l7RarRLcefz4MZcvX+by5cvA6yovANWqVWPChAnkzp0bPz8/li5dqkyhZQjwWFhYULduXZlK9n9kHNyZPn06jRs3pk+fPsrzlSpVolatWsyZM4eoqChu3rwJoExZuWjRIm7cuEHJkiXRarXo9fo02Q8hhBDiQ6j0cqUSQgghhBBCCCG+aXPmzMHHx4eOHTvi6elJ/vz50Wg0FCpUiCtXrgDg7e2Nl5eX8mvWxMREfvzxRyIjI9m/fz/ZsmVLy10QQohUNWfOHHx9fWnfvj2DBg0iX758ABQoUIA//viD5ORkBgwYQFhYWIqB8sTERO7evYuDgwPW1tYpBiE/JuMqEX5+fkyePJmkpCRMTU35/vvvGTVqFDVq1ECv13PkyBHCwsJYtWoVGTNmpESJEty5c4fr16+TJ08eNmzYgIuLS6q1VQjx95KSkti+fTvDhg3j4cOHTJw4kZYtWypTaC1dupT+/fvz/PlzsmTJwtmzZ7G1tVXWX7p0KQEBAeTKlYtVq1aROXPmtNqVL45x9bKJEycSExPDqVOnMDMzo2TJkvj7+1OpUiVsbW3RaDTs2bMHHx8frl69yvjx4+nQoQMZM2ZM4734ehi/HsOGDWPGjBmUKlWKjh074ubmpiy3efNmAgICOHnyJC1atKBx48bUrl2bpUuXMm/ePF6+fMnBgwdxdHRMq10RQgghPoiEd4QQQgghhBBCiG/YrVu36NChA+bm5oSHh1O0aFGePn1KhQoVePLkCf369WPnzp3ExsYycuRI+vfvT5YsWQB4+fIlWq0Wa2vr907VIoQQX7o7d+7Qpk0bLC0t39lPuru7s2HDBuLi4hg2bBhjx47FzMzsrfDLu6Zh+djGjRvHiBEjqF+/PuXKleP3339n4cKFZMmShTlz5tC4cWMAnj17xvz581mzZg3Xr18nf/78VKtWjZ49e5IlSxYJ7giRRjQaDaampmi1Wnbv3s3QoUN5+PAhwcHBNG/enEyZMvHixQu8vb1ZtGgRWbNmZdGiRdjZ2ZEjRw4iIiKYPXs2Op2O2NhYcuXKJfdoH8i4jx42bBjh4eGULVuWRo0a8eeff7Jjxw5evnxJ165dGTRoEA4ODsrr5OPjw82bN/Hz86Nnz55K0Ep8HGFhYfj4+ODp6Ymnpyd58+YFUr5m27dvZ86cOSxfvjzFukWLFmXt2rUSShVCCPFFkPCOEEIIIYQQQgjxDXlzAOfRo0eUKFGC0aNH0717dxISEqhRowbXrl1j0qRJdOrUiY0bN9KkSRMAhg8fzoABA1JMzSCEEF+z58+fU6VKFdzd3endu7fST169epWwsDC6dOnC/v37qVq1KgCDBg0iNDT0kwwQvjkQWatWLRwcHAgNDSVXrlwATJ06laFDh5IpUybmz5+v9OeG9TUaDRYWFspjMtAvRNowPp/37dvHsWPHiI2NZe3atRQsWBBfX1+aNWuGjY0NT58+JSgoiOjoaB49ekT69OmV87lYsWKsWLECZ2dnCSv8BwsXLqRPnz5069aNQYMGUbBgQeLj45kyZQoBAQFUrVqVzZs3K1NoabVa9u7dS48ePUifPj1HjhzB2to6jffi63HlyhWaNWtGhgwZWLJkCblz507xvHGA58mTJ5w4cYLY2Fh0Oh1FixalevXq2Nvby7kghBDiiyDhHSGEEEIIIYQQ4ht0+PBhihUrhpWVFU+ePMHGxgadToe3tzczZ85k9OjR9OvXDysrKzQaDZUqVeLZs2f89ttvjBkzBn9//1SvICGEEJ/a+wb3jPtJPz8/oqKiGDVqFP379yddunQkJSVRuXJlEhMT+eWXXxg+fDhjxoz5qG37u8o9EyZMIGPGjEyZMoWpU6dSt25dpYIHQFRUFAMGDMDGxobo6GgaNWoEvK7yYWJigkqlktCOEGnozaov0dHRZMqUiXLlyhEXF8eVK1fInj07wcHBSoAnMTGRkydPsn79eq5fv0769OmpXr06devWxc7OTsIK/5LhNejYsSP79u1jy5YtFC5cmOTkZNasWYOXlxdmZmYcPnz4rTCIRqPh8OHD5MuXj2zZsn2SSmvfitjYWKpVq8a4cePw9fV95zL/dP2S65sQQogvhek/LyKEEEIIIYQQQoivSWhoKOPHj+f69esAZMqUSXlu//795M2bFzc3N6ysrABITk7m2rVrtG3blqpVq9K5c2cZkBBCfJUMA7ELFy7k+++/J1++fMBf/aRGo2Hfvn3kzp2bPn36KJUXTE1NuX37Nk2bNqVMmTL06tXro7bLeCD4+fPnypQsOp2OI0eO4OfnR548edBqtUplNONATv/+/QEYMGAAXbt25aeffqJhw4ZKuAeQgU0h0pDh/J42bRqTJk1i4MCBeHh4kDdvXh49ekR0dDRRUVH4+PgAKAGeihUrUrFixbfCIjqdToI7/5Jer+fFixfs2rWLsmXLUrhwYV6+fMnatWvx9vZGrVYrwR2A06dP4+TkhL29PaamplSuXBl4fwhU/DcPHjwAUD6XGAdT4a9gzuPHj8mcOTPwdthVrm9CCCG+FHLFEkIIIYQQQgghviHJyckkJiby5MkTIiIigL8GjB49esTFixfJmjUr6dOnV9aJjo7GysoKd3d3ZsyYgZOTE1qtNi2aL4QQqW7Dhg107doVLy8vrl27BvzVT7548YLr169jZ2eXIkAzc+ZM9Ho9Q4YMYd68eTg7O6PRaD5amwx/v1GjRkyaNIlHjx4Brwckv//+eyIiIvj999/5448/iIuLA0hRUQegf//+TJ06lfj4eBo3bszOnTs/WvuEEP87vV7Pli1byJo1K56enuTNmxeNRoOtrS19+/ZlwoQJWFhYMHLkSNauXcuzZ8+Udd8MVUtY4d9Tq9VkyJCBbNmy8ejRI5KSktiyZYsS3ImLi1OCOwAtW7Zk2LBhb21HgjsflyGQs2zZMh49eoSpqSmGCUX0ej1qtRq9Xk+tWrWUyjzyIwMhhBBfKrmDE0IIIYQQQgghviFmZmZ07dqVLFmysHr1av7880/g9eCzjY0NderUYceOHcpAcFRUFBEREdjZ2ZEtWzZlMEgGJoQQXytXV1c8PT3ZvHkzQ4YM4erVq8pzlpaWlCtXjr179zJq1Cju3btHVFQUkZGRODo6YmtrqwwaGlcG+Bju3btHfHw8oaGhzJ8/XwnwwOuKOjNnzgTA29ubDRs2AK8HMN8M8IwZM4ZcuXJRsGDBj9o+IcR/p9PpeP78OSdPniRz5szkypULrVar9CPp0qWjfv36dOrUiRs3bhASEvJWgEf8b/R6PVqtlrx583Lw4EEGDBjAgAEDMDEx4fDhw0pVM71eT0hICPHx8VSoUEEJkojUUb16dWrWrMnBgweZM2cOz549Q6VS8erVK+X69tNPP3Hv3j0sLS3lBwZCCCG+aCq93FkIIYQQQgghhBDfDENp+YkTJ+Lr68uCBQvo0qWL8vy2bdsYPHgwv/76q/JYvnz52L59O87Ozsr6QgjxNfvjjz+IiIhg6tSpNGvWjEmTJpE7d24ADhw4QNeuXfn999+V5fPmzcuOHTtSvZ+8fv06Q4YMYdu2bfj6+uLm5oadnZ3y/IwZM3B3dydPnjxMmzaN+vXrA68Hmw0VCuB1BaH06dPL9C5CfGaaNGnCoUOHOHHihFLp0MTERJkG6I8//qB06dK8evWKhIQEli5dSps2bdK62V8FwzE+f/48NWrU4P79+2TJkoUTJ06QPXt2Zblly5YxfPhwcuTIwcqVK1P0weLjMlxPDxw4QLdu3Xj27Bm9e/dm8ODBynFfvHgxY8eOxczMjO3bt5M1a9Y0brUQQgjx30l4RwghhBBCCCGE+Aq9a0DWeED54MGD1K5dmzx58rBx40acnZ2V5Y4cOUJcXBynTp3iu+++o3PnzmTLlk0GeYUQX5V39WmGwVv4K8ATGRlJ06ZNCQsLI3fu3KhUKk6fPs2uXbu4cuUKuXPnpmPHjp+sn7xx4wZubm5s2bKFixcvki9fvhT9+/Tp0/H09PzHAI/xvgoh0pZWq0WtVjNy5EiCg4Np2bIlixYtUiqJqFQq1Go1T58+pUiRInTo0IHTp08zd+5ccubMmdbN/2ro9Xo0Gg1z584lMDCQdOnS4ePjQ926dTE3N2fOnDnMnz8fgP379+Pk5CTB9k/g5cuXbNiwgZEjR3LhwgXy5MlD2bJluXHjBidPnsTBwYE9e/bg4uIir4cQQogvmoR3hBBCCCGEEEKIr9iGDRvInz+/Mj2K8RfaQ4YMYerUqaxZs4bGjRuTlJSEubn5O7cjwR0hxNdq8eLFNGvWjPTp0wNvB3gmT57M1KlTad26NePGjSNv3rzvDL18yn7yjz/+4M6dO5QvX1557H0BnunTp1OvXr1P0i4hxN/7p2DB06dPqVSpEufPn8fT05MJEyZgaWmpPL9gwQLGjRvHihUrKFSoEGZmZnKP9oHePPZ/F2B88OABa9aswd/fnwcPHmBjY0NycjJ6vZ5ixYrx888/4+zsLMf+f2B87D7kOCYlJfHHH38wcuRIDh8+zPXr1ylSpAjly5dn1KhRZM+eXV4PIYQQXzwJ7wghhBBCCCGEEF+pqKgoPDw8cHJyok6dOowcORJbW1usra0BOHbsGPXq1cPV1ZU9e/akmJZBCCG+BcuWLaN9+/a0bNmS6OhopX807gsvXbrEoEGD2Lx5M507dyYwMJA8efKkZbNTMG7ruwI8GTNmZPXq1dSoUSMtmynEN884WLBp0ybOnDnD1atXyZw5M926dcPBwQE7Ozt+/fVXGjduzNWrV6lVqxa+vr5kzZqVXbt2MWPGDNKnT8+uXbvIkCFDGu/Rl8O4nzx//jyurq4ftN6NGzeYPXs2d+/exdzcnKpVq1KrVi1sbW0lKPKRhISE4OrqStOmTT+4as6TJ094+PChMlWlubm5vB5CCCG+ChLeEUIIIYQQQgghvlLHjh3j9OnTRERE8Msvv+Do6EiDBg3o0aMHFStWRKPR0KlTJ5YtW8bSpUtp27athHeEEF+1N/u4S5cuERQUxOLFi2ndujXz589XAjzG1qxZQ8uWLQGoVq0aP/30E7ly5fpk7f43jAc/Q0NDCQ4O5vz582TPnj2NWybEt8v4vPTx8WHatGkkJiYqzzs6OtKpUyf69OlD3rx5+f333/nhhx+Ii4tDrVajVqvRaDTkyZOHHTt24OLiIvds/0FAQAD79u1j3759/7js3wVJZGqmj+PkyZOULl2avn37EhUV9Y/LGx934/e/nAtCCCG+FhLeEUIIIYQQQgghvgJ/96X1kydPWL58OStWrGD79u0AdO/enU6dOpEjRw4qVqxIo0aNiI6O/pRNFkKIT+p9g61Xr14lMDCQRYsWvRXgSU5OxszMjPPnz9O4cWPKli3L4cOHOX78OPb29p96Fz6Y8b6+ePGC9OnTS1UCIT4DY8aMITAwEDc3Nzp27EjOnDlZunQpy5Yt49SpU3Tv3p2AgABy585NfHw8q1at4tSpUzx9+pQCBQrQtWtXsmbNKufzfxAfH0/Pnj1ZtmwZu3fvplq1ah+0noREUs+tW7eUKlNbt26lXLlyad0kIYQQIk1JeEcIIYQQQgghhPjCGQ/g/Pnnnzx8+JBXr15RokQJTExMUKvVymBDdHQ069atY/Xq1QBUqlSJW7ducfXqVdatW0fjxo3TcleEECLVeXp6otVqU/zK/+rVq4waNYqFCxfSqlUr5syZQ6ZMmZTng4KC2Lx5M4sXL8bOzo5MmTJ99pUXDO0zfP0rA85CpK0zZ87QrFkz8uXLx9y5c3FycgJAo9Fw7tw5fHx82LVrF0FBQQwePBhLS8t3bkeCO/+dYarE3r17ExkZiamp6Wfdj38LFixYQI8ePZg8eTIDBw787K+tQgghRGqSK6AQQgghhBBCCPEF0+l0ygDOuHHjqFu3LiVKlKB8+fKULFmSWbNmcefOHWXQtmvXrixdupQ9e/bQrFkzrl+/ztWrV3F2dqZMmTJpuStCCJEqdDqd8v9Pnjxh06ZNzJw5k4CAAOXx3LlzM2rUKDp37szKlSvp3LkzsbGxPHnyhOjoaJYsWUKuXLlwdnYmU6ZM6PX6z35w0dA+lUolwR0hPgP37t3jxo0b1K9fXwnu6HQ6TE1NKVGiBCNHjiR37tzMmTOHpKQk5XkDQxBPgjv/nuHYtW3blnr16rFhwwYePXqUIuAoUs+bx9j433Xq1KFQoUKEh4dz+/btz/7aKoQQQqQmuQoKIYQQQgghhBBfMMMX3N7e3owYMQJ7e3vCwsIIDAzEwsKC4cOH4+Xlxe3bt5V1TE1NqVq1KvPmzWPTpk24ubmxe/dusmXLhlarTatdEUKIj06r1Sr9ZExMDKtWrSJjxoxkzJiRcePGERgYqCxrCPD06dOHnTt3Ur9+fYoWLUr37t1JSEggLCxMGTSXMIwQ4t+6f/8+Op2OxMRE4PW0fMZBhTJlylCzZk2uXr3Kjh07AFI8L/3OuxmCIIb/vu9e1vB8/fr1uX37NqGhoWi1WjmuqUir1aLT6ZRjnJCQALx+LxuCaTly5KB+/frcuHFDmd7XOLQmhBBCfEtk2iwhhBBCCCGEEOILFxMTQ/fu3enSpQtDhgyhQIECJCUlMXfuXNzd3Slfvjw7duzA2tr6b7ej0WgwNTX9RK0WQohPx8vLi9mzZ5M/f34KFSrEq1evWLFiBQC+vr6MGzdOWfbOnTvs3buXWbNmkZSURO7cuRk/fjw5c+b8aNPVvDktiEwTIsTX79SpU9SoUQNnZ2f2799P+vTplXPfcA+2efNmGjVqxOLFi+nQoUNaN/mL8ODBA+zt7YGUU4odO3aMbNmykTNnzhTLx8fHU6pUKTJmzMi+fftIly6dMr2s+N/t3r2b48eP4+XlBaAcW39/fy5cuECzZs3o2rVrinX+/PNPypYti6urqxJcE0IIIb5F8olQCCGEEEIIIYT4ghj/mtjwe5zdu3eTPn16PDw8KFCgAMnJyaxevZqJEyfi4uLC+vXrsba2Jjk5+W9/ySrBHSHE12j27NmEh4fTs2dPVq5cycKFC1m2bBlbt24lX758hISEpJhCK1u2bLRr145du3axf/9+5s+f/1GDO/BXNY3ly5eTlJSEWq2WSgNCfAX+7jwuXLgwVatW5cyZMwwePJiEhATUajWvXr3C1NQUvV7Pzp07MTU1JW/evJ+w1V+uvXv3kj9/fpYvXw78NaXYhAkTKFeuHI0aNWLq1KkpKlBaW1vTtWtXjh8/TnR0NCBVjT4GvV7PkydP6NixI97e3oSHhwOvj+0vv/zCsWPHWLNmDd27d6dq1aqEhYVx584dALJkyULNmjXZtWuXEqwVQgghvkUS3hFCCCGEEEIIIT5zu3fvJigoCHg9KGEI8Oj1ehITE9m7dy958+alSJEiJCYmsmrVKry9vQE4cuSI8mvk3377jbNnz6bNTgghRBo5ePAgVlZWdOnSBWdnZ/R6PTqdjjp16hAdHU22bNnemkIrKSlJ+X9DsPFjBXcMunfvTrt27YiMjFSmz5EAjxBfLuNp+g4cOMCOHTs4fvy4ct9mZmbG7NmzKViwIHPnzqVXr148evQICwsLAFauXMmaNWsoW7YsBQsWTLP9+JJcvHiRp0+f4u7uzpo1a5THXV1d8fHx4e7duwwaNIhq1arRu3dvfv/9d5KTk2nXrh3m5uYsW7aMBw8eIBNU/O9UKhU2NjYsWLCAnDlz4uXlRVhYGPA6uLZt2zaOHz/OwIEDuXv3Lt7e3pQuXZqRI0fy66+/EhAQgEqlIjY2No33RAghhEg7Mm2WEEIIIYQQQgjxmdLr9bx8+RIXFxfu37/PmDFjGD58OJByipV69epx8+ZNTp06xebNm/H09EStVhMXF4eDg4OyPVdXV4oWLcrixYulyo4Q4pug0WioVasWly9f5vr165iYmKDT6VCpVEqlhXXr1tG8eXMAhg0bxoQJEwA+aqWdd3nw4AFVqlThxo0bBAUF4enpiZmZ2QetK1O8CPF5MT4n/fz8CA8PJzk5GQsLC+rUqcPPP/+sTF964cIFWrduzfnz58mZMyfFixcnISGBEydOkD59evbv34+zs7NMp/eB5s6dS//+/bGysmLu3Lm0bNlSee7s2bMcP36c8PBwzp07h42NDTVq1MDb25vo6Giio6PZuXMnFSpUSMM9+DoYnwO7d++mY8eO3Llzh/Hjx+Pj46Msp9FoePXqFZMnT2bPnj3s2rULlUpFmzZt2L59Oy9evCA2NpZy5cql1a4IIYQQaUbu/IQQQgghhBBCiM+USqUiXbp07N27F0dHR0aMGKFU4FGr1SQnJ6PVasmfPz+//vornTt3xt3dHRMTEw4dOqQEd/R6PaGhoTx8+JDvv/9eBoKEEN8MlUpF1qxZuX37NkuXLgVe958qlQq9Xo9Go6FKlSoUK1YMJycnQkND8fX1BVJWOvvYkpOTsbe358CBAzg5OeHl5cWkSZPeW/3B8Pjjx4+V/RJCfD4M5+TkyZMJDw+nZs2ahIWFUb58eTZs2MD333/PxYsXAfjuu+/Yv38/vXv3xsHBgY0bN3Lnzh0aNWrEoUOHcHZ2TlHFR7yboVJZz549mTZtGgkJCfTs2ZPVq1cryxQtWpRu3bpx8OBB1qxZQ/369Vm/fj3ff/89ixYtIjExkeDgYJ4/f55Wu/HVUKlUyjWzRo0a/PTTT2TLlg0/Pz+lAg+8ft2sra0JCAhgx44drFy5krZt27Jnzx4eP35McnIymzZtUpYVQgghviVSeUcIIYQQQgghhPiMaTQaTE1NuXz5MpUqVeL+/fsEBgammN7l9u3blC1bllu3bmFvb8/p06dxdHQEXg/4rlixAn9/f7Jly8bq1auVabSEEOJr8XeVaDZs2ECzZs2oV68eYWFhuLq6Aq8DNIZKN4UKFaJWrVocOXKE48eP4+3tTUhICPC/V+AxbptOp0Ov16fY3v379ylTpgyRkZE0bdr0vdvZvHkzQ4YMITw8nAYNGvzn9gghPj69Xk9SUhLt2rXDwsKCiRMn4uzszLNnz5gwYQKTJk0ib968rF69mgIFCgCv7/E0Gg1XrlzBxcUFExMTLC0tU73q19fEuDrR7Nmz8fDwwMrKivnz5ysV1Yz7enhdFebIkSOEh4eTkJCAra0te/fuJXfu3FLt6F9481gZv2+TkpIwNzdnx44ddOnShTt37jBx4kS8vLyAt1+T+Ph47t69y5QpU9i0aRM6nY4TJ06QKVOmT7tTQgghRBqTuxAhhBBCCCGEEOIzZmpqilarJV++fBw4cAAHBwdGjx7N6NGjlWUcHR2ZOXMm2bJlIyEhgaioKM6cOcOFCxfw8/NjyJAhvHz5ksWLF2Nvby+/YhVCfFW0Wm2K4M7Tp09JSkpS/l25cmW6devG1q1biYiI4MSJEwCYmZmh1+v56aefSEhIYOjQocyaNYvSpUszceJE/Pz8AP6nQXTDFF0GarVa2d7ChQs5d+4cDg4OXL58+W+DOy9fvmTZsmX89ttvzJo1i6dPn763So8Q4tMwvp8ynOf379+ndevWODs7k5ycTMaMGfH398fPz48rV67QokULpQKPqakplpaWFCpUCGtraywtLd8K94m/p1arldehd+/eSgWe7t27s2bNGuB1X6/T6ZTlatSoga+vL0ePHmX48OHcvHmTKVOmKNsTH8ZwrDZu3Aj8da309vbGw8OD5ORkateuzeLFi8mWLRve3t5KBR7Da2KQLl068uTJw8SJE+nQoQNXr15l8eLFn3iPhBBCiLQndyJCCCGEEEIIIcRnzjB1y98FeGrVqsW8efOwt7cnODiY8uXLU6xYMaZPn06ePHmUqVlkGgYhxNfE+Jf+M2fOpHnz5ri6ulKxYkV8fHy4f/8+NjY2DBgwgEaNGjF37lz69u3LpEmTOHfuHBMmTGD8+PFkzpyZDBkyUKpUKaKioihTpgwTJkxg2bJl/1P7DP1t+fLl6dSpk/J4v3796NevH+fOnSM5OVnZh/cFciwtLRk9ejRt2rRh165d3Lt3T6bOEiINGd9Pbd68mYULFzJ//nwePXqElZUV8FdAwdraGi8vr3cGeCBlYETO63/2ZghdrVYr0zW9L8BjmC7RQK/X4+zsTOfOncmfPz/btm1TpiUUH87NzY0mTZowf/58AAYMGEBYWBhZs2blxYsXwOuw1KJFi94K8BgHrwz/b2Fhgbu7O2ZmZhw9ejRtdkoIIYRIQzJtlhBCCCGEEEII8ZkzlKU3lJg3nkJr5MiRjBo1Sln28ePHxMTEcP36ddRqNRUrVqRixYrY2NjINAxCiK+K8XRUQ4cOJTIykly5clGiRAmuXLnCmTNnqFmzJt7e3tStW5fTp0+zaNEiwsPDU4Rk8ufPz9atW3FxcVH6yQMHDrB161aCgoL+53b+/vvv1K9fn8uXL+Pt7Y1eryc0NJQBAwbg4+OjTHP4ITZt2kTv3r3p2LEj48ePlz5diDTm4+NDaGgo8FfY2svLi4CAADJmzAj8dR8XHx9PWFgYYWFhWFtbc/DgQfLkyZOWzf/iGN/L3r59m/v372NtbU26dOnInj27styPP/6Ip6fnW1NoGV83DNvy8/NjwoQJnD59mqJFi37yffqSxcTEMHHiRE6ePEmNGjXYvXs3Xl5eDBw4kBw5cqQ43rt27aJTp05vTaH15ueTW7duUbZsWcqUKcPSpUtJly5dmuybEEIIkRYkvCOEEEIIIYQQQnxmDIM8Bq9evcLCwiLFMpcuXaJy5crvDPB8yDaFEOJrMW3aNAYPHkzfvn3p168frq6uXLt2jcDAQBYuXEjHjh356aeflAHE48ePc+jQIR4/fky+fPmoXbs2Dg4OSiWNNytffIz+88KFC/Tv3589e/YAMGLECDw9PbG3t/9X29FqtTRo0IDs2bOzYMGC/6lNQoh/zziMMHv2bAYMGECTJk1o27Yt27ZtY+vWrTx//pzp06fTunVrzMzMgL/6kYSEBAIDA1m5ciUHDhz4V+G9b51xXzx+/Hiio6OVCkY5c+Zk9OjR/PDDD8o98+zZs/Hw8PjbAM/Tp09xc3Njy5Yt7N27l+LFi3/6HfsCGR/D/fv306JFCx4+fEidOnVYuHAhWbJkeeeyxgGesLAwhgwZkmK78fHxLFiwAE9PTwIDAwkMDPx0OyWEEEJ8BiS8I4QQQgghhBBCfEaMf30aHR3Nrl27OHz4MKVKlaJMmTIMHTpUWdY4wGP8BbehQg+k/MJcCCG+dMaDt3q9noSEBOrXr8+LFy9YunQpBQsWJDk5mQ0bNjBgwADMzc05cuTIP4ZkUjPgaOiHGzZsyJYtWwDo1q0b8+bNA0Cj0WBqavqP2zGuwqZWq6XqjhCfmHE/8eLFC/z8/Lh27RqRkZG4uLig0WiIiYlhzJgxPHjwgBkzZtC8efO3AjyJiYkkJyeTMWNGqYr4HximXqpVqxZNmzbl+fPnLF68mF9//ZVBgwbh7++v9PmGAE+mTJmIjIykXbt2ynYMr1fv3r1p3bo1P/30U1rt0hfH+H0bHh7OsGHDsLW15eHDh8ybN49u3bqlWP7NAE+3bt24efMmUVFR9O3bV1nu4cOHdOjQARsbG2XaSvksI4QQ4lsi4R0hhBBCCCGEEOIz8eYUMNOnT8fOzo48efJw9epVbt26RbNmzZg9e7YyKGE8hVZQUBABAQFpuQtCCPHRHT16lLt379K4cWMgZV957do18uTJg7+/P2PHjuXVq1esXr0aHx8f1Go1R48exd7eHq1Wy8WLFylUqNBb20gtb/6NLl268OzZM27dusWxY8dwd3cnMjISeHvakPcxDg/IoL8Qqcf4XHsz3Dd8+HCeP3/O/v376dWrF/3791eC0zqdjhUrVjBy5Mi/DfCAhBL+i6VLl9KrVy86d+7MsGHDlGnHxo0bR0BAAHny5OHs2bMpplqaO3cuvXv35rvvvuP48eNYWloqx33r1q1s3ryZiIgIQF6TD2F8jKZPn87169fJnj07mTNnZvLkyZw+fZoZM2bg5uamrPPmObRx40ZGjBjBmjVrcHJySrH969evK49J5VAhhBDfmn/+SYcQQgghhBBCCCE+CcMX4ZMmTWLKlCnKFDCFCxfmypUr9O7dm7Vr1+Lg4MCsWbPQarXky5ePAwcOUK1aNUaOHIm1tTWDBw9O4z0RQoiP4/bt25QvXx4XFxfUajUNGzZEpVIpg4eWlpaYmJjw8uVLANatW6cEd+Li4pSgo06no1q1agQGBuLu7p7qg7PGwZqLFy+SNWtWparD5cuX6d69O9OnTwcgMjISExMTkpKSMDc3ByAhIQErK6u3tms8iCnBHSFSh16vV861Bw8eYG9vr1TIun37NrGxsezfvx+VSkVycjIAZmZmynnfunVrVCoVI0eOpG/fvqhUKpo2bYq5uXmKc1hCIv/evn37sLS0pFevXuTJk4ekpCTWrl3Ljz/+SL58+Th48CDp0qVLUdGsZ8+epEuXjipVqqQI9QDUq1ePevXqARIU+VCG960hMBUYGEjHjh2xt7fH1taW4cOH069fP1QqFX369ElxXM+ePUv+/Plp1KgRtWvXxsLC4q0gqiG4Y3weCiGEEN8KufIJIYQQQgghhBCfkYcPH7Jo0SLKlSuHp6cnhQsX5tWrV1y4cIHffvuN/PnzExwcjEqlUgYl8uXLx86dOylWrBitWrVK4z0QQoiPx9HRkSlTpnDz5k38/PzYsGED8HrwUKvVYmZmRrZs2di2bRsTJ05k6NChqNVqjhw5goODA/B6AHD06NEkJyeTK1euVG+z8UDkpEmTaN68OR07duT69evodDry5cvHtGnTqFSpEtOnT8fT0xNACe5s3bqVoKAgLl++nOptFUK8zRBOaN68OY6Ojvz555+Ympqi1+txdHQkIiKCTp06odfr2bx5s3KumpiYoNVqUavVtGrViqCgILJnz07btm3Ztm1bWu7SF8l40gi9Xs/Lly/Zt28f3333HaVLl0aj0bBq1Sq8vLxQqVQcOHBACWweOnSI2NhYZf0ffviBXLlyodVq3/v3JCjy94yPXXx8PCtWrKBjx4506dJFOe6NGzcmODiYokWL0rdvX2bOnKkc182bN9OzZ0+lSqihGtX7gqgSbhNCCPEtkso7QgghhBBCCCFEGnqzPP+ff/7J6dOniYiIoGDBgiQnJytTwJibmysDExqNhosXL+Lq6gqgTAVgGDiSigxCiK+Fp6cn5ubm9OvXD39/f+D1AKGJiQl2dnb4+PgwYMAARo4cSZYsWTh+/Di2trbA6z42JiaGn3/+mUqVKlGtWrVUbater1f6Xy8vL6ZPn061atXo3r17iqlBihcvzvTp03F3d2f69Ono9XpCQkLYtGkTI0eOJDk5maFDh6ZqW4UQ76fX66lQoQKxsbFUrFiRgwcPkiNHDgBKlSrFwIEDSUxMZNWqVcyZM4cBAwaQPXv2FPdhrVq14uXLl8ydO5cSJUqk7Q595t6sevPmvaxKpUKtVpMhQwbu3bvHlStXOHXqVIpKa4bAJoC3tze2traULVsWS0tL5XG5P/7vDMdu6tSpVKpUCZ1OR8+ePcmdOzfw12tomOIyICCA/v378+eff6JWq4mJieHhw4csX74ckLCUEEII8S5ydRRCCCGEEEIIIdKIVqtVgjsPHz5UHgPQaDRotVpWrVr1zilgkpOT6d27NzExMcr2DF+Cy8CEEOJrYegT3dzcWLJkCefOnSMoKIg1a9Yoy9SrV4/OnTuj0WgoXbo0t27dIjk5mZcvXxIREYG/vz96vZ6ZM2eSKVMmdDpdqrXX0KdPmzaNqVOn4ubmxowZM95ZFa1YsWJMnz6d6tWrExUVRe7cuenevTsJCQls374dBweHVG2rEOLdDMFqHx8fRo8eTUJCAkWLFuXmzZvKMqVLl8bf358mTZowadIkoqKiuHXrFpCyAk+nTp3YvHkzOXPm/NuqL98y4+mRjh07Bvx1L+vv709QUBDwujpZ7dq1uXr1KiEhIUqltTeDO5MnT+bSpUvUr18fCwuLT7w3X7f169czaNAgatasyb1795TPJfD6c4ihWlLjxo0ZP348NWvWJDg4mLFjx2JqasrRo0dxdnaWc0EIIYR4D6m8I4QQQgghhBBCpIE3qzO8fPkSb29vcuTIgbW1Nfv378fBwYGAgIB3DkwEBARw/vx5HB0dlcekvLwQ4mtiXHnh8OHD2NraUqNGDfbs2UNoaChqtZqmTZuSP39++vbti16vZ9GiRWzfvh1XV1ceP37M7du3yZMnD+vXr1cGz1Mz4KjX67l//z5Lly4lT548uLu7K1UJ3qVYsWLMmDGDn376ibNnz2JnZ8fYsWPJkSMHGo1GmR5RCPHpqFQqpYqIu7s7SUlJ+Pv7c+7cOXLmzKksV7JkSUaNGoVer2fChAkA9O/fX6nAY9hGunTpAAlXv4/h/rVDhw7ExMSwY8cOatasiaenJ9OnT2fUqFE8ffqUTJky0apVKzZs2MDcuXOxtbXl2LFjKe6Ply5dSlRUFAULFqRDhw5yb/yR1a5dm759+xITE8OjR484f/48RYoUUa6tKpVKCb81aNCAggULcv78eZ4+fUq9evWwt7eXCqFCCCHE31DpjScOFUIIIYQQQgghxCcVGRnJwIED6d27NyNGjCBnzpx4eHgQFRVFhgwZsLW1fSu4s2jRIkaOHEmJEiWIjo4mQ4YMabgHQgjx8RlPKejt7U1MTAzx8fGUKFGCw4cPk5CQQIkSJRg1ahRNmzYF4Pbt2+zbt4+ZM2fy+PFjcuTIQc2aNenSpQsODg6fbMDwwoULlC1bli5dujB9+vS/3UdDxYmkpCTMzc159eoVFhYWMrgpxGfAEL7R6/VcvXqVPHnyKM8Z91GnTp0iMDCQzZs34+fnR+/evVOEfMSHmTJlCuHh4Tx79oxq1aqxbt06hg4dyqBBg5Qpy/R6PTNmzGDKlCncvXuX4OBgChcuTL58+YiKimLhwoWoVCr279+Pk5PTW9Nxif/OECh9+fIlQ4cOZcaMGeTJk4e9e/e+FTh9c1pgA3k9hBBCiL8n4R0hhBBCCCGEEOITenNAtnXr1iQmJjJlyhTy5csHwIEDBxg4cCAnT56kf//+TJ48GbVajVqtZubMmYSFhaFSqdi9ezc5c+Z87xfkQgjxpQsLC8Pb25thw4bRuXNnihQpwuHDh1m3bh0hISEUK1aMoKAgJcADb/ez8GkHDI8cOUKlSpXo1KkTCxYsIDk5GTMzs7fa8uTJE65du0bx4sUBqZ4mxOfozb7D+J7rzQDPmDFjWL16NSEhIXh5eUlI4QMZH8eVK1fSvXt34uPjadmyJTNnzsTOzg74q2/XarUsWLCAWbNmKdNsAVhaWlK2bFkWLlyIk5OThCD/B++7ZhpeK0PF0GnTpvHdd99x8OBBbGxspGKcEEII8T+S8I4QQgghhBBCCJFK/i5U4+vri5mZGdu2bWPw4MG0b98+xRflGzduJDAwkBMnTpAnTx7y5s3L7du3+f3338mVKxebN2/GxcVFBiaEEF8lvV7PgwcPaNy4Mffu3WPXrl0ppp/SaDRERUUxaNAgSpYsyciRI2nWrBlAirDMpwo3Gv+dmzdvUqNGDRITEzly5Ag5cuRIUcHDsFyDBg1wcHBg1qxZyrQ6Qogvi/E5ffToUWbMmMGoUaNwcnJK45Z9WQx95JgxYwgMDCR9+vTo9XrWr19P9erVleNs3JfeuXOHzZs3c/36dQCqVKlC6dKlsbGxkfvj/4HxsTtz5gwPHjzg+fPnlC9fnqxZsyrvdwnwCCGEEB+fXEGFEEIIIYQQQohU8OZArmH6BJ1Ox8WLF5k4cSLp06dHpVLx6tUr5TmVSoVKpaJRo0Y4Ojqyc+dO5s2bx8WLF8mZMydeXl7069ePrFmzysCEEOKrpVKp0Gq13Lx5kyJFiijBHcPArampKd27d+e3335LUZGsadOmKarcpFZw582qBMZ/J2fOnFSvXp25c+fi6enJrFmzcHBwUKbG0ul0LFu2jF9++YUWLVpIPy7EF8z43C9btiwlSpTAzMxM7tH+JUN/2rBhQywtLVGpVERERNCsWTNiYmKoX7++spwhHOLo6EiPHj3e2pZOp5Nj/x8ZH7uxY8cye/Zsbty4AUCRIkXo0aMHnp6emJiYYGlpycSJEwGYNm0aFStWVAI88v4XQggh/hupvCOEEEIIIYQQQqSiWrVqkSlTJkJCQihQoIDy+Jo1a+jXrx93797F3d2dyMhIIGWAxyAxMZGkpCQyZcqkPCZfigshvnZ3796lbNmymJmZsW/fPrJnz/5WGGfZsmW0b98ec3NzsmbNyrx586hVq1aqtsu4/z1x4gQ3b97k7t27VKxYkezZs5M5c2ZevXpFtWrViIuLo1atWkRFReHk5ISFhQVz5sxh0qRJ6PV6du/ejaOjY6q2VwghPkfvm4LMYO7cuYwcOZIXL14oAR7j4OSlS5fInz//J2/318r4NfDx8SE0NJSaNWvSrFkzcubMiaenJ69evaJr165MmDBBuQ4aV+BxcHDg4sWLKT6zCCGEEOLDyaSrQgghhBBCCCFEKsqfPz9r1qxh4sSJXLx4UXm8efPmzJkzh0yZMjF9+nSmTJkCoEwFYKDX60mXLt1bX4JLcEcI8TXT6/VkzZqVFi1acPXqVTZs2IBKpVL6R41GA0DdunUpUaIEXbt2RaPR4OrqmqrtMq5KMHLkSBo1akTz5s1xc3OjVKlSeHh4EBsbi4WFBatXr6ZKlSrs3LmTEiVKUKJECfLmzUvfvn1JTk5my5YtODo6otVqU7XNQgjxudFqtUpQRKPR8ODBA+B1H2vQs2dPxowZQ/r06WnXrh2bN29WgjubNm2iR48ehIeHf/rGf6UMr8fMmTOZPXs27u7uTJ8+HU9PT1q0aIGVlRWPHj1i8uTJ+Pr6KtcuS0tLQkNDletwQkJCWu6GEEII8UWTyjtCCCGEEEIIIUQq8/X1JTQ0lPbt2zNixAi+++475blNmzbRqVMnXrx4wZQpU+jXrx/w9pQsQgjxNXpfX2eoAGDoI588ecKqVato3ry5so5Go2HKlClMnTqVkydPYm1tjYWFxSepTDZ8+HDGjx9PixYt6Ny5M9bW1qxevZqZM2fi5OTEokWLqFy5MjqdjuDgYI4dO8aZM2coUKAA5cqVw93dnWzZskkVNSHEN8e434uKimLVqlUcPnyY4sWLU69ePTw8PLC1tVWWnzdvHoGBgTx//pyQkBCeP3/O/PnzefDgAceOHcPJySmtduWrc/PmTTp06ICFhQWRkZEUKlSI58+fU7ZsWeLj4xk6dCjh4eHcvHmTwYMHM2HCBExNTQF49eoVCQkJZM6cWa5tQgghxH8k4R0hhBBCCCGEEOITGDRoEFOnTuXYsWOUKlUqRWn6DRs20LVrV549e8bUqVMlwCOE+CYYD+6dP3+e69evo1aryZs3L3nz5lWWM/zyH2D8+PFUr16d0qVLEx0dzdSpU8mWLRurVq3C2tr6k7R7x44dtGnThkaNGhEYGEj+/PnRarVs3ryZZs2a8d1333Hw4MG3KqY9ePAAe3t7Zb9lcFOIT0Pupz4fxq/F0KFDiYiIIE+ePJQqVYorV65w4sQJ6tatS3R0NFmzZlXW++mnn5g0aRJnz54FoECBAmzevJncuXNLX/oRXbx4kT59+uDp6UmrVq1ISEigWrVq/PHHH0yYMIHu3buzb98+GjZsiLm5OT169CAkJEQJ8MC7p0ATQgghxIeR8I4QQgghhBBCCPGJ/PLLLxQuXFj59/sCPNOmTcPNzS2tmimEEKnOeAA3KCiIGTNmcPfuXQDSp09PREQEbdu2JX369ADMmjWLkJAQ/vjjDwBsbW159OgRTk5O7NmzBxcXl082YDhx4kRGjBjBvn37KF++PBqNhuXLl+Pn54dKpeLo0aPY29vz6tUrkpKSyJAhQ4p9loFNIdKGv78/3bp1o0CBAmndlG9eSEgIQUFB9OjRg/79++Pq6sqzZ8/IlSsXz58/p3LlyixfvjxFgOfMmTMcP36cly9f0qpVK7JkySLBnY9Mp9Nx5swZSpQogUajwdvbm5kzZzJu3Dj69++Pubk5ly5domLFiiQmJpKQkEBQUBABAQFp3XQhhBDiqyDhHSGEEEIIIYQQ4hMzHrh9M8DTq1cv7t27x4IFC+jSpUtaNlMIIVKFcb/n7e1NWFgY9evXV/q88PBwjh07RkhICD179sTOzg6AI0eOcPToUdauXUvmzJnJnTs3gwYNwtHRMdUGcN/cbnJyMh06dGD37t3cunULvV7PmjVr8PHxQa1WExcXh4ODA/C6gkFsbCzt27f/ZFWBhBDvtnbtWlq0aEGDBg2IjIwkT548ad2kb9bhw4fp2rUrFSpUwN/fn4IFC/Lo0SOqVavG3bt3yZMnD3FxcVStWpWYmJgUAR5jUlHpv/uQY/f06VNq166NSqUiLi5OeVyr1VKhQgV69erF4sWL+emnn3BxcUnlFgshhBDfBrmzEUIIIYQQQgghPjHjigsqlQrD72oaN27M9OnTcXV1pXr16mnUOiGESF2GPvDHH39kzpw5eHh4EBERQfv27WnZsiWPHj0CwM/PjxkzZij/Ll++PB4eHmzcuJFly5Yxfvz4VA3u6HQ6ZbszZsxAr9djZmbGd999x7Nnz7hy5Qp79+59Z3AHwM3NjenTp5OUlPTR2yaE+HeqVq3KtGnTiI2NZeDAgVy+fDmtm/TN0Ol0Kf596dIlrl27Rrdu3ShYsCDx8fFUq1aN+/fvM3PmTPbs2UPVqlXZt28fbdq04c6dOwBoNJoU25Hgzn+j1WqVY3fjxg3Onj3Lr7/+SnJycorl7t69y++//46NjY3ymEajYdq0aVy5coWmTZuyb98+XFxc3npthBBCCPHfyN2NEEIIIYQQQgiRxowDPK1ateLYsWM4OTmh1WrTuGVCCPHfvTlga+zmzZssXLiQsmXL0qdPHwoUKMDTp08pXrw48fHxDB8+nNKlSxMYGMicOXOUAA+gBGoMIaDUmjLFMLg5YsQI3N3dGTZsGABFihRBq9XSvn17evXqhampKYcOHUoR3ImMjOTSpUvUr19fmfpLCJE2dDodmTNnpkOHDoSEhLBx40b69u1LQkLCO5c33JNdu3aN27dvf8qmfnWMK7zs3bsXrVZLlSpVWLNmDTVq1CApKYkuXbpw69YtgoKCqFevHpaWlvzwww+YmZlx4sQJatWqxf379zE1NU3jvfnyGYdSg4KCqFKlCsWLF6dw4cKULFmSefPmcfPmTQCcnZ3Jly8f+/fvZ+rUqTx69Ij58+cza9YsChcujJWVlbJdeW2EEEKIj0PCO0IIIYQQQgghxGfAOMBjYWEBpN6AtBBCfAqGAdv58+fz+PHjFM+Zmpry66+/0qlTJ4oUKUJCQgK1a9fm0aNHhIWFMWbMGLy8vNDr9fj5+fHjjz/y5MkT4O3wzsdmHJy8ePEiS5YswdPTk27dugHQvn17GjZsyLlz53j69CmrV68mW7ZsyjoxMTFERkbi6OjIwIEDMTMzS5V2CiH+mXGVEZVKRf/+/QkPD2fAgAEpwgfGVCoVe/fuxdXVlcjISJKSkv42jCjez3DshwwZQseOHYmJicHFxYUaNWoAcPz4cXbv3k2TJk3o3LmzMsVgnjx5cHR0xNXVlT///FOO/0dieD0CAgIYNWoUjo6ODB8+nEaNGnH//n08PDwICgriwoULWFhYEBERQZYsWRg0aBC5c+fGzc2NpKQkFi1aRKZMmeR1EUIIIT4yicMKIYQQQgghhBCfCcNAdGoNSAshxKcWERHBkCFD+P333/Hy8iJTpkwAZMuWjfPnz5MlSxa0Wi3Dhw/nwoULjB49mhYtWgDQpEkTihcvzosXL/D398fS0pJBgwalepsN4aADBw7w22+/8ezZM3r16kWRIkXQaDSYmpqyaNEimjdvzr59++jXrx9ubm5kypSJ9evXs2HDBkxNTdm+fTtZs2ZNUXlCCPHp6PV65Xz29fXll19+ISoqioEDB/7jvdbZs2exsLBgzpw59OjRg3z58n2KJn81jPu9DRs2EB0dzQ8//ECFChUAsLS0BF5P2/TkyRNat25NunTpgNev24oVKyhQoADbtm3j8ePHZM6cWfrS/4Hx9JI3btxg0aJFeHp6MnjwYFxcXHj16hW7du0iIiKCOXPmYGlpyYgRI/j+++/ZvXs3QUFB6HQ6smfPzsCBA8mWLVuqTVkphBBCfMskvCOEEEIIIYQQQgghhEgVjRo14rfffmP8+PGoVCqGDBmCjY0NAFmyZAFeD9TGxsZSoEAB+vXrpwzgajQabty4wQ8//MCTJ09o2bJlqrZVr9crA/rTp0/H09OTBg0aULhwYYoWLaoMVOr1emxsbNi4cSM9evRg3bp1SlUeOzs7KlasyLRp08iVK5cMbgqRhgzn84wZMwgNDaV///7odLoPCkl7eHiQmJiIj48P0dHRjBkzJkUfId5Pr9crIRudTselS5ewsbHB3d2dPHnypFjWMN3SwoULqVixIra2tqxYsYJdu3ZRoUIFtFotNjY2Etz5HxmuQ2vWrMHc3BwTExN69+6Ni4sLWq0WCwsL6tWrR65cuXB3d2fhwoU0adKEOnXqkDt3bubPnw/8FcqSa5sQQgiROlR6Q01uIYQQQgghhBBCvNebgwbypbUQQnyYq1evEhYWxowZMxg2bBjBwcHKgC3ArVu3yJcvH3Xq1GHt2rXK41FRUUyaNIlt27bh4uKCiYlJqvW9xtt9+PAhV65cYeDAgRw5coQMGTJw/PjxFJU3jK8JR44c4ebNm8THx1OqVCly586NtbW1XCeESCNv3rO1atWK+Ph4ZsyYQe7cuf/V+jVq1ODPP//k4sWLqdber5WPjw/Lly+nVKlSODk5ER4e/s4AVL169di+fTtFixbFzs6Oo0ePkjlzZg4ePEjOnDnTqPVfJuP3ruFYG/4bFRWFh4cHZcuW5cGDBxw9ehQbG5sU54pOp+Pnn3+mc+fO1KtXj02bNgF/BeEkwCaEEEKkLokqCyGEEEIIIYQQH8DwxXZYWBiPHz/GxMQEnU6Xxq0SQojPX+7cuRkyZAjdu3enQoUKKYI7APb29tSuXZv169czf/58bty4waxZs4iMjMTOzg57e3slBJNaYRjDdvv160ffvn2xs7MjKiqKWrVq8fz5c0JDQ7l3756yvKHyAED58uVp1aoVXbp0oUiRIlhbW6eYrkcI8WkZ7tl8fX2ZNGkSDx48oGPHjuTOnfuD7t2Mz+/g4GAKFiwo93z/0suXL3nx4gUPHjxg1apVnDx5kqdPn6YIfhiO8apVq+jQoQP37t3jxo0b1K5dmwMHDpAzZ05lGfFhDO99T09Pdu3alSJs07t3b0qXLs3Ro0e5f/8+N27cSPFeN6zfokUL8uXLx40bN4iPj0/xmklwRwghhEhdUnlHCCGEEEIIIYT4QKNGjSIoKIju3bsTHh5OpkyZpLKCEEJ8oISEBKysrN753Jo1axgyZAjXrl1DrVaj0+nImzcvO3bswNnZOdWmTDEe2Jw5cyaDBw+mefPmTJ48mWzZsnHy5Ek8PDw4efIkI0eOpE+fPtja2n70dgghPq7z589Ts2ZN7t27h1qtZurUqfTv3/9fbyc5ORlTU1NUKpVM3fSBDP3q48ePCQkJITo6GnNzc5YsWULlypXfuSzAlStXsLa2JmPGjFhZWck99n908OBBKleuTPny5QkNDaVSpUrKMU5OTqZ27drExsZSuXJl1qxZg62tLVqtFpVKhVqtRq/XU6xYMczNzdm/f78ylaUQQgghUp/caQohhBBCCCGEEB/I39+fli1bsmTJEoYOHcqTJ0/+cVDB8JsZ+cW2EOJb967gjqGPbN68OfPnz2fcuHG0a9eOoKAg9u3bh7OzM1qtNlUGzHU6XYoqAr/++iuVKlVi7NixZMuWDYASJUowbdo0ihYtSnBwMD/++COPHj366G0RQnxcrq6uTJ8+nYoVK6LX6zlx4gTPnj3719sxMzNT+gkJ7rzbm78PNxyvzJkz4+PjQ9euXbl79y4DBgzg+vXrby1ruEfOmzcv2bJlw8rKSqqX/Q/KlSvH6tWruXLlCj4+Puzduxe9Xo9Op8PMzIydO3dSuXJl9u/fj4eHBw8fPsTExER5fy9btoyLFy9SvHhxzMzM0nhvhBBCiG+LVN4RQgghhBBCCCE+QHJyMmZmZiQnJ9OhQwdWrVrFDz/8wIIFC96aAgb++iXx7du3cXR0TPGYEEKIv/xdNYtPUXlhyJAhPHz4kEOHDtG/f38GDRoEpOyzT548Sd++fTl//jzDhw+XCjxCfMaM+42VK1cyduxYTp8+zfTp0+nXr18at+7rYnysb9++zcOHD0lKSqJEiRKoVCpUKhUPHz5k4sSJhIWFUapUKVauXImTk1Mat/zrptFo2LRpE+3ateO7775jx44d2NnZodFoMDU1JTk5mWrVqnH48GFKlCiBt7c3jo6O7N69mxUrVvD06VMOHTpEzpw503pXhBBCiG+KhHeEEEIIIYQQQog3vBmyeXPw+NWrV7Rs2ZI+ffrQrFmz925n06ZNNG7cmBkzZuDm5paqbRZCCPHvPX/+nKxZs5KUlIS9vT0jR46kf//+SmDTmCHAc+nSJdzd3Rk6dCg2NjZp03AhBPD34T+DNWvWEBAQwPnz55k7dy7du3f/RK37uhkf+/Hjx7NkyRLOnz+PXq+nePHi9O/fn6ZNm5I1a1YePXrExIkTCQ0NlQDPJ6LRaFi3bh0WFhY0atQoxeOmpqZoNBpq1apFbGwsVlZWWFpaUqxYMTJkyEBkZCROTk4ydZkQQgjxiUl4RwghhBBCCCGEMGIc3ImPj8fa2lp5zvCL4Zo1a35QFZ1x48YREBBA/vz52bRpE3nz5k3VtgshhPhwhn781q1bVK9encuXL1O9enV27NiBWq1+56Dl6dOnadOmDSqViqNHj5IxY8Y0ar0Qwvgc3bp1KxcvXuTKlSvUrVuXkiVLKpUPAdauXcvw4cM5f/48c+bMoUePHmnV7K/OsGHDCA8Pp1KlSjRu3Jhnz56xbt06bt26RePGjRk/fjyOjo48efKEkJAQwsLCKFu2LEuWLCF37txp3fxvknGAp0aNGhw4cIBGjRoRHR2tVJWT4I4QQgjx6Ul4RwghhBBCCCGEeIeaNWuSkJDArl27sLKywsPDg6ioKKZMmUKfPn0wNzf/oCmwAgICGDduHNu2baN27dqfoOVCCCH+iWFQMikpCXNzc+7cuUPVqlW5fPkyHh4eTJ48GRMTk3cOXv7yyy/Y29uTNWtWmQ5RiDRiXPXF39+fqKgonj9/rjxWq1YtwsPDcXV1VdYxDvDMmzePbt26pUXTvypLly6le/fudOvWjcGDB1OgQAFevXrFnDlz8PT0pEKFCmzfvl0Jwz9+/JjQ0FBCQkJo0KAB69ev/8fKSSJ1GE+hVblyZY4ePUr37t0JDQ3F1tZWwjtCCCFEGpC7IiGEEEIIIYQQ4g0PHjzA1taWuLg4unTpgpubG1FRUQwZMoSWLVtiYWHxwYO1nTp1okSJEkyePJmEhIRUbrkQQqSeL/k3gDqd7p3/Njc3ByBbtmzs27cPFxcXpk2bhr+/vzJwqdVqU6xbuHBhsmbNik6nk+COEGlAr9crgY+AgIAUQZAXL17QsWNHtm3bRps2bTh79qyyXrNmzQgODqZYsWL06NGDpUuXptUufJGM+0LD9WDnzp1kzJgRDw8PChQogEajYc2aNYSFheHi4sK6deuwtrYmOTkZjUZD5syZ8fLyYuzYscyYMUOCO2nIUHnHzMyM/fv3U758eebPn8+wYcN48uQJJiYmX/R1XwghhPgSSeUdIYQQQgghhBDiHe7du8e4ceOYOnUqAB4eHgQEBJAlS5Z/va2hQ4dy//59oqOjZaBXCPFFMq4w8+zZsy9quijj6gExMTEcOHCA/fv3U65cOYoWLYq7u7uy7K1bt6hUqRJ//PEHw4YNY9y4ce+twCOESFtLlizBy8uLpk2bMnToUPLnz49Op6NkyZJcu3aN58+f891337F8+XIKFy6srLds2TJmzZrF/PnzcXJySsM9+Pzt3LmTlStXEhUVBfzVn+p0Ol6+fEnJkiVxcHBg//79JCYmsm7dOry9vVGr1Rw9ehR7e3sAzp07B7wOP6pUKuWaIn1r2jOuwFO9enUOHTpEs2bNWLx4MVZWVmndPCGEEOKbYprWDRBCCCGEEEIIIT43er2eLFmy8Pz5c+Wx8+fPY2Njozz/ISEcw3KTJk1SKjTIFCtCiC+Rod9q2LAhRYoUwdvbWxmU/ZzpdDplYNjLy4vIyEgsLS1Rq9WcO3cOjUbDxo0bWbhwIXZ2dmTPnp2DBw9SsWJFQkNDUavVjB07VgaXhfjMPHv2jFWrVpEpUybc3d3Jnz8/L168oEyZMjx79ow5c+awZcsW5s+fT5s2bYiJiaFo0aIAtG3blqZNm2JpaSnhkffQ6/W8evWKYcOGcerUKUxMTIiMjEwRZrSysiJHjhw8fPgQvV7P9u3b3xncAWjdujVlypQhOjoaExMT5Zoixz7tGVfg2bNnD4UKFUKtVktwRwghhEgDUpNQCCGEEEIIIYR4g2FAwdnZmb59+9KhQwd27dpFy5Ytefz4cYrwzd8VtFWpVMrULGq1WqZYEUJ80RISEnj06BHTpk1j1qxZPHjwIK2b9I8MU7KEhIQQERFB7969OXjwIL/++it79+6lbNmybNmyhebNm/PixQsAHB0dOXjw/9i764Cq7v+P4897KYMSQcEA7Nbpps6u2R1Ym92KBSKICBYYKHaLgRggds/Czul0OrsDRWfS3Pj94e+egbG570SUvR//KPcEn3Mu53PPve/X/XyOUrBgQSZNmsTEiRPT8xCEEO9hZGREoUKF8PLyolSpUsTHx9OgQQP++OMPxo8fj4uLC8HBwZQuXZrLly/TsWNHzpw5o2yfKVMmZT/iXSqVikyZMhEWFsZ3333HnDlz6N+/P/DmnCUlJaHRaChcuDC//fYbnTp1YsCAARgZGXHs2DEluKPX6wkMDOT58+dUqFBB7oO/UCkDPFevXmXdunXA1z1dphBCCPE1kmmzhBBCCCGEEEIIPjyajk6n4/nz54wYMYLFixfTqFEjQkNDsba2TvVt7efPn5MtW7bP3WwhhPisXr16RceOHYmMjGTo0KEMGjQIOzu79677dr+q0+mUMM3nFBUVRZ06dciePTsrVqzA2dlZaUtUVBSDBw8mIiKCNm3aEB4ermx3//59XFxcWLNmDU5OTp+93UKId6XsR6Kjo5X+Z9q0aYwYMYJRo0bh4eGBmZkZWq2W+vXrc/XqVe7fv0/16tXZs2cPxsYyIcHHMNznXr9+HRcXF86dO0fv3r2ZP3++ss6dO3eoUKECT548wc7OjvPnz5MzZ07gzWtAREQE3t7eODg4sH79+q9ixLYvyduvm2k9gmfK3yejhQohhBCfn4R3hBBCCCGEEEL856UM4URFRaHRaMiaNSs2NjbKOvfu3WPcuHFKgCckJERZvn37dpYvX46bmxsVK1ZMl2MQQojP5dWrV7Ro0YLIyEguXbpEkSJFPrju0aNHuXPnDh06dPiMLUzt119/pVy5cowZM4ZRo0Ypfb6hSPngwQN++OEHrly5wvbt22nQoAFJSUmYmpoq62g0Gin4C5EOPjb0165dO/bv38+9e/cwMzNTHq9atSrNmjVDrVbTpk0bnJ2d07C1GUvK++OnT5/yww8/cPHiRXr06JEqwLNhwwb69OmjTLPVqlUrTExMWLJkCaGhoQAcOXIER0fHdAtxfo1ShmcuXrxIiRIl3nlcCCGEEBmL3CUJIYQQQgghhPhP0+l0SmFi/Pjx1K5dm+LFi9O8eXNmzJihrJc3b158fX3p2bMn27dvp3Pnzpw9e5aQkBA8PDw4ePAgefPmTa/DEEKIz8bS0pINGzZw5MiRvwzuXLlyhapVq+Lu7s7hw4c/YwtT02q1AFy+fDlVMdownWHu3LkZPnw4ANevXwfA1NQU+HMaRQnuCPH5abVaJehx8OBBlixZwtixY9mxYwdRUVHKei9fvuTZs2ckJiZy69Yt4E3AISQkhMuXL1OsWDGGDRuGs7MzGo0mXY7la5OyrwwNDWXDhg1kzZoVExMTFi5ciKurq7JuvXr1CA4OJmvWrPj6+lK+fHlKlizJrFmzyJcvnxLcSfl8ir9neP2pUaMGLi4uHD16VHlcvpMvhBBCZEzyrlMIIYQQQgghxH+aoYjg7e3NxIkTKVSoEJUrV+bw4cMcOXKE27dvM23aNADy5MmDr68vJiYmBAcHs337dkxMTHBwcODo0aPkypVLvlEshMhQDN/wNxQKVSoVWq0WKysrKlWqlGqdt5mYmNCvXz8WLVrE/v37qVq1apq29UP9b8GCBcmVKxfHjh3jxIkTVK5c+Z118uTJA7yZhiclGd1AiPSRMlzt7e3NvHnzePnypbK8atWqDBw4EBcXF6ysrKhatSp79+7Fzc2NoUOHcuLECZYvX469vT3ff/+9sp0E8T6O4dy7u7uzZMkSnJycqFKlCjY2Nhw8eJC5c+ei1+uZM2cOWbNmpWnTplSsWJGwsDDu3LmDiYkJVapUoWrVqu9MNSv+maZNmzJ8+HC8vb0JCAigcuXK8tokhBBCZFAybZYQQgghhBBCiP8kQxFBr9dz9epV6tWrR9OmTRk6dCgFChTg6NGjDBo0iDNnzuDq6srMmTOVbaOjo9mzZw+RkZFYW1szZMgQcuXKJYUJIUSGkrJP02q1vH79OlUR9mPCir///jt9+/YlKiqKQ4cOYW9vn+Zt3bx5M6dPn8bNzQ1ra2sAAgMD8fb2pnXr1kyePBlHR8dU248bN44JEyawYsUKWrdunSZtFEL8c35+fowbN46OHTvSq1cvbG1t2bNnD8OHD8fY2JjFixcr0/J169aN5cuXK9uWKFGCLVu24OzsLOHq/8HSpUvp0aMHQ4YMYeDAgeTLl4/k5GQuXLhA69atuX37Nv3792f27Nl/uR859/+blMHYBQsW0K9fP7799luCgoKoVq3aX25jOOfy3kQIIYT4ukjMXAghhBBCCCHEf5Lhg+wDBw5gbW1NpkyZ6NOnDwUKFECn01G5cmUWL16Mq6urUpQwBHhy5MhBx44d6dixIxqNBmNjY/lwXAiRoaTs0+bPn8/WrVs5ffo0Tk5OVK1alb59+1KoUKG/7fuKFy9O9+7d6d69O7du3UqT8E7KETp8fHxYvHgx0dHRlCpVChcXFwCaN2/OiRMnCA8PJz4+nh49etC4cWOMjIwIDw8nJCSE4sWLU6tWrU/ePiHEh31o5C6Ao0ePMn/+fFq1aoWvry+FCxdGo9Fw9+5dTE1NyZs3Lw0bNlTWX7p0KXXr1uXZs2dYWFjQuHFjbG1t5R7tf6DX6zly5AgmJiZ06dKFfPnyAW9GIytbtiy7du2ievXqzJ07F0C5V05KSlKmHTQ8txLc+d+kvDb69OlDXFwc7u7uf7mNSqVi165drFu3jqlTp2JhYfE5miqEEEKIT0RG3hFCCCGEEEII8Z81depUPDw8KFOmDKamppw4cQKdTgf8OZ3WuXPnGDBgAEePHk01Ak9ycjImJibp1nYhhEgrKQuG7u7uzJgxg4IFC1KsWDEeP37M8ePHcXZ2Zs2aNVSoUOGj9jN69Gh8fHzSdMoab29vJk+eTPfu3RkwYABlypRJtfzcuXNMnjyZjRs3kpSUROnSpdHr9dy4cQNra2sOHDggI3QI8Zm9fv0aCwuL94Z4Fi9eTJ8+ffj555+pU6cOGo2GiIgIPD09UavVnDp1CltbW5KSknj58iV2dnbv7F+u5/+NVquladOmnDhxgqioKExNTd8ZzSUyMpKGDRuSmJhInz59mDdvXno3+6v19t9pYmIiZmZmAOzfv18Jlt66dUsJUr3P06dP+e6777h79y4BAQF4enoCMgWkEEII8bWQu1YhhBBCCCGEEP9ZtWvXpmDBgly4cIE//viDV69eoVarSfk9lzJlyjBnzhwqV67M7Nmz6datG4AEd4QQGZahyDd79mxmzZpF//792bZtGxs2bODo0aO0a9eO27dv07dvX7RaLR/6bqBh6g54E94xNjZGo9GkSZt37NjBvHnz6NixI97e3qmCO4b2lSlThoCAAJYsWUK5cuV49uwZarWaTp06cfToUZydndFqtVLoF+Iz2bdvH9988w3Hjh1DpVIp16qh3/j1118xNjamePHiJCcns3btWiW4c/LkSWxtbQG4d+8eCxcu5MmTJ8q+DfuS6/l/ly1bNp4/f86aNWuAP8+lkZERGo2GokWLkjdvXuzt7VmwYAEjRoxIz+Z+1QzntnHjxkRGRirBnQEDBtCnTx8OHToEgJOTE8AHX3etra2ZPXs2efPmZefOnahUKgnuCCGEEF8RuXMVQgghhBBCCPGfpNPpKFu2LOvWraNQoULcvHmTYcOGAW+KElqtVlm3TJkyzJ07l6JFixIeHs7z58/Tq9lCCPFZxMbGsmHDBgoWLEjfvn0pUKAACQkJbNy4kaNHj1K0aFF27dqVaiqa9xUT3y6cp9XIO6dPnyYhIYG+ffvi7OycalnKwqWTkxPt2rXj0KFDnDlzhsOHDzNjxgxy584tU+sI8ZkdOHCAW7du0bVrV06dOqUEeAzX7DfffENycjInT54kMjISLy8vJbiTcpQdd3d3Fi1aRFJSkvKYBBb+OcO9r2Eqwt69e5M1a1bWrFnDrVu3lPWSk5MxNjbG3t4ea2trunbtSoMGDejTp096NT1D2LBhAzt27KBJkyZcu3aNkSNHMm/ePJo2bUqRIkWAP19TP/T3bWxsTO3atenevTvHjx9n27Ztn639QgghhPj3JLwjhBBCCCGEECLDMxSU3y4s63Q6SpUqxdq1aylatCiLFy/Gw8MDeDfAU7p0aSIiIrh58ybZsmX74DdehRAiI3j69CnHjx+nfv36FC9eHI1Gw6ZNmxg8eDBqtZoDBw4oxfNz587x+PHjdCmW63Q69Ho9e/fuJUuWLOTPnx+9Xq+M3JFyPfizOG1iYkK2bNnIlCmTEtiR4I4Qn9eYMWPw8/Pj2rVrtGvXLlWAB6Bw4cIYGRnRt29funXrhrGxMSdOnEgV3Jk/fz5nzpyhefPm7502S3zYh/pJQ0CkQIECtG3blp07dzJmzBguXryIXq/HxMQEnU7HihUruHfvHl26dGH79u04Ozun2ehq/wUtW7Zk9uzZaLVaihcvzoQJExg7diyDBw8mR44cH72fLFmy4OLiQqZMmbh27VoatlgIIYQQn5qEd4QQQgghhBBCZGharVYpKD9+/Jhbt25x69Yt1Gq1UpwoUaIE4eHhFClShKlTp35wBJ7ixYuTM2dOdDqdfKNbCPHVShk+1Ol0qfo5g/j4eLRaLRqNBo1GQ3h4OMOHD39n1IvExERcXV1Zu3ZtuoQa1Wo1KpWKIkWK8Pz5cy5cuIBKpUo14o9Op0OtVvPixQu6devG69evpQ8XIp0Z+h0/Pz9GjRrF7du3lQCP4fqtXr06Q4cO5fHjxzx58oQ5c+akCjGsWrWKoKAgbGxsGDFiBKamphKu/kgppwgMCwtj0KBBVKxYkb59+zJnzhwA8uTJQ//+/WncuDEhISH07t0bf39/fvvtNyZMmEBAQAA5c+ZUpi+DtBtdLaMzBKf69+9P8eLFMTIywtjYmDJlyuDo6PiXU1S+T/HixVm3bh2urq5p1WQhhBBCpAGVXu5mhRBCCCGEEEJkUCmnQJk2bRqrVq3i2rVrqNVqfvrpJ9q3b0/lypWV9X/77Tfatm3LlStXcHd3JzAwEPiz8CuEEF+7lFPSJCQkkClTJmXZsmXLKFy4MJUrV0ar1VKpUiViYmIYPnw4Y8eORa/XvzNdzejRo5kyZQqrV6+madOmn63tb5s/fz79+/enQYMGzJo1iwIFCgBvpncxMTEBYMqUKQwfPpydO3dSr169NG2rEOLvpbxP8/X1Zfz48Tg7OxMWFkb58uWV9fr06cOiRYtwcHCgZ8+e5MyZkwMHDigjbh08eBBnZ2eZ+u4jpbyvHTZsGLNmzSJTpkyo1WpiY2PRaDTUq1ePVatWYWNjw9mzZwkNDWXJkiW8fPlS2U+hQoXYtWsXzs7Ocq/8Lxhe25KTk3n+/DkFChSgePHinDt3DrVazb59+/j+++8/+hy//Vqp0WgkVCWEEEJ8JSS8I4QQQgghhBAiQ3q7MBEUFESJEiWoWrUqCQkJhISEUK1aNQYPHkzLli2V7VIGeHr37s38+fPT6xCEECLNVKtWDY1Gw8GDBzExMaFv374sXLiQ0NBQWrdujYmJCf7+/vj5+ZEpUybs7Oy4fPkymTNnVvaxatUqfHx8KFy4MGFhYVhZWaVZe1MW5XU6HTqdLlUxUqvV0rZtW7Zs2YKrqyu9evWiWLFiyvJ169bh7e1N7ty5Wb9+PdbW1mnWViHEX/tQCCFlgGfNmjVUqFBBWTZ69GiCg4N5/PgxGo2GPHnyULVqVSZPnkyePHkkuPM/mDhxIj4+PvTt25d+/fqRPXt2bt26xZAhQzh16hSVKlXi559/JmvWrLx+/Zro6Gi2b99OYmIiefLkoU6dOtjZ2cm5/xdSXgt//PEH2bNn58WLF+h0OsLCwhg6dCgqlYrIyEgqVqz4ztRm79uPEEIIIb5eEt4RQgghhBBCCJGhTZ06FT8/P3r06EG/fv0oWrQoL168oEiRIjx9+pTSpUvj5+dHixYtlG0uXrxI7dq10ev1XL16VYq8QogMJT4+nho1anD69Glat26Nra0tCxYsYNCgQQwfPpxcuXIB8PTpUzp06MDevXupWLEie/bsITExERsbG6ZNm8bs2bPR6/UcPHiQPHnypFnxMOV+58yZw8GDB3n06BGNGjWiSZMmlChRAr1ez7Fjxxg5ciQHDhygTJky9OvXD3t7e/bu3cv69etRqVQcPnwYR0dHKXQKkU5SBj3Onj1L5syZKVq0qLLcz8+PcePGvTfAc+3aNZ49e8aTJ08oXbo0dnZ2ZM6cWcIj/4OoqCjq1KlD9uzZWbFiRarRc6Kiohg8eDARERG0atWKiIiID+5H+tL/Xcq/20WLFrFu3Trq1KlD7969lTDs9OnT8fLyShXgMThw4AAPHjygY8eO6dJ+IYQQQnx6Et4RQgghhBBCCJFhXbhwgY4dO1KkSBH8/f0pXLgwr1+/pnz58sTExFCvXj2WLVtGqVKl8PPzo1WrVsq2V65cwdLSEgcHh7+cqkUIIb4mhkJrUlISHTp0YMOGDQAMGjQIf39/smbNCvxZVIyKiqJ3795s27aNrFmzYm9vT0xMDM+ePaNo0aJs2rTps01XYxhFLUuWLKjVamJiYihRogTz5s2jatWqaDQazp8/z6xZs1i+fLmynYWFBeXKlWPZsmU4OTlJoV+IdJLy2pswYQILFy4kOjqaS5cu4eDgoExx96EAz/vux+Qe7X/z66+/Uq5cOcaMGcOoUaOU58bwGvHgwQN++OEHrly5wrZt22jYsKGc608oZejJw8ODBQsW4ODgwLhx42jbtm2q5TNmzMDT0xOVSsX+/fv5/vvv2bJlCx4eHmTPnp0dO3ZgaWmZnocjhBBCiE9EJroUQgghhBBCCJFhpPygW6/Xc/fuXWJiYujZsyeFCxcmNjaWqlWr8vz5c6ZMmULz5s1xcHBgwoQJTJkyBZVKpUyhVaRIEQAp8gohMhS1Wo1Wq8XU1DTVNFcXLlwgS5YsAGg0GoyNjdFqtTg4OBASEsLWrVvZsWMH9+/fJ2fOnNSsWZO2bdum6ZQpKfv0TZs2ERwcTP/+/enatSu5cuVi6tSpTJs2jY4dO7JixQpq1KhBuXLlWLp0KW3btuXZs2c8fvyYChUqUKpUKaysrKRPFyKd6PV65dobNmwYM2fOpHXr1nTo0AFHR0fgz3uuMWPGADBu3Djat29PWFgY5cuXf294RMIk/xutVgvA5cuXU/WLarUanU5H7ty5GT58OD169ODGjRuAnOtPyfDaNmHCBKZPn06/fv0YOHAghQoVUpYbnpfBgwejUqkYOXIkNWrUoFKlSly6dAmA7du3Y2lpKcEqIYQQIoOQkXeEEEIIIYQQQmQIKYu8r169wtLSkqioKM6ePUujRo1ISkqiV69ebNiwgUmTJtGzZ09MTExYu3Yt7dq1w9TUlOzZs7NkyRLq16+fzkcjhBBpLzAwkFu3bnH58mUiIyNp3Lgxq1evxtzcXCkaGoI8BjExMZibmys/p9WUKSkLkc+ePWPPnj0EBAQQERFBwYIFlfUCAgLw8fEhd+7crFy5kurVq39wnzK9ixDpLzg4mH79+tG/f38GDx5Mvnz5Ui1PGSQxjMBTqFAhli5dSuXKldOjyRnSy5cvKVGiBKampoSGhqY6t4a+cvfu3dSvXx8fHx/Gjh2bjq3NmC5evEjz5s3JmzcvwcHB5M+f/511Ur5uLVu2jJCQEO7cuUOhQoVYuHAhjo6O77xOCyGEEOLrJe9WhRBCCCGEEEJkCIYPtl1dXenUqROxsbE4ODjQoEEDAG7evElkZCS1a9emT58+ytQM33//PaVLl6Zv376o1WpKly6dbscghBCfg06nA8DNzY25c+eyZ88eGjZsyLZt2+jQoQOxsbEYGRmRlJSkFASfPXsGkCq4A3yyMMyjR494+fKl8rMhuDNw4EBKly5NUFAQZcuWpWDBguh0OjQaDQDe3t74+/vz4MEDfvzxRw4dOgS8Cf+8TYI7QqQfvV5PcnIyGzduxN7eHldX13eCO4AydRPAmDFj8PPz49q1awwbNozk5OT3Xtvi/Qzn8W16vR4rKysGDx7MvXv3mDlzJnfv3lWWG/rK48ePkylTJsqUKfNZ2vtfc+/ePW7evEmnTp3eG9yBN8+F4fWua9euhIeHc/DgQSIiInB0dESr1UpwRwghhMhA5B2rEEIIIYQQQogMQ6fTsX//fs6dO0dcXFyqZbdv3+bevXuUKVNGKUpotVrmzZvH8+fPGT9+PNevX8fBwUGZSkAIITKCtwu4huJ3ymlSVq9erQR42rdvz6tXrzA1NUWr1bJjxw4CAgI4ffp0mrTv7NmzlChRgvnz57/Td2fOnJmHDx9y8eJF5ThUKhVGRkZKXz1ixAglwNO5c2f27dsn04cI8YVRqVQ8ffqUffv2KUE8QyghJb1er0zdBG9G35k8eTKrV6/GxMREru2PpNVqlfvd7du3ExoaysaNG3nx4oVyDps0aULz5s0JDw9n4MCBbN68WelXw8PDCQkJoXjx4tSqVSvdjiMjMrwGG6a+Mpzzt68Hw+OvXr3i8ePHANja2pI7d27Mzc1TTUUnhBBCiIxBwjtCCCGEEEIIITIEQ7FnyJAh3L17l8DAQODPbw87OzuTNWtW9u3bx6lTpwBYs2YNGzdupHz58hgbG2NmZgYgH4QLITKMlAXciIgIhg4dSsOGDRk7diy//fabsp6lpSVhYWFKgKdt27Y8fvyYtWvXMmTIEFauXImTk1OatPHZs2eo1Wp+//33d0YQmDx5Mv7+/sTGxrJixQr27NmDSqVCpVKhVqtTBXgmTJjAnTt38PT0JCkpSUboEOILY25ujrW1NbGxsQAYGxunuk51Oh0qlYrHjx9z4sQJ5fFhw4bh5OT03rCPeD/Dvezw4cNp0qQJnTt3plWrVrRt25YNGzYAUKxYMUaMGEGHDh3Ys2cPrVu3pkKFCpQrV45evXqRlJREREQENjY2HxzFR/xzhvBUmTJlUKlUyt+6sbGxcp4NwRydTsdPP/3Eli1bPrgfIYQQQmQcKr28ixVCCCGEEEIIkYHcvn2bGjVqYGRkxJYtWyhRooRSvB43bhzjxo3Dzs4OW1tbrl+/To4cOTh48CCOjo7o9Xr5IFwIkWHodDoluDN8+HBmzJiBiYkJVlZWREVFUaxYMQYOHEifPn2Uvi8mJoZOnTqxadMmMmXKhF6vx8HBgT179pA/f/5U+/xUtFotly5dwsnJCQsLC06cOEHhwoXJli2bss7kyZPx8vKiaNGiLFiwgGrVqgFvCpw6nU4pVM+ZM4emTZvi6Oj4SdsohPh39Ho9L1++pHr16ly4cIFVq1bRvn17ZRn8GUZo3749x44d45dffsHW1jbd2vy1W7BgAe7u7jRu3JiaNWty/fp1Fi9ejJWVFSNGjKBfv37Am6llT506RVBQENHR0WTPnp3vv/+eESNGkDt3brRarQTb/0d/9d7i5s2b/PDDD9y+fZt58+bRp08fAJKSkjA1NQVg2bJlDBo0CD8/P4YOHSrTPwohhBAZnIR3hBBCCCGEEEJ8dd4uHhuKCoYPyBcsWEC/fv1SfRAO8PjxY7Zs2cKkSZOwsrKiSJEiTJ48WQoTQogMzc/Pj4CAADp16oSrqyvlypVj3bp1uLi4UKhQIQYMGMDAgQOVAmNcXBxTp07l1q1bmJiY4OfnR65cudBoNO+MjPNvvV3YnDVrFoMHD2bOnDl07NgRKysrZZm/vz+jRo2iZMmSzJkz54MBHkD6dCG+UIa+p2bNmowePZrq1asry3Q6HWFhYfj5+VGpUiUWLFhApkyZ0rG1X7fevXvz4MEDZs+eTb58+dBoNOzbt48OHTpgbGyMn58f/fv3V9ZPSkoiNjaWzJkzY2JiokxPKH3p/yblubt//z6vX78mJiaGsmXLolarUavVbNq0iZYtW2JtbY2vry9DhgxRtl+zZg2jR48mU6ZM/Pzzz+TIkSOdjkQIIYQQn4uEd4QQQgghhBBCfLUuXLhAyZIllZ8NReALFy7QoEEDAA4cOECBAgVSbffq1SvMzc1JTk7GzMxMChNCiAxr69at9OvXjyZNmuDm5kahQoV4/fo1lSpVIioqiuTkZDJlysSoUaNwdXVVgjSG/tQQ2Pkc/aRer2fnzp2MHDmSe/fuMW7cODp06JAqwDN+/Hh8fX3fCfAIIb4OL1++ZOzYscyYMYOyZcvSo0cPZQSeFStWMGfOHLRaLZGRkeTOnVtGRfxI7+ujv/32Wzp06MCwYcOAP/v1yMhI2rRpg5GREaNHj1ZG4EmLkdX+q1Key4kTJ7JmzRquXr1KcnIy33//PW3btqV79+5kzZqVJUuW0LNnTwBq1KhB3rx5efjwISdPniRbtmwcPHgQJycneX6EEEKI/wB5pRdCCCGEEEII8VUaOXIkpUuX5qeffuL8+fO8ePFCKe6ULFmSH3/8kYcPH3Lo0CEANBoN8KZwYWFhgVqtxszMDL1eL8EdIUSGlJiYyKFDh9DpdHTv3l0J7lSsWJGnT58yZ84cli5dSnJyMtOmTWPGjBnK9DU6nQ5AGWnnc/STKpWKevXqMXXqVJycnPD29mb16tW8fPlSWcfHx4exY8dy4cIFBg8ezN69e9O8XUKIv/ex3xG2srJi0KBBeHl58csvv9C/f3/KlClDkSJFcHd3R61Ws3v3bmVURAnu/L2UI4+Fh4czYcIE5s2bh6OjIzY2NsCbUXUM57JmzZpERESg1WoZPXo0CxcuBJBgyCdkOJceHh54e3tjbm6Op6cnXl5ePHjwgJEjR9K7d2/i4uLo3r07e/bsoVatWty4cYOVK1fy8OFDWrVqxdGjR3FyclKmABZCCCFExiYj7wghhBBCCCGE+OokJSURGhrKtGnT+P3337GysqJ69eoMGzaMsmXLkjVrVu7fv0+VKlXImTMnJ06ckOKPECLDe3uECr1ez7Rp07C3t6djx44kJCTQpEkTzp07x+TJk+nUqRM6nY7mzZuza9cuChcuTNeuXfH09EzXPlOr1XLgwAGGDx/OzZs3CQgIeGcEngkTJjBy5Ehq167N9u3bMTU1Tbf2CvFfl7LvuX//Pnny5Pmo7SIjIwkODub+/fvY2NhQqVIlOnfuTI4cOWRUxP/B8OHDmTJlSqrHatSowc8//4yJick75zQyMpL27dsTHR3N8uXL6dSp0+ducoa2Zs0aunXrRteuXZWR7wAWLlxI3759KVeuHJGRkZibmwPw7NkzNBoNt27donDhwmTOnJlMmTLJtSCEEEL8h0h4RwghhBBCCCHEF89QFDK8hTUUiJ49e8bhw4dZtGgR27ZtQ61W06xZM1q1akWHDh3o1asXy5YtY+bMmbi6uqbnIQghRJpKOZ3G2/9PSkoiU6ZMLF26lP79+zNo0CD8/PzIkiULAGPGjGHdunVcuHCBYsWKceLECaWYmF4M0+Z4enp+MMAzc+ZMmjVrhrOzc/o1VAihGDFiBNHR0QQHB//tFD9/NR2WTA/0cVKepxUrVjBw4EDatGlDw4YNSUpKYty4cVy+fJl+/foxderU9wZBfv75Z9zd3dm5cye5c+dOr0PJUAx/27169WLjxo3s37+fkiVLotFoCA8Px9fXF61Wy+nTp8mePTsJCQmYmpq+929epo0TQggh/lskvCOEEEIIIYQQ4ouWssig1+t59eoVVlZWJCUlpRppYe3atezevZvFixcD0LZtW5ycnJgxYwZNmzYlPDxcPvwWQmR47u7uxMTEMGfOHIyNjVMV/oYNG8acOXO4cuUKjo6Oyja1a9emcOHCDB48GEtLS3Lnzv1ZCoZ/9ztSjsBz48YNJkyY8E6AB95Mi2iY3ksIkT6ePXtG/fr1uXLlChcuXEjVx/yVlAEUCSp8vLfP1dy5c1m2bBmrV6+mQIECAERHR1OzZk0uX76Mq6srkydPfm+AJzExETMzMxnh5V94+9y9fv2aSpUqYW5uzvHjx0lISGDTpk0MHz4ctVrNyZMnsbOzA+Dy5cu8ePGC77//Pr2aL4QQQogvhMTXhRBCCCGEEEJ8sVJ+EL5gwQJatGhB8eLFqVq1Kr6+vty+fVtZ18XFhYULF3Lo0CF69OjB/v37CQwMJCkpiZ9//pmYmJh0OgohhPg8YmNjWbx4MYsWLcLb2xuNRoNKpSI5ORmAP/74g8TERI4ePapsEx4ezp07dyhcuDDFihUjd+7caLXaNCmga7XaVD//3e8wMjKiRo0aTJo0iQIFCuDr60twcDCvX79OtZ4Ed4RIfzY2NrRs2ZKYmBhCQ0PR6/XodLq/3S7laCMS3Hm/t89jyuDO4MGDKVasGGvWrKFBgwZKcCc5OZkcOXJw5MgRihYtyuzZsxk+fDgJCQkYGRml6o/NzMwAJLjzP9LpdMq5O3nyJAAWFhZky5aNxMREALZv3/7e4I5Op6NNmzYsWbIEjUaTPgcghBBCiC+GhHeEEEIIIYQQQnyR9Hq98kG4u7s7AwYM4NKlS5QrV46EhAQmT55M/fr1OX/+fKrtqlSpwowZM/jll1/o3Lmzso6FhQUy+KwQIqPS6/VkzZqVq1evUqhQIaZMmYKXlxcajQYTExMAevfujY2NDePHj2fkyJEMHDgQNzc3ADp27KjsK60KuIb9enp6MmPGjI/epmbNmgQGBmJubk5wcLAU+IX4whjurwYOHEjhwoXZvHkzKpVKpr76RAznce/evSQnJ6NSqZRAz7Vr17hy5Qrnz59XgurJycmYmJig0WjIli1bqgDPiBEjlACP+DQMz8/QoUP5/vvv2bp1KwDffvst586do1u3bgwZMgQjIyOOHTumBHcAgoKCiI6OpnTp0nK9CCGEEELCO0IIIYQQQgghvkyG4uysWbOYOXMm/fv3Z/v27WzZsoXTp0/TqlUrrl27Rs+ePdHpdKmCOZkzZyZPnjwsWbKEdevW4eTkpIxAIYQQGcHbYUSVSoVGoyFnzpwcOnSI/PnzExQUpAR4AIoXL05gYCCxsbFMmDCBJUuWkDdvXvbt24e9vf07I+OkhZs3bxIYGKiMTvAxjIyMqF69OitWrGDv3r2Ym5tLGFOIz+Tta+19o+moVCr0ej2ZM2emUaNGnDx5knnz5n2uJv4ndOrUiYYNG7J27Vo0Go0S9Ni6dSsdO3bk1atXREREEBUVhYmJCVqtFmNjYyXAc/ToUUqVKsWMGTMICAhI56PJGFJeC7t27WL16tX06NEDJycn4M1zZmJiwvLly0lKSuK3337D3t5e2Xbt2rXMnz+fokWL0r59ewnvCCGEEAKVXt7pCiGEEEIIIYT4QsXExNCkSRP++OMPwsLCKF68OPHx8fz8888MHDgQc3NzDhw4gJ2dnTKFwNv/QurpBYQQ4mun0+mUIp9hhAXDYxqNBmNjYx4/fkyVKlW4efMmQ4cOZeLEiZiYmJCcnMwff/zBgQMHyJUrFyVKlMDGxibVNIVpSavV0qFDB9avX09kZCRVq1b9223e7sNTHr8QIm3Fx8eTOXNmdDodKpVKuRZv3LiBnZ0dlpaWqa7RixcvUqFCBRo1asTatWvTs+kZypkzZ2jbti0ajYaAgABcXFyUUdV0Oh1du3YlNDSUChUqsHHjRiWQaWRkpLwuPHv2jNatW7Ns2TIlYCL+vXv37rF+/XoWL17M5s2byZcvn/I6FRERQdu2bTEyMiIwMJDGjRtjZmbGggULCA0NRavVcvToURwdHeW1TQghhBAy8o4QQgghhBBCiPR17tw5nj59+t5lT5484fjx4zRs2JDixYuj0WjYvHkzgwYNwsjISAnuAJw/f54//vhDKR6lLPRKcEcIkZEYins+Pj6MHDmSuLg41Go1Op1OGWkhZ86cHDlyBGdnZ6ZNm4aHh4cS9LG3t6ddu3ZUq1YNGxsbdDrdZ5tCxcjIiCZNmqDT6Vi/fj16vf69I3mk9HYfLsVNIT6PvXv3UqFCBS5evIharVZG4fHz86Nw4cJ06tSJPXv2EBsbq2xTokQJ2rdvz7p164iMjEynlmc85cqVY8OGDZiamjJw4EBWr14NoIzCs2zZMjp27MjJkydp27Ytjx8/xsjIKNUIPDY2Nuzbt08ZkVL8eyNHjqRhw4asX7+esmXLki9fvlSjfbZp04b169djYWGBm5sbpUqVUkbGc3R05MiRIzg6OqLVauW1TQghhBAS3hFCCCGEEEIIkX6uXbtG2bJladKkyXsDPPHx8eh0OjQaDVqtlvDwcDw9PVGr1Zw8eVIJ7iQkJODq6irf8BZC/Gc8ffqUhQsXMn36dIKCgj4Y4AkPD8fKyoqlS5fi6elJcnLyO/tKi4Lh24EcvV6vFP47d+5MjRo1CAsL4+nTp6lCAUKIL8eWLVu4ePEiP/30E5cvX0atVhMbG4uDgwONGzdm27Zt1KtXj2bNmhEUFKSEeNq1awdASEgIcXFxfxvQEx+nVKlSrF27FjMzM3LkyAGAsbGxEvwICQmhQ4cOHD58GBcXl3cCPCm9/bP4OCmnl4yNjcXa2pqrV69y6NAh5b1MynOr1+tp0aIFx44dY+HChfTp0wcvLy8iIiLYsmULTk5On23kOyGEEEJ8+WTaLCGEEEIIIYQQ6ebRo0d4e3sTEhJCzZo1WbNmDba2tsrypKQkKlSogF6vZ9iwYfj5+aHX61MFdwB8fX0JCgoiPDycRo0apcehCCFEmnrfdBpXrlyhTZs2XLt2DW9vb4YNG0aWLFmUdbVaLS9evKBq1arcu3ePuLg4Ro8eja+v72dra3R0tFJkBpQi8+TJkxkxYgSjRo1i9OjRMkKaEF8oNzc3pk+fTokSJZQpTA127drFwYMHmTNnDq9evaJYsWI0a9aMgQMH0r17d65evcqxY8fImTOnTGH6Cb1+/RoLC4tUjxkCIHq9nk6dOrFq1Spq1KjBypUryZUrVzq1NGNJ+Tc8YsQInJ2dadq0KVu3bsXNzY34+HiWLVtaJ9tCAAEAAElEQVRGp06dUq3/V3/7MlWWEEIIIVKSuwIhhBBCCCGEEOlCr9djb29PQEAAffv2Zd++fbRv31751qphGpdmzZrx22+/0bdvX3Q6HRcvXkwV3Fm1ahWhoaFUq1aNKlWqpNfhCCFEmjIU986dO6dMd1KkSBEiIiLInz8/AQEBBAYGEhsbi1qtJikpCSMjI7Jnz469vT2BgYFUr16dzp07f/K27du3j4kTJ77TVk9PT+rVq8eiRYuIjo4G3kybpVKp6NGjB7ly5WLPnj3K8ch3DIX4chhGGAkKCmLQoEFcvHiRdu3acenSJWWd+vXr4+/vz/Hjx5k1axampqZMmjSJIkWKcPbsWW7fvs2ECRMkuPOJGYI7KftMwwg7KpWKFStW0KlTJw4cOICrq6uMfPSJGP6GJ0+ezKRJkzh9+jRZsmThxx9/JDAwEFNTU2bOnMmePXuU9VP+7ad8vgz/l+COEEIIIVKSOwMhhBBCCCGEEOnC8IG2vb09Pj4+9O/fP1WAR61WY2RkRL9+/ahWrRrx8fE4OTlhbGzMixcvAJgxYwajRo0CYNGiRVhZWUmBQgiRoaQs9gUEBCjTTaUM8GzYsIH8+fMzceJEJk+eTExMDKampuh0OhYtWsTvv/9O48aNiYyMxNnZWdn2U3j58iWjRo3C29ubwMBA5fHo6Gju37/PpUuX6NOnD7Vq1cLNzY0HDx7w+vVrbG1t6datG8eOHWPFihUAUtwX4gtiuE8DmD59OgMGDODixYu0bduWy5cvA28CPjqdjqJFizJgwABOnTrF8uXLadeuHU+ePMHExITjx4/z+vVrQAJ6n9rbfWbKAM/SpUsZMGAA06ZNk4DIv5RyqiyAbdu20b59ezw8PLC2tiZr1qx07tyZwMBAzp8/j5+fH7t37wZSX0cpny95vRNCCCHE+8i0WUIIIYQQQggh0pXhG6mPHj1i/PjxzJ07l9q1a6eaQuvOnTv07t2b3bt3Y2lpSa5cuXj58iVPnz6lSJEibN68GWdnZ2XKACGEyAhS9mmxsbGsWrWKQYMGUa5cOfr370/79u2V5VeuXKFVq1ZcunSJVq1aMXToUPbv38/y5cuxtrbm559/Jlu2bGnSzj179jBu3DgOHTpEQEAAXl5eyrIzZ85w4sQJpk6dys2bN7Gzs6NevXoMHDiQhIQEGjRoQKNGjVi6dCnm5uZS0BQiHbw9dY9Go8HY2BiAX375hW+//RaAwYMHM2vWLEqUKMHatWspWrSo0k+9fQ928OBBVq9ezYIFC1iwYAG9evX6vAf1H5by+Xvfz+J/ExgYSNasWZkyZQrz5s2jfv36qUbWiYuLIzg4mGHDhvHdd98xZswYfvjhBwAZfUoIIYQQH0XCO0IIIYQQQggh0t2HAjyrV69WpsiKjo5m69atbN++nYcPH5IzZ05q1apF+/btyZEjhwR3hBAZSspi+pQpUzhw4ADXrl3j1q1bJCcnU7p0aTw9PWnbtq3S992/f5+2bdty/PhxZT/58+dnz549ODs7v1Og/7dSFiMjIyMZOXIkx44dw9/fnxEjRqRa948//mDXrl1ERESwceNGVCoV7dq1Y8eOHSQnJ3Pw4EHKlSsnBU4h0lH//v0ZPHgwRYoUAd6EdbZv3054eDhly5ZVHns7wJOyb0l5DZ84cYIffviBqlWrsnHjRkxNTeX6Fl8dvV7P0aNHqVatGvny5SMxMZF169ZRsWLFd95/pAzwfP/99wwfPpzGjRunY+uFEEII8TWR8I4QQgghhBBCiM/q74rHDx8+JCAg4L0BHoOYmBjMzc0/ep9CCPG18vLyIigoiPbt29O4cWOMjIzYsGEDW7ZswcHBgdGjR6cK8CQnJxMWFkZ0dDRZs2alZcuWaRpwTDmiw969e/H19eXYsWOpRuB5+3dv376dgwcPMnfuXLRaLfHx8bRv357g4GAyZ878ydsohPh7S5YsoWfPnnz33Xfs27cPX19fpk+fjoeHB+7u7uTIkUNZ958EeFq3bs2+ffu4dOkS9vb26XJsQnwKU6dOxcPDA4BZs2YxYMAA4N1RdeLi4li6dCkDBw6kWbNmhIWFYWZmli5tFkIIIcTXRcI7QgghhBBCCCE+m5QF3P3793PlyhWePn1Kjhw56NixI2ZmZpiYmKQK8NSqVYs1a9ZgZ2cnIR0hxH/K7t27adq0KU2aNCEoKAhHR0fgzUhk27dvZ9iwYdjZ2eHr65sqwPO2TxXcebsPfl+f/KEAz/vacenSJfbt20dwcDDPnj3j2LFjODg4yOg7QqSDp0+fMmvWLCZMmEC2bNl48uQJXl5e9O/fnzx58gCpr+G/C/AAPHv2jPbt23PlyhUiIyPJly9fuhzbl0z6uy+bXq9Hq9UqIdW5c+fi6uqKqakp69atU0bVeft5jI2NJTw8nB9++IG8efOmS9uFEEII8fWRiU6FEEIIIYQQQnwWOp1OKfiMGDGCWbNmERcXpyxfsGABw4YNo1GjRuTKlQsfHx9UKhVz5syhffv2hIWFYWtrm17NF0KIz+7BgwckJyfToUMHHB0dlcJ4jhw5aNeuHbGxsXh4eDB16lS0Wi0dOnTAyMhIKbAbiomfasQdQ1E+NDSU6tWrK2Gizp07k5iYSFhYGHXq1MHIyIiRI0fi7e0NoAR4UrZDr9dTrFgxChcujLm5Od26dWPhwoX4+flJIVuIz0yv12Nra8uYMWPYtm0bv/76Kzlz5qRZs2bkyZMnVV9i6F9mzJiBSqVi5syZdOjQgRUrVlCyZElln4mJiWzatInIyEi6dOkiwZ3/93bASaVSSYDnC/K+58cQ3IE3U8vpdDoGDRqEm5sbpqam1K1b953nMWvWrHTr1g34dAFaIYQQQmR88nVFIYQQQgghhBCfheGD8NGjRzNp0iRcXFw4evQoDx8+ZN68edy/fx9XV1e2bNmCTqfD3t6ekSNHMmDAAA4cOEC9evV49uxZOh+FEEKkPcNA2Xfu3EGv1/PHH38Ab4qKBpkzZ6ZJkyYULVqUM2fOMG3aNMLDw98J7nxq3t7edO7cmdDQULRaLW5uboSGhmJtba20s2bNmvj7+1OpUiW8vb2ZOHHiO/sxtM3IyIgGDRpgYWHBmTNn0Gq1n7zNQoi/plKp0Gq1nD17losXL/Ltt9/y5MkTBg4cyK1bt1L1JUZGRmg0GgCmT5/OkCFDOHfuHIMHD1YeBzAzMyMuLo7OnTuzaNEi4M++7b/McD/cpUsXQkNDgT8DPCJ9abVa5fnZs2cP8+bNo2/fvoSGhnL27FllPVdXV4KCgrh27RoDBgxg9+7dwIefRwnuCCGEEOJjybRZQgghhBBCCCE+mxMnTtC6dWsqVKjA5MmTKViwIACrVq1iwIABWFpacu7cOaytrZVvvj569AhPT092797Nr7/+So4cOdL5KIQQIm0ZgjeHDx+mfv36NG3alDVr1gB/foPf0EeOHz+elStXEh0djbW1Nf7+/ri4uKRZgOfw4cNMnjyZ3bt3880333DixAm8vLxwdXUlV65cqUYtiIyMZOTIke+dQiulmJgYypcvT44cOdixYwdZsmT5pG0WQny8c+fOYWZmxurVq/H396dMmTKsW7cOZ2dn4M/+KeVoIj4+PvTq1QsnJ6cP7lemPv3T4cOHqV69OgULFmTChAm0bt0akHOUnlKee29vb+bOnUtcXJwSSLO0tGTGjBl06dJF2Wb69Om4ublRqFAh5syZww8//JAubRdCCCFExiF3gkIIIYQQQgghPpubN28SFRVF586dKViwIBqNhjVr1uDt7Y2NjQ2//PIL1tbWJCcnK6Mv2NvbM2XKFC5evEiOHDlSjTwhhBBfu/f1aYbAjaOjI2XKlCE8PJyAgADgzTf4k5OTlSLj0aNHKVeuHCtXriQmJgZfX18iIiLQarVpMvJO1apVmTZtGra2tpw6dYrKlSvj4uJCrly5lLYbviv49gg8gYGB7+wvPj6ekJAQrly5QtWqVSW4I8Rn8qFRrsqUKUPRokUZOnQow4cP5/z587Ru3Zrbt28Df/ZPhw8fZvv27QCMHz8eJyenVCPvpPzOsF6vl1BKClWrVmXdunW8ePGCkSNHKuHM950jvV6f6lzK6GRpw3DufX19mTRpEo0bN2bz5s1s27YNLy8vXr16Rbdu3Zg+fbqyzZAhQ5QReNq1a8fBgwfTqfVCCCGEyCjkjlkIIYQQQgghRJp4X3Hh4sWL6PV6ypYtC0B4eDienp6oVCqOHz+Ora0tAJcuXWLQoEFKEcjOzo5s2bJJ8UcIkaGknKLj6tWrnDx5kp9//pno6GiSk5NxdHTE398fIyMjfHx88PX1BcDExASAjRs3cvfuXb777jvq1KnDnDlzeP36Nb6+vixfvvyTT8Ni2N+xY8d48eIFuXPn5tSpU+zcuZNHjx4Bbwr77wvwVKtWDU9PT+bMmZNqn3FxcVy6dIl27drh7++f6vcIIT49jUaTatScNWvW4OXlhY+PD2vXrlXWs7a2xtPTEw8PD86fP0+rVq24desWWq2WHTt2MGDAAMaPH09cXJxyzRobGyvbpwwPpkWQ8GtluLdt2bIl8+bN486dO3h4ePDw4cMPbqNSqThz5gzPnz9XRl4Tn97Zs2eZN28erVu3ZsKECTRo0ICGDRsSEBDAqlWryJUrF25ubqmukyFDhjB+/Hg0Gg2FChVKx9YLIYQQIiOQabOEEEIIIYQQQnxyKadqmThxIg4ODnTp0oWQkBC6du1KYGAg+fPnZ8iQIajVak6ePImdnZ2yfadOndi8eTNnzpyhQIEC6XUYQgiRZlJO0TF+/HhCQkK4ffu2UgBs0aIFPj4+WFhYsG3bNtq3b09sbCyVKlWiWLFixMTEsHv3bszNzTl27Bi5cuUiOTmZrVu30rp1a9zc3JgyZconbyvArVu3OHXqFJaWlsydO5ddu3YxYsQI+vbti729PUCqcADAjh07WLBgATNmzHhnap379++TJ0+e9/4uIcS/t3fvXhYvXkxoaGiq63LYsGEEBQWlWvenn34iJCRE+fnVq1dMnjyZqVOnkidPHooVK8bZs2dJTEzkxIkT5MuX77Mdx9fm7f7s7X4RICwsDHNzcxo3bvzB/Rw7dowqVapQoUIFdu/ejYWFRZq1+b9s/fr1tGnThrVr19K6dWt0Oh06nU4JpRnexxQrVozt27eTJ08e5fmMiYnB3Nz8vc+xEEIIIcTHMv77VYQQQgghhBBCiH/GENzx9fXF39+fXr160a5dOypVqoSFhQWTJ0/G2NgYU1NTfv31V6ysrJRtlyxZwv79+/nxxx+VYq4QQmQ0hoKup6cngYGB/PDDD/Tv3x9zc3OmT59OYGAghw4dYteuXTRu3Jg9e/YQFBTE8ePHOXbsGHZ2dpQqVYqQkBBy5cqFVqvFxMSEJk2acPLkSb777rtP0s6Uhcg9e/bw6tUratasSdu2bQGwtbVFo9EwYcIEVCoVffr0wd7eHiMjI/R6PWfOnOHbb7+lYcOG1K5dGzMzMzQaTaoROgx9vYyuJsSnpdfr0Wq1jBs3joMHD2JsbMzSpUsxNjZm8eLFLFiwgJ49e+Li4oKxsTEeHh6Ehoby4sULNm/eDIClpSXDhw8ne/bsLF26lKNHj1KqVCmWLVuGk5OThBX+gqE/i4yMpGbNmsp58vT0JCkpiWnTptGuXTtl/ZTh95RsbGwoUKAAJ0+eZNeuXbRp00aCjmng5cuXwJsR4eDN86dWq5Vz3blzZ3bu3Mm2bdt49eqV8jqnUqnImjUrer1ergUhhBBC/CtydyeEEEIIIYQQ4pNJOVXWvXv32LFjBz179sTLy4tMmTJRqFAhJk+eTGxsLFFRUYwdOzZVcGf58uVMmjQJa2trRo0ahZmZmUyfIoTIsDZs2MDs2bPp2bMnc+fOZciQIUqfCRAdHa1Mj1KxYkUWLlyoTK118OBBNm/ejKOjY6riuYmJiRLc+bdTq6Tc75gxY2jfvj1Dhw7lxo0bytQv3333HePGjaNOnToEBAQwf/58njx5AsDOnTtp0KABvXv3BsDU1BRIPbVOSjK1jhCflkqlwtjYmLCwMGrXrs3KlSv56aefAIiKiqJEiRJ4enpSt25datWqxcaNG2nRogVbt26lWbNmyn4sLS1xdXVl3759HDhwgM2bN0tw5yN17dqV2rVrs27dOgAGDhxIYGAglpaWSljE4EN9YJEiRQgNDcXW1pZNmzYBSHDnX/jQa2OuXLkA2Lx5szIVJLw518nJyQAULlyY169fc+nSJeDP58wwZaQQQgghxL8hI+8IIYQQQgghhPhkDAWcLVu2oFKp+P3335kyZQrOzs7KOk2bNiUqKoqJEyfi5eXFgQMHKFWqFJGRkURGRmJlZcWuXbtwcHCQopAQ4qv3oZEU4M1UKCqViv79+1OwYEG0Wi1r1qzBz88PZ2dnTpw4gZWVFfHx8ZiammJlZYWVlRU5c+ZU9qHT6T7YT/6b4m7KEQSGDx/OtGnT6NixI927d6d8+fLK71ar1Xz77beMGTMGlUrFhAkTuHPnDtmzZ2f79u0YGRnh4eEBSDhHiPSg1WrJmTMnq1atwsXFhfDwcDQaDc+fP6d169YUKFBAGaEnT548zJo1C4CNGzfSrFkzZQQelUqFra0ttra2ADLKyEeqXr06J06c4Mcff2Tx4sXs2rULd3d3evfunSrA/ndKlixJjx49WL58Ob/99hulSpVKw1ZnXCnfWxw5coS4uDjq1q0LQJ06dWjSpAnbt2+nQYMG/Pjjj2TKlAm9Xo+JiQnwZppHOzs7SpQokW7HIIQQQoiMS8I7QgghhBBCCCE+qXXr1uHi4kKpUqVwdnamYsWKwJ8flufKlQtXV1eKFCmCl5cXK1euJDExkXz58tGsWTMCAgLInTu3BHeEEF+9ffv2ERoayrx58zAzM0u1LCkpiV9++QV7e3u++eYbkpKS2LBhA97e3qjVak6ePEn27NkBuHPnDhcvXqRly5bvBHLSavQFQ9Bm/vz5zJgxgwEDBjBkyJBUYUy1Wq2Ek8qXL8+4ceOwsbFh+fLlGBsbU6RIEY4fP46zs/M7U2UJIT4PIyMjtFotOXLkYO3atbi4uLB582YyZcpEtWrVANBoNJiYmKDT6cidO3eqAE/Lli3ZsGHDO9evhPH+mqFv7N69O3ny5KF9+/b8/PPPNGnSBE9PTyUE9bGyZs1KkyZNuH//vgRH/kcpw65jx45l8eLF3L9/n9OnT1O2bFmMjY3p1KkT58+fx93dnZiYGJo0aUKBAgWAN6Pl7dixgzJlypA3b970PBQhhBBCZFAqvYw/LoQQQgghhBDiE/r999+ZPn06ERERvHjxglWrVtG+fXvg3REoHj9+zIsXL4iKiqJ06dJkyZKFTJkySXBHCPHVi4+Pp0uXLkRERNCjRw9mz579ToCnZcuWnDx5khs3brB9+3aGDh2qBHfs7OyAN/1mxYoVcXJyIiQkhMyZM3+W9uv1euLi4mjbti1Xrlxh586dFCxYUFm+atUqTpw4QXx8PM2aNaNJkybKsj179gBQrlw5bGxspE8XIp0YRsdK6fHjx7i4uHD48GHKly/P0aNHlYCPkZGRss2DBw8YMmQI69ato0uXLixdujSdjuLrZTiXU6ZMYfjw4WTLlo1Xr14RHh5Oy5Yt/3Jkto/dt/g4Kc+1h4cH06dPp2PHjnTt2pVatWqlWr5w4UKmTJnC3bt3KVq0KA0aNODmzZscOXIElUrF0aNHcXR0/FfPnxBCCCHE+0h4RwghhBBCCCHEJ3fp0iVmz57N4sWLqVOnDrNmzVK+tWr4oPtDH3jLB+FCiIzi4sWL+Pv7s2bNGrp06cL8+fMxMzNTiq6zZs1i8ODBNG/enLNnz6JWqzly5AgODg7KPmbMmMGECRNwd3fH3d39sxZro6OjKV++PI6Ojhw6dAiAQ4cOMXfuXMLCwjA2Nkaj0QCwfv16WrRo8c4+pMAsRPo7ffo03333nfLzkydPaNeuHZGRkbRq1YqwsLD3Bnju3r3L2LFj8fHxSTXqlvhnrl+/zo4dOzA2Nmb69OncunWL5cuX06FDB2Wdt+9/pe9MGytXrqRHjx707NkTd3d38uXLpyxLec537drF2rVrWbJkCQB2dnZ8//33zJ49m7x580ooVQghhBBpQsI7QgghhBBCCCHSxJUrV5g6dSqLFy+mU6dOTJgwgVy5cqV3s4QQ4rO6dOkSfn5+RERE0LlzZxYsWKCMwHPp0iUaNGjAvXv3sLW15d69e8oyvV5PREQE3t7e2NnZsWnTJmU0ns8lPj6eWrVqcfLkSXr16sWLFy/Yt28fOp0ONzc3GjZsyG+//Ua3bt3o2LEjixcvxszMTAKYQqSzlEGQMWPGMH78eGbPnk2fPn2UdZ48eYKLiwsHDx6kXbt2hIaGvjfAY/hXpr77OH8XugkPD8fHx4fbt28TEhJC+/btU21z+fJlihYt+rma+59huCY6d+7Mzp07OXjw4HvP89vP39WrV4mPj8fW1hYbGxsyZ84swR0hhBBCpBm52xZCCCGEEEIIkSaKFCnCsGHD0Ov1BAcHA0iARwjxn2EodBcrVozJkyej1+sJCQnBzMyM6dOnkzlzZooVK8ayZcuoX78+T58+ZfTo0TRq1AhbW1sWL17M2rVr0el0rFmzBjs7uzQbieFDI55lzpyZkJAQmjRpwqJFi7Czs6NixYpMnz6dfPnyYWRkRNmyZRk4cCBGRkZkypTpk7dNCPHPpAwWJCUlUahQIQDmz5+PSqWid+/ewJuRRMLDw2nbti1hYWEASoAnZVDH0OdIcOfvpTz3v/zyC0+ePMHOzo58+fJhY2MDQNu2bQHw8fGhc+fO6HQ6OnbsCMD27dsZN24cDRo0wM/PL30OIgN7+fIlu3btIn/+/BQtWvS9gTTDa6HhdbFw4cKpluv1egnuCCGEECLNyB23EEIIIYQQQoh/5J8UjwsXLoyHhweABHiEEP8ZWq1WKQguWbKEx48fc+HCBSwsLFi0aBHGxsZMmTKFzJkzU6tWLfbu3Uu/fv2YNGkSkyZNAsDMzIxKlSqxbNkyHB0d0+yb/in3++zZM54+fYpOp6NIkSJK4fL48eP88ssv5M6dm/z58yshHb1ez9y5c9FoNJQvX/6Tt00I8c/odDrleg4MDGTnzp3ExMSgVqs5d+4ckyZNQq1W07NnTwBy5MhBeHg4Li4uytRZy5cvl6DO/yBlqMPb25vp06eTkJCAqakpFSpUYNGiRRQpUgT4M8AzatQounXrxr1790hOTmblypU8ffqUrl27ptdhZFgqlQorKyty587NixcvSExMxMzMLFV41fAe5/Hjx/z888906tTpvfsRQgghhEgrMm2WEEIIIYQQQoiPlvID7nv37pE3b96P2u7q1asEBgayfPlyGjduzLx587C3t0/LpgohRLobNmwY8+fPp0KFCpQsWRIjIyMWLlxIfHw83bp1Y/bs2WTOnBmAu3fvcvnyZX755RcyZcpEhQoVKFWqFJaWlmkW3EkZxpwyZQqrV6/m7NmzAJQtW5YBAwZQt27dD/b1q1atYty4cWTNmpUdO3Z89mm9hBDvN2LECIKCgmjXrh0uLi4kJCRw8OBBli5dirW1NWPGjKFHjx7K+tHR0XTo0IH9+/fTr18/5syZk46t/7oFBAQwatQofvjhB6pXr87JkyfZsmUL2bNnZ//+/ZQsWVJZd/369UyZMoXjx4+jUqkoUqQI27ZtI1++fDI10yem1+vRarW0atWKrVu3EhAQgLu7OyYmJuh0OlQqlfIex9XVlWXLlnHu3DkKFCiQzi0XQgghxH+JhHeEEEIIIYQQQvxjgwYN4tdff2Xr1q1YWFh81LdQr127ho+PD0eOHOHcuXNkz579M7RUCCHSx4oVK+jSpQv9+vXD09MTR0dHAE6cOIGfnx8///wzPXr0YObMmUqA530+x1RZ7u7uzJgxgzJlylC1alV+++03zp8/T0JCAj/99BNeXl44Ozsr2z569Ihp06axatUqjI2NiYyMxMnJKc3aKoT4eHv37qVx48Y0btyYoKAgnJycAHj9+jW7du2iR48eWFlZ4evrq4zAA2+ua1dXV6ZMmZLqehd/7e2QTeXKlcmXLx8TJkxQ+v2xY8cyZswYLC0tOXToUKoAz5UrVzh37hwxMTE0bdoUOzs7Ce78C3937s6dO8cPP/xA9uzZGTNmDK1bt0410lR4eDijRo2iVKlSLF++nKxZs36OZgshhBBCADJtlhBCCCGEEEKI/8Hly5f5/fffef36NZaWlqmKwB9SqFAhJkyYgLW1NTY2NlLkFUJkaOfOncPExIQff/wRR0dH9Ho9er2eihUrMmXKFPr160dwcDCmpqbKFFopv/1v6FfTqp809NkrVqxg5syZuLq6MmjQIPLnz09MTAyRkZFMnTqVhQsXYmZmho+PD3Z2djx69AgXFxeOHDlCkyZNmDt3Lnny5JFisxCf0Z07d5RQztsePHhAUlISP/30E05OTsq1aWFhQZs2bYiNjaVfv35MmTIFrVZLnz59ALC3t2ft2rWoVCo0Go1MnfWRUk5TVqBAAZKTk+nVqxeOjo4kJydjYmKCr68vpqamjBw5kmrVqqUK8BQpUkSZTgtST30m/pmUr0MRERFcvnwZtVpNxYoVqVOnDgAFCxbEz88PHx8fhg0bxsmTJ+nduzcmJiaEh4cTHByMVqtl+vTpZM2a9aPe4wghhBBCfCryKakQQgghhBBCiH+sffv2PHv2jHHjxinF5o+RP39+Ce4IIf4Trl+/jqmpKfny5QNSj6BTsmRJAgICAJg3bx6DBg0iISEBtVqt9Kefq1i4b98+rKysGDBgAPnz50er1WJubk6jRo2YPXs2FStWZOXKlZw/fx4AW1tbpkyZwtq1a1mxYoUEd4T4zM6fP0++fPkYOHBgqsd1Oh0A9+/fB95Mbwq8c7/VoEEDKleuzNWrV5kxYwbLli1TlhkG6Zfgzj+ze/duPD09GTRoEA8fPsTKygq9Xo+RkZHyvHh5eREQEMCrV6+UEc7gTeAkJbk//t8ZXoc8PDxo27Ytvr6++Pj40KJFCwYPHgxA1qxZadu2LUFBQeh0OqZNm0b58uUpWbIkfn5+mJubs2/fPuW1TYI7QgghhPic5E5QCCGEEEIIIcQ/5uLiQpEiRTh06BBPnz4F/iz4fAwpTAghMrqiRYsSGxvL6tWrgTdFRUM/qdFoqFatGvXq1aNkyZIEBwczYsSIz9o+nU5HXFwchw8fxtzcnDx58qQa8UGtVlO0aFH69OnDs2fPmDlzJvCmqF+xYkVat26NlZWVjBIhxGf2+vVr8ufPz927d0lISFAeN9xb1alTh0yZMnH06FHgTRDQECDR6/XkzJmT2rVrU7BgQe7cucOkSZM4cOBAqn2If6Zu3bp4enry9OlToqKiuHHjhhL6UKvVyvn39PQkICCAhIQEypQpw6VLl6T//ARSvgeZP38+c+bMoWvXrkRERLB+/XpsbW2ZNWsWnTt3BiBHjhx069aN48eP4+3tjYuLCz/++CPz5s1j9+7dODs7SyhVCCGEEOlC7saFEEIIIYQQQrzX298ENhQeNBoNFhYWDBgwgEuXLhEWFgZ8vlEihBDiS2ToIw2aN2+OSqVi8eLF7N+/H3jTTyYlJSmjWty9e5dvv/2Wvn37MmTIkM/aXrVaTZYsWShdujQvXrzg4cOHqNXqVH2/kZERzZo1I0+ePNy9e5eYmJj37kcI8flUqVKFbdu2sXLlSjJlysSePXtSLXdycqJatWqEh4czadIk4M11mnL6n19++YVatWqxcuVKbt++zdq1az/7cWQUycnJAEyYMAFPT08AevbsycmTJ5Xz/naAZ/jw4eTJkwcLC4t0a/fXzhDYeXsE0Hv37vH999/j4+NDq1ataNGiBUePHqVSpUqEhobSqVMn4M3rcd68eRk/fjzBwcEsWrSI7t27Y2trK6FUIYQQQqQbeXcthBBCCCGEEAKAuLi4VD8bPrQ+deoUcXFxSoHWUHSuUqUKlpaWLFu2jAcPHnzexgohRDp7O6zzdoCxUqVKjB07lqtXrzJp0iR27NgBgKmpKQBr1qwhMTGRfv36MXfuXJycnNBoNGnS1veNjKbVatHr9ZQuXZpXr14xbNgw4uPjMTIyQqvVKsdnbW2NqakpVlZWZMqUKU3aJ4T4axcvXuT3339Xfi5SpAjm5uYsXLiQevXq4evrqyyzt7fH3d2dbNmyMWLECMaMGcOLFy+UPmrTpk1cvnwZR0dHKlWqRPHixVm6dClXr1797Mf1tXm73wcwMTFR/j9mzBhGjRrFq1ev6NixI6dOnUKlUr0T4Bk7diwXL15UpmYSH88QIk05shHAkCFDaN++PVu3bqVJkybkz58feBOucnBwYO3atVSqVImVK1cqI/AAJCUlKf83vFZKKFUIIYQQ6UXuQoQQQgghhBBCcPDgQcqWLcv58+dTPT5u3DgqVqxIrVq12LhxI9evX1eWlS1blj59+vDbb79x7do14J9NnSWEEF8rrVarFPfWr1/PiBEjaNSoEXPnzuXXX39V1uvQoQMDBgxgz549dO7cGQ8PD7Zt24aPjw8jR47E1NSUfPnyKesbwpGfuq2GImd8fLzyuJGRESqVCjc3N0qVKsWmTZsYPnw4cXFxGBkZKce3evVqHj58SMWKFaWgKUQ6uHLlCqVKlcLd3Z1Lly6lWla4cGGqVq3K+PHjGT16tPJ4vXr1WLJkCfb29owZM4Z69erRvXt32rVrR48ePXj16hVdunQhZ86cFC9eXOkPxIel7PcjIiLw9vamVatWDBw4kGPHjinhyzFjxuDt7c3Nmzdp3779BwM8FhYW6PV6GeHlHzh8+DCtWrXi4MGDqR5PSEhg1apVhIeH8+TJEzJnzgy8Ce6YmJig1WrJlSuXEuAJDQ2lW7duwJ+BWpBRRIUQQgiR/lR6+WRVCCGEEEIIIf7T9Ho9EyZMwMfHh2LFihEeHk6JEiVISkoiMjKSefPmcezYMaKjo8mfPz/9+vWjUaNGFCtWjDNnzvD9999TuXJlNm/ejKWlZXofjhBCpCmdTqcUcD09PZk1axZqtRpzc3Oio6MpVaoUw4YNU6bmuH//Pps3b8bT05PY2FhlP8WLF2fr1q04Ozun2mdatXX27Nns2bOHLFmyULJkSTw9PZWi8ZUrV2jSpAk3btygZs2aeHh4kDNnTvbu3cvixYtJSkri6NGjODg4fPI2CiH+WnJyMl27dmX16tW0atWKsWPHUrx4cWX50aNH8fb25uDBg/j6+qYK8Rw4cICVK1eydetWHj16hK2tLcWLF2fZsmU4Oztz8+ZN6tevj7m5OTt27MDe3j4djvDLl7Iv9fDwYPbs2ajVaqytrYmOjsbIyIjRo0fTqlUrChcuDICvry/jx48nX758hIeH8+2336aaukz8MxqNhsmTJ+Pj48Po0aPx8fFJ9br54sULatasyfnz56lWrRq7d+/G1NRUee60Wi1GRkZERUXRvn17Dh06xMCBA5kxY0Y6HpUQQgghRGoS3hFCCCGEEEIIQXx8PDNmzGDcuHHkzp2bDRs2UKJECWX5pUuXWLduHcuWLePmzZvY29vTsGFDPDw86NGjBw8ePGDjxo188803aVaEFkKIL4mfnx/+/v78+OOP9O3bl0qVKrF48WL69OmDk5MTPj4+dO/eXVn/ypUrXL16lTt37lCoUCG+++47smfPrhQU05KnpyeBgYFkypQJrVZLcnIy33//PREREeTKlQuAO3fu0LFjR44dO6ZsZ2pqStGiRdm4cSPOzs6fpa1CiHclJyfTr18/lixZ8t4Az5EjRxg5cuR7Azzx8fE8e/aMK1euYG9vj4ODA9myZePhw4fMnTuXgICAd7YR7zdhwgRGjhxJ37596dGjB99++y3btm1j1KhR/Prrr0yZMoWBAwcqU2kZAjwWFhYcPHiQMmXKpPMRfN2ioqI4duwYtWvXxtramvv375MnTx5l+cuXL6lRowbnz5+nb9++BAUFKa97hikhjYyMuH//Pq6ursyYMQMnJ6d0PCIhhBBCiNQkvCOEEEIIIYQQ/3GGsE1CQgJBQUH4+/uTK1cuNmzYQMmSJVOte+PGDS5cuMCkSZM4efIk2bNnx9jYmKioKPn2qhDiP2PTpk24urrSqFEj3N3dKVy4MC9fvqR69ercvXuXly9fkj17dgIDA+natesH9/M5wo7Hjx/HxcWFVq1a0adPH+zs7Bg5ciSLFy+mZMmSrF+/noIFCwJvivwbN27k4sWLxMbG8s0339C4cWNsbW0luCNEOvtfAzzv62fOnz/P7NmzWbFiBc2bN2fNmjUAMjLMX7hx4wYNGzYkX758zJkzR+k3IyIicHV1xcTEhF9//fWdUKabmxurVq3izJkzSlhS/Hs+Pj7s2LGDuXPnUrFiReXxly9fUqVKFX7//XcGDBiQKriaMsBjuC40Gk2aTFkphBBCCPG/kPCOEEIIIYQQQvzHpSwwxMfHM3XqVCZMmJAqwPN20TYmJobffvuNxYsXs2fPHu7fv4+trS07d+6kbNmy6XUoQgiR5hISEvD09CQ8PJxt27ZRrlw5YmJiqFChAs+fP2fRokVER0fTs2dPcuXKxZgxY+jRowfwecI6huK74d81a9YwYMAADh48qIyo9vr1awIDA5k0aRKFCxdmw4YNSiH6fWRENSG+DElJSQwYMIDg4OB/PAKPwblz5+jbty+///47HTt2ZN68eYBc5/BmmsMsWbJgY2PzzrJDhw5Ro0YNgoOD6datG8nJyURERDBixAhUKhWnTp3C1taWxMREEhISsLKyUrZ9+fIlVlZWEoL8RGJjY/H392fKlCnUrl2bsWPHUqFCBWX5ixcvqFKlCpcuXXpvgEcIIYQQ4kv1374bF0IIIYQQQoj/GJ1O987/DR9iX7x4kcyZM+Pu7s7IkSN5+PAhLVu25MKFC6k+6NbpdJibm1OpUiWCg4PZsGED48eP5+nTp5w8efLzHpAQQnxmarUaZ2dnpk2bRrly5UhISKBZs2Y8ffoUf39/6tWrR/fu3alfvz5RUVEEBAQoxfG0LoxrtVpl1Iznz5+TlJREXFwcLVq0UII7Go0GCwsLhg8fjpeXF1evXqVly5Zcv34dSP06YfjO33+9oC9Eekh5LWq1WuDNVHZz5syhe/furF+/Hl9fX37//XdlvSpVquDv70+NGjUYO3Ysw4YNe2e/hQoVYtiwYaxatUqCOymcPXuW4sWLM2nSJGJiYt5Z/vr1awAcHByANyPueHl5oVKpOHnyJLa2tgA8efKEKlWqcO3aNWVbKysr9Hq9BEc+kaxZszJkyBDGjh3LgQMH8PHxSfUexNramiNHjlCsWDHmzJmDl5cX8fHxcv6FEEII8cX7b9+RCyGEEEIIIcR/jKEw07dvXzZv3qw83qtXL0qVKsWlS5fInDkzbm5u7wR43t6HoahUrlw5WrZsiYODA9OnTyc6OvozHpEQQnxepqam9OrVixYtWgAQGhrKkSNH6NmzJ+3bt8fU1BSA/Pnzkz9/fm7dusXs2bOJi4tL03bpdDqlMDl16lSaNm3KDz/8QFBQEPv37+fOnTsAGBsbKyFMDw8PJcDTtm1brly5kqqAL9PnCJE+tFqtci3u3r2bsLAw7t69C7zpg+bNm/e3AZ5SpUoREhLCq1evlGV6vZ4sWbLQunVrGjdurDz2Xw/uGGTNmpUHDx5gYmKiPGYIMWbNmhWA9evXs2zZMry8vFCr1Zw8eRI7Oztl/YkTJ3L37l1evHiRat/Sn35aOXLkoHv37owaNYpDhw59MMBTunRpZs6cSUBAQDq2VgghhBDi48hknkIIIYQQQgjxH7Njxw4WLlzI6dOnyZ07N8uXL2fp0qUMGDAAS0tLADJlyoSbmxsA/v7+tGzZUplCyyBloado0aLUqFGD3bt3k5CQ8HkPSAghPjNzc3Pl/4bRyYYPH06WLFmUx3///Xd69epFjRo1sLe3T7UsLRj6ZC8vLyZPnoy9vT0mJiY8fPiQzJkzs3v3bjp16oSZmRlqtTpVgEetVjNmzBgGDBjArl27ZHQCIdJRyiDe6NGjmT17NtmzZyc4OBhHR0cATExMlFFzlixZApBqCq3KlSsr61taWirT6L0vQCKhkje++eYbjh49iq2tLWZmZkRGRlKyZEllRJ0aNWpQv359li1bxsaNG8mSJQvnzp1T7p31ej2hoaFs2bKF5s2bK6OdibSTI0cOevbsCcC4cePw8fFh/PjxyhRa1tbW7N+/n9atWyvrCSGEEEJ8ySRSL4QQQgghhBD/MRUqVGDp0qXcu3ePJk2aMHfuXIYMGYKvry+5c+cG3hSODAGeD43Ak9Lr16+Ji4tDrVbz/Pnzz3k4QgiRLvR6PcnJyTx69IiEhAQOHDigLAsLC+PatWuYmZlRsWJFnJyc0Gg0adIOw3Q6ADdv3mTjxo0MHDiQI0eOcOfOHQIDA8mZMycjRoxg27ZtJCcnA6QK8Li5uTF58mSWLl0qwR0h0lHKUXCGDx+Ov78/jRo1Ijg4mKpVqyrr6XQ6TExM3plC69KlS8o63333HTly5ECn00lA5yOoVCry5cuHhYUFs2fPpnbt2ixatCjVfe2gQYMoVqwYT58+pV+/fkpwB2DRokWMHTuWTJkyMWnSJLJkyaKM2iPSjiHA86EReLJly8bevXvT9HVYCCGEEOJTUenlDlIIIYQQQggh/pPq1KnD/v37yZEjB0FBQXTs2BF4UxAyFHXVajUJCQkEBQXh7++Pk5MTq1at4ptvvlH2k5yczObNm3FxcaFbt24EBwen0xEJIcS/Z+j7DAwjVnzI7t27qV+/PmXKlKFDhw5ER0ezdu1aTE1NOXToEPb29p+j2Rw8eJDbt28zcOBA9u/fT7ly5QDQaDRERETg5+fHs2fPmD9/Ps2aNVOmhdFqtakCO2//LIT4/BYuXMigQYPo168fgwcPxtnZOdXylNdpYmIirq6uBAcHU6tWLebNm0fhwoXTodUZx8mTJ+nfvz83btzAy8uLnj17kj17dmJjY1m1ahVTpkzh7t27fPPNN1SsWJFff/2Vs2fPkiNHDnbv3o2zs7P0pf/C/3LuoqOjWbx4MePGjaNmzZr4+PhQpUqVNGqhEEIIIUTakPCOEEIIIYQQQvwHnT17lh49emBnZ8epU6dwdnZm4sSJ1KpVSynowp8fnickJDB9+nS8vb2pXLkykZGRGBv/ORNzeHg4Z86cYeLEicDfF7uFEOJLlLLvunz5MkWLFv3b9QHmzZuHm5sbSUlJmJmZUbp0acLDw3FycnonDPS/ens/Go1G6YeXLl1Kjx49aNiwIRqNhl27dgFvwpUmJibodDrWrl2Lr6/vewM8QojP60PXs16vJy4ujg4dOnD+/Hl27dpFkSJFlPVCQkI4ceIE0dHR9OrVi3r16gGQlJRE586d2bt3L5cuXVKmehJ/L+VzYXgN0Gq1nD9/nj59+nD58mVGjhxJjx49sLW1JT4+nl9//ZU5c+awbds2Xr16RcmSJalVqxaenp44ODhIcOcTmT59OjVr1kz1pYG/Eh0dzZIlS/D29qZdu3aEhITI65wQQgghvioS3hFCCCGEEEKI/6hz585hbm7OkSNHcHNzI0+ePAQGBlKrVi2MjY2VwoOhqBEbG8vixYtp1aoVefPmTbWvlAXvT1WoFkKI9OLp6cnRo0fZs2cPJiYmH9Wn/fbbb5w/fx4bGxsqVqyIjY1NmhRwf/rpJyZNmkTu3LmVgv+JEyfw9/dn69atqNVqDh8+zPfffw+Qqi83BHhev35NYGAg7dq1SxXEFEJ8XuPGjWPUqFHAn9fq8+fPqVSpEtbW1hw/fhyAAwcOMG/ePMLDw8mSJQtxcXEAbN26lUaNGgFvAkCvXr3CxsZG7sU+Uso+et++fdy+fZv69esr08iePXtWCfB4e3vTs2fPVMGoJ0+ekJCQQN68eZX+WII7n8bmzZtp0aIFCxcupGfPnh99Xh89esTatWtp0aLFO+9XhBBCCCG+dBLeEUIIIYQQQogM7u0Pu98eFefly5eEh4czYsQI8uTJw+TJk6lRowZmZmbodDp27NiBubk5NWrU+OA+hRAio9BoNPz444+sX7+eX375hdKlS//tNu8bbSwtiucbNmygdevW5MuXjyNHjqSakuv06dMEBQWxZs0a+vfvj5eXF3ny5AFSB3jWrVtH3759yZ07NydOnCBz5syftI1CiI8zc+ZMhgwZgouLC2FhYcCb/icuLo6mTZty6NAhunTpwuvXr4mMjATAzc2Nhg0bcvbsWXr27EnDhg0JDw8nc+bMSn8jwZ2Pk/I8jR49mvnz55M1a1aCgoJo1qyZ0qe/HeDp3bs3NjY2yj5UKhUqlUpGnfzEHj58SOXKlcmdOzeHDh36R3/TKUdQkvcrQgghhPiayF28EEIIIYQQQmQwOp1O+X/KD62XL19Ov3796NmzJ4sXL1bWsbKyom3btkycOJH79+8zfPhw9u/fT3x8PLt376Z///60bt2a+Ph4Zd/yQbgQIqMyNjamSZMmaLVagoKCiI+P/9tt3lewTYviebNmzZgxYwZ//PEH1apV4+HDh8qy7777Dnd3d5o2bcrChQtZtGgRUVFRwJs+W6vVolaradWqFUuXLmXXrl0S3BEiHbVq1Yr+/fuzdu1a2rdvD7zpfywtLVm4cCFFihRh1apVHD58mCpVqnDixAm8vLwoW7Ys3bt3J1u2bNjY2JA1a9ZU/Y0Edz6O4Tx5enoyfvx46taty8qVK2nevHmqPr1s2bIsWLCAokWLEhAQwKJFi3j+/LmyD8O6Etz5tLJnz06lSpU4duwYa9euBf6cqvLvGJ4Leb8ihBBCiK+NjLwjhBBCCCGEEBnIvn37mD9/PkuWLMHc3Fx5fNiwYQQFBaVa96effmL8+PE4OjoCb0bgiYiIwNvbGxMTEwoWLMj169dRq9Xs37+fAgUKfNZjEUKI9FS9enUePnzIyZMnsbGxSfdRFQyjROh0OubOncugQYPImTMnt2/fxtjYWClSnjlzBj8/P3bt2oWXlxf9+vXDwcEBeHfUNBmVQIj0YehPoqKiCAgIYM6cOTRr1oyNGzcq0y89evSIGzdukC1bNgoUKICZmRnwpi9YsGABbm5u+Pv74+bmlu7909cqIiKCbt268eOPP+Lt7a3cE7+PYQSe69ev4+rqipubG9bW1p+vsRnU+/52DY9dvHiRypUr07JlS5YtW5Y+DRRCCCGE+Iwkhi+EEEIIIYQQGURiYiIhISFERETQq1cvYmJiAAgLC2PRokX07t2bX375hQMHDtCmTRtCQ0MZPHgwN2/eBN6MwOPi4sLChQuxtLTkypUrFClShMOHD1OgQAE0Gk16Hp4QQvwrb39/TavVvnc9w+Pt2rXj5s2bSvAxvQvjhuCOWq2mX79+TJkyhYULF2JmZpYqgFOuXDnGjBlD/fr1mThxIvPmzePRo0fAu6MQSHBHiPRhmGbJwcEBLy8vevTowU8//QS8GX1Hp9Nhb29PlSpVKF68uBLcAVizZg2zZ8+mWLFiyjbp3T99yf7qu8uHDx9Gp9PRs2fPvwzuAHzzzTcsWLAAGxsbwsLCpP/8BLRarfK3a3ifYZiKTKvVkjt3bqpXr05ISAj79u1Lz6YKIYQQQnwWMvKOEEIIIYQQQmQg169fZ8KECSxdupRWrVoRHh7O6NGj2bRpE+vWraNgwYIA3Lx5k1mzZjFr1iyaNm3K1KlTyZ8/v7KfxMREHj9+jK2tLVmyZJHRGYQQX73Xr19jYWGhTB9lKBieOnWKPHnyKKPTGNy7d4/vv/8eBwcHtm/fTo4cOb6I0S0MAZ6UDO1K2T7DCDz79u2jb9++eHp6kiNHjvRoshDiAwzXbGJiYqqAzvvWe/jwIdOnT2fNmjUYGRlx4MABnJyc3tsniDcOHz7M2bNn6datW6oRKQESEhKoUaMGL1684MqVK8C7o8AYzm1SUhKmpqZotVouXbqEra0t9vb2X8RrwtfkxIkTZM2alZIlS6Z6PCAggOjoaDw8PMiZMyfGxsbKsrCwMDp06ICXlxcBAQHynkQIIYQQGZrc1QshhBBCCCFEBlKwYEF8fHzo1KkT69evp127dty4cYMOHTpQsGBBtFoter2e/PnzM2TIEAYNGsSWLVtwd3dXRuABMDMzw9HRkSxZsqDX6+VDciHEV+3AgQPY29tz7NgxjIyMlJEYJk+eTMWKFalbty4hISFcu3ZN2SZv3rwMGDCAM2fOEBkZCXwZo1u8r0hvaJchwANvRuAZO3Ys5cqVY/369X8ZDBBCpA/Dtft31+fz589p3LgxU6dO5bvvvuPw4cM4OTkpYUTxrtjYWNzd3Rk8eDCHDh16Z7ler0etVnPnzh1Onz4N8N7gzuvXr/Hx8SE6OhojIyNKliyJvb29MkKM+Dh3796lTp061KlTh4sXLyqPnzt3jilTpjBz5ky+/fZb+vXrx65du5Tl7dq1o0qVKgQHB/Pw4UN5TyKEEEKIDE3u7IUQQgghhBAig8mXLx+jR4+mU6dObNq0idWrV3PlyhU0Gk2qD7ydnJwYPHiwEuAZPnw4169ff2d/UpgQQnztzp49S3x8PA0aNODkyZOo1WoSEhL45ptv6N27N3/88Qddu3alQYMGSpgxMTGRli1bYmRkxMyZM3n8+HF6H8ZHSRngKVu2LPPmzePkyZNYWVn95fQxQogvl42NDcuXL2ft2rUsW7aMPHnyyAgkf+HMmTOYmZkxYcIEvLy8qFSpEgDJycnKOpkzZ6Zly5YkJyenCovAmymcDKGocePGsWHDBh48eJBqHQlN/TM5cuTAzc2NxMREWrRowYULFwAoVaoU9+/fZ9asWVSqVIng4GAaNmxImzZtmD9/Pnq9ngYNGvDkyROWLVuGTqeT1zIhhBBCZFgybZYQQgghhBBCZACGAk7K4ftv3LjBuHHjWL9+PcWLFyc8PBxHR8d3tr1z5w6zZs0iKCiIbt26sXDhQikGCSEynJkzZ+Lh4YGRkRH79++nYsWKyrLffvuN48ePExQUxJUrV7Czs6NatWr4+/vj7e3N4cOH2b17N6VLl/5qpqj50PQvQoivj1zPH2/37t3Ur19fmWZJo9FgbGzMyJEjsbS0ZODAgWTJkgV4M61Wz549uXHjBsuXL6d169apRkFav349np6eFC1alFWrVmFhYZFeh/VVM/y9JiQkEBQUhL+/P7ly5WLDhg3KFFqGv/EtW7awc+dOwsPD+eOPP6hatSo1atRgwoQJ1K5dm127dr0zTaQQQgghREYh4R0hhBBCCCGEyECOHDlClSpVlJ9v3LjB2LFjWbFiBS1btiQkJISsWbO+s93NmzdZsWIF3bp1e2/ARwghvlYpR6eYPn06np6eGBkZERkZSYUKFVKt++DBA86fP8/MmTPZs2cPKpUKS0tLnj17Rps2bVi9erWEG4UQ4gt15swZGjVqRKlSpRgxYgS1a9cG4Pbt29StW5fbt28zffp0unbtqtwPL1y4EC8vL169eoWrqys1atSgXLlyLF26lBUrVpCcnMyRI0fImzevBEb+BcNrcUJCAlOnTiUgICBVgOftQNqdO3dYsGABO3fu5Ndff1UeX7RoET169EiHIxBCCCGESHsS3hFCCCGEEEKIr1jKIsK4cePw8/Nj0qRJeHh4KOvcvHmTMWPGsGLFClq3bs3SpUsxNzd/Z1+GD81lGgYhREaTsig4Y8YMPD09UalUREZGUrFiRWWKlJSFQ8NoOwsWLCA+Ph4LCwt27NhBqVKlZNQLIYT4Anl7e7NgwQJCQkJo3Lgx8GbaxLJly7J//378/Pw4ceIEU6ZMoWvXrspIOsuXL2f+/PmcOHFC2Zdarebbb78lPDwcJycnuT/+B95+jUxOTsbExET5OTY2lunTp38wwGM414Z/Z8+ezdmzZ1m2bBmtW7cmPDxcglRCCCGEyJCM07sBQgghhBBCCCH+N28XERwcHMiRIwdjxozB1NSUwYMHA5A/f35Gjx4NwIoVKwDeG+AxfMguhQkhREaTshho6Bs9PT2pWbOmEuDRarXAn31r1apVqVq1Ki4uLuzatQsPDw/Wr19PqVKlPklwRwqPQmQccj2nv+TkZO7fv8+LFy+UUE6/fv04deoUISEh1KpVC61Wy+jRoxk2bBh6vZ6uXbtiaWlJly5dqF69OufOnePs2bMYGRlRvnx5KlasiI2NjQR3/gG9Xq+8Rv7yyy8UL16czJkzA+Dq6kqDBg1o0qQJ7u7uqNVqxo8fT4sWLdi4caMyhZbhXBv24+rqyosXL1Cr1QQHB3Pq1CnKly+fDkcnhBBCCJG25CtCQgghhBBCCPEV0ul0ygfb/v7+VKlShfnz55OYmEhcXBxDhw5l1qxZyvr58uVj9OjRdOrUiXXr1tGrVy9ev36dXs0XQog09/Zg00ZGRspjgwcPZuLEiej1emrWrMmJEycwMjJK1bca1i1ZsiRNmzbF2dmZFStWEBUV9UnaZij0P3r06F/vTwiRflJez3Fxcencmv+eY8eO8eTJE0xMTGjTpg16vZ7AwEBcXFxYsGABNWrUIFu2bAD88MMPjB49mvLly+Ph4cGyZcuU++F8+fLRokULxowZg6+vLw0bNsTGxibV64L4e4ZroXXr1jRp0oTjx48DMGzYMObOnUtkZCRxcXFkypQJNzc3fHx8iIqKokWLFly4cOGdfRlei62tralbty4Au3btAt59nRdCCCGE+NpJeEcIIYQQQgghvkKGb6J6e3szZswYChQowOTJkwkNDSUgIAB4U5yeMWOGso0hwNO1a1fCwsIYNmxYurRdCCHSmlarVQqI0dHRXLx4kZs3b/Lq1StlnSFDhjBhwoRUAR61Wo1OpwNINYpG4cKFadq0KQ8fPiQmJuZft8+w76ZNm9KyZUtu3br1r/cphEgfhuu5Xr16eHt7p+pnRNras2cPVapUYeTIkcTHx9OsWTNmzJjB9u3bWbduHR07dsTT0xMHBwc0Gg3wboBn6dKlxMbGKvt8OxAiUyT+c3FxcVStWhW9Xs/w4cNp0aIFQUFBeHt7M2jQILJkyYJOp8PMzEwJ8Dx69OiDAR7D63KlSpXIkiULf/zxh7JMCCGEECIjkTtPIYQQQgghhPhKnTx5kunTp9OoUSPGjx9P7dq1ady4MV5eXmzZsgULC4v3jsBj+ODc29s7HVsvhBBpI+UoCRMmTKBGjRqUKlWKggUL8s033xAWFsbTp08BGDp0KJMmTXonwGOYQsvg+fPnPHjwgCxZsnyywnxMTAyVKlXi4sWLDBo0iNu3b3+S/QohPr+oqCisrKyYM2cOU6ZMkQDPZ3D27Fk6d+5MnTp16NChgzI108WLF5XRkO7fv8/9+/cBMDY2/qgAjwRC/r0sWbLQt29fZs6cyW+//cbWrVtp06YNvXr1wtHRUZlaK2WAZ+TIkURFRdGyZct3AjxqtZrXr1+zbNky4uLicHJySqcjE0IIIYRIWxLeEUIIIYQQQogv2I0bNz5Y0L1//z4JCQm0a9cOR0dHtFqt8s3Uxo0bs3z5cuDNCDzTp09XtitYsCBTp07FyclJKWIIIURGYRglwcPDg5EjR+Lg4MCMGTPw9vbGwsKCnj17MmnSJO7duwe86SMNAZ66dety+PDhVFOkJCcns3fvXrZs2ULjxo359ttvP0k7zc3N6d+/P/7+/hw6dIgePXpw8+bND65vGA0iOTn5k/x+IcSn4+DgwKRJk+jevTuTJk3C39+fly9ffnB9w/Us0/7877Zv305sbCwDBgygVq1aAGzbto3Dhw8zaNAg3NzcOHToECNGjODw4cPA+wM8lSpVYtCgQYSFhaXbsWREmTNn5tq1ayQlJWFiYsLFixe5c+cOycnJSkDq7QCPj48PT548oVq1aly+fDnV/h4+fMi+ffto0qQJQ4YMSYcjEkIIIYRIe8bp3QAhhBBCCCGEEO935coVSpQogYuLCzNnzsTOzg5A+TaxoYB79+5dAKXYbFjeokULunXrxtKlS/Hx8UGtVjNo0CDgz28VGxvL20IhRMazevVq5s6dS+/evRk2bBgFCxZEr9eTJUsWfHx82LNnD2PHjlXWHzx4MGq1msGDB9O1a1cuXbqEsbExKpUKExMTjIyMGDJkCJMmTQL+7Gf/F3q9XhkdyNrams6dO5OYmIinpyePHz8mf/78791OpVKxb98+jh8/TpcuXcidO/f/9PuFEJ+WRqPB2NiY/Pnz4+vry+vXrwkMDKRr165YWVm9dxuVSsXRo0e5f/8+jRs3JmvWrJ+51V+/Bw8e8Pr1a0xMTADo3bs3165dY9q0aZQtWxZra2ssLS0ZPXo0AL6+vlSpUkUJ8BgbG/PDDz+QlJREtmzZqFu3bjoeTcag0+lSTTNWoEABJkyYgFqtZvLkyQwaNIhJkyZRp04d5T2I4bXUzMyMoUOH8vr1a9auXYulpWWqfRcuXJjx48dTpUqV9/4uIYQQQoiMQKWXeL8QQgghhBBCfJEuXbrEwIEDyZYtGyEhIcp0AAbnzp2jbNmy1KhRg4ULF1KoUCFlmVarxcjIiIkTJzJz5kySkpJITExkxYoVtGjR4jMfiRBCfF69evVi8+bN7Nq1i2+++YakpCQ2b96Mu7s7ZmZmHD16FFtb23eKf8HBwdSrV4+8efN+cN//tGD4V+tfvnyZokWL8uLFC/744w8KFCjwwf3cvXuXihUr8vjxY8LCwnBxcZHipRDpLOU1uHv3bqpWrcqLFy949OgRZcuW/eB2ly9fplSpUuTIkYOtW7dStmxZuZ7/oSNHjlC3bl2+++47smfPzqZNmxg8eDBeXl7kzJkTgJcvXzJr1iz8/Pz44YcflAAP/HmvDJCYmIiZmVmqx8Q/k/Lc/frrr5ibm1OwYEEAkpKSCAkJwcvLizx58jB58mRq166d6ksEjx49wt7envj4eJKTk7G0tFT2+XZg9t8EaIUQQgghvmTybkAIIYQQQgghvlDFihVj+fLlLF++nMyZM7N582Zu3LihLC9Tpgy9e/fm4MGDhISE8OLFC2WZ4cPz69ev07p1a1avXk1sbCyhoaHExMTINA1CiAzDMF2gQUxMDPv376dUqVJKcGf9+vW4u7ujVqs5cuQItra2ABw6dIjr168r2/bo0YO8efOi1WqVx97uL/9pcd2wfq9evVi1apXyePfu3WnSpAlXr17F2tpaCe58qH/OmzcvHh4eWFpaMnHiRGJiYqTQL0Q6M1yDbm5udOjQgdDQUBwcHJTgzoeuZ2tra7p160ZUVBSzZ89OtS/x9zQaDVWqVGHRokUcO3aMTZs24eLigqenJzlz5lT6cCsrKwYNGsSYMWOUEdeOHDkCvLlXNqxnZmamPCb+uZTBnUmTJtG8eXP69++vvL6amprStm1bJk6cyP379xk+fDh79+5Vtt+0aRM1a9Zk9+7dZM6cGUtLS/R6vbLPt4M6EtwRQgghREYl46MLIYQQQgghxBfk9OnT2NjYKNOmGKZFCQsLo0OHDvTv359hw4bh7OwMQPv27Tlz5gz+/v7ExcXRsWNHvv32WwA2btzIoUOHaNKkCXXr1qVRo0bs3r2bqKioVKP0CCHE1yrlSBV79+6lUqVKmJubY2dnR1RUFPfv3+f06dN4enqiVqs5efKkMgUhQL9+/ciTJw/btm1Tpl6B1AXcT1Ek3LlzJ8HBwZw6dYrcuXOzefNmli1bRr9+/d6ZVud9v89wnEOHDuXSpUusWrWK+/fvU7Ro0X/dNiHEP5ey7/n5559ZtWoVHTp0oE6dOqnWe9/1rNfrsbe3x8/Pj/Pnz3PgwAGeP39OtmzZPkvbMwLDiC2RkZFKcOTGjRtcu3YNe3t7jIyMlOfI0tJSmTbWz88PY2NjPDw8qFmzpoR1PoGUIRsPDw9mz55NnTp16N+/vzLyDoClpSVt27YFYMSIEXh4eHDnzh20Wi3z58/nzp07qUafk4COEEIIIf6LJM4vhBBCCCGEEF+Iq1evUqFCBerXr8/NmzcBlG8ElyhRgg4dOrBo0SKCgoKU5TVr1mTUqFFUqVKFGTNm0LJlSzp37kzz5s3p0aMH8fHxSsHC3t6euLg4Xr16lT4HKIQQn1jKUS9++uknVq9eDUC5cuW4dOkSfn5+DB48GCMjI06cOJEquDNlyhSePn1K06ZNU03dkRYaNGjA8uXL+e2332jTpg3Tpk1jxIgR+Pn5KdO7/BW1Wo1Op0OlUjFjxgy0Wi0RERFp2mYhxPvp9Xql74mLi+PevXuYmJjQt29fJXz9V1QqFXq9nty5czNjxgxu3rzJoUOH0rrZGYperycxMZHbt2/j6uqKn58fFy5cwNPTk59//hn4s98ElADPuHHj2LFjB4sWLSIpKSk9DyHDMIRsAgMDmTlzJn379mXmzJk0atTonXUtLS1p1aoVU6ZM4cWLF/Tt25dBgwYRGxvLpUuXyJ8/f6qR74QQQggh/mtUehkrXQghhBBCCCG+GF26dGHFihV88803REREpCoCXb58GX9/f1atWsWAAQMYPHiw8g3VU6dOsXPnTqZNm8arV6/Ili0bZcqUYfHixTg7O3Pv3j0aNmyIXq9nx44dODo6ptchCiHEv5Zy1IsdO3bQpUsX2rVrx6BBgyhUqBDXrl2jVatWXLx4EVtbW44dO5bqG/1r1qzB19eXnDlzsnHjRrJnz/5Z2l21alWOHTtG9uzZmThxIt27dwdSTznyVwzr7d+/n2rVqqV56EgI8WEjR45k1qxZ1KlTBzs7OxYuXAi8CZZ8zKghhkBecHAwXbt2lev5b7zvvCYkJBAXF4eNjQ2zZ89m2LBhlCtXjjFjxlC3bl0g9evFy5cvWbZsGS1btpR74U/o1q1bNG/eHHNzc5YvX55qhM9du3bx6NEj4uPj+fHHH7GwsCA2NpaoqChCQkLImTMnrVu3xt7e/qNfC4UQQgghMip5RyCEEEIIIYQQXwDDh9XLly8nS5YsLFiwgDZt2qQK8BQtWpSRI0cCMGfOHAAlwFO+fHnKly9Pjx49ePHiBZkzZ8bW1hYLCwuioqJYsGABv//+OyNHjpRihRDiq5Zy1AvDyAuZM2emf//+SsHQ0dGRQYMGMXXqVJ4/f87mzZupXLkyOXLkYPHixSxfvhwjIyNWrlxJ9uzZUxV3P3VbDcXmCxcuEBcXR/Xq1Tlw4ADTp08ne/bsNG/eHCMjo48q+BuKmrVq1QI+PvQjhPi0tFotarWaTJkysWnTJsqWLUt0dDQ5cuT46Ol+DH1Oz549AdBoNBLg+YCUfV1iYiIJCQloNBqyZ89OpkyZAOjcuTMAw4YNw8/PD4C6desqI/Co1WqsrKwYPHjwO/sU/87Lly+5fv268jqs0+n4/fffmTNnDgsWLFDWCw0NZceOHVhYWFCwYEHGjh2rLJPnQwghhBBCwjtCCCGEEEII8UUwMjJSPrSeN28ewEcHeIYOHUq+fPkAyJUrF7ly5VL2e/LkSZYuXcry5ctp27Yt48aNAz7+W+FCCPGlMfRdI0aMIDg4mGrVqtGkSROKFSuGXq9Hr9djZmZG+/btMTMzY9q0abi7uytFwUyZMlG2bFlCQ0NxdHRMs4JhykDQq1evKFmyJOHh4WTPnp3t27fTqVMnpT9v3rw5KpXqHxfvpdApRPowMjLCx8dHCVzfvn2bkydP0qRJk/95nxLceb+UffS8efPYtWsXFy9exNTUlI4dO9KgQQO+/fZbLC0t6dq1K/DXAR4D6T8/ncTERBITE9m4cSMVKlTg9OnTbNiwgcePH9OjRw/q1KnDkiVL2LNnD8uXL8fV1fWd9yLyfAghhBBCSHhHCCGEEEIIIb4YRkZGSuH2nwZ43N3dcXJyUval1+u5ffs2AwcO5MKFC3Tt2pW5c+cCpNkIE0II8bkkJyej0WjQaDRs2LCBihUr8scff5A9e3ZUKhV6vR4LCwt+/PFHmjRpwvLly3ny5Ak6nY4aNWrw/fffky1btjQL7qTc76JFi1i9ejXVq1dn9OjRAPz4448kJibSs2fPVAEeY2NjtFothw8fxtjYmIoVK0pBX4h0ljJkYPi/RqPBzMwMNzc3VCoVEydOxNXVlbx581KmTJl0bnHGodfrlb7U3d2dGTNm4OTkRLFixYiOjmb06NH8/PPPdO/enS5dumBubk6PHj2ANwGecePGkZSUROPGjeXe9xP4UPi/YsWKjB8/npEjR9K+fXtMTEwoV64ca9asoUiRIpibm1OmTBlKlChBQkICgHyJQAghhBDiPeTdvxBCCCGEEEKks5RhmpSFhY8N8CxcuJDXr18zduxY8ubNC7z5QNzBwYGZM2fy6tUr6tat+87vEkKIr5WJiQmjR48ma9asLF68mP9j764Dqrr/P44/Lw2CioSICohizC5m6+xu3eyeogIWIGKAimKh2IGt2MHsRKyJTqfYPWMmdlA3fn/4u2dgbG5fgcnej3+Ue4LPuZfzuefez+u8Pzdu3ODUqVNKX6cfFDQ0NCRHjhwMHDjwg31otdo0q7ij36+vry9z586lUKFC2Nvbp1qve/fuqFQqevTokSrAs3PnTvr06UOePHmIioqS8I4QGej9gN+rV6/ImjWrcl6mDPCMHTuWFi1asGnTJgnwfCH6vnz27NmEhYXRt29f+vbtS+HChXn06BEjRoxgwYIF5MyZU6m2Zm5uTvfu3TEwMKB///5YW1tTu3ZtTE1NM/hovm4pzwWNRsObN28AyJo1K/CuGl7BggW5f/8+efLkoW7dulhYWADv3hfXrVuHiYkJRYsWzZgDEEIIIYT4Cqh0Op0uoxshhBBCCCGEEP9VKb8I37ZtGxcuXKBLly6pBnk9PDyYN28epUqVShXgAbh8+TI+Pj6cPn2aM2fOYG1t/cnfJVNlCSG+Rp+qemFkZMSbN2+YPHkykyZNIk+ePERGRlKoUKEMbvE748aNY8SIEfTr149+/fp9sl1Lliyhe/fuWFlZUa5cOa5cuYJWqyU6OpoCBQqkc6uFEHopr9HCw8PZunUrx44do1KlStSpU4cff/xRWZ6UlMTUqVMZO3YsdnZ2EuD5QnQ6Ha9evaJFixY8fPiQjRs3UrBgQSUM4u/vj06n45dffsHGxibV1IOvX79m/fr11KpVSwm3i3/m/XNh9+7dnDp1ClNTU9q2bUuDBg1wd3f/6LY6nY7169czcuRI7O3t2bx5859+XhFCCCGE+C+T8I4QQgghhBBCZJCUVXACAwOZPXs25ubmzJkzh4YNG6b6ovzPAjzXrl3D2toaGxsbCegIITKV96tevH79GktLy1TrvH37lkmTJjF+/HhcXV3ZtGlThgd4zp07R9OmTSlQoAALFixINa3hx2zZsoU+ffpgYmKCi4sLS5cuxcnJKdVAtBAi/aS8RhsyZAhhYWE4ODhQpEgRLl26xN27d+ncuTPh4eHKOZoywJMrVy5WrVpF2bJlM/IwMoVbt25RrFgxOnfuzKxZs0hKSmLjxo34+flhYGDAiRMnsLW1BeDmzZvY2Ngo1WD00mqKxP+ClJ8tBg8ezIwZM7C2tsbFxYWzZ8+SkJBA6dKl8fT0pGvXrqm2SUxMZPLkyYSHh6NWqzly5AhOTk5SCVQIIYQQ4hPkCkkIIYQQQgghMoBOp1O+tPb19WXs2LHUrVuX9evX07BhQ+DddC9qtRp4N4XWjz/+yOnTp2ndujU3btxQ9lWgQAFsbGzQarUS3BFCZBopB1sXLVpEmzZtKFy4MO3bt2fRokXKehYWFvj4+DBs2DBu3LhBixYtuHz5ckY1G3g32Pzbb7/Rtm3bvwzuADRp0oRffvmFY8eOsWXLFpycnNBoNBLcESKD6K/RgoODmTlzJr169WL79u3s3r2bqKgozMzMWLZsGR07dkSj0QBgYmLCwIEDGTVqFNeuXcPT01O5jhP/nKGhIQYGBpiZmQGkCu4cP35cCe68fv2aunXrsmnTpo/uQ/wz+s8WM2bMYNq0aXh5eREVFUVMTAwHDhxg2LBhXLlyhaCgIFatWqVsc/XqVYoWLcq4ceNwcXHh6NGjynubBHeEEEIIIT5OvgEQQgghhBBCiAyg/yJ80aJFzJkzh379+jFgwADy5cv3yW3mzp0LwPz586lZsybR0dGpBoXli3AhRGah1WqVwVZ91Qt7e3vc3NzYv38/69at45dffmH27NnAuwDPkCFDABg/fjxt2rRh1apVFC1aNEPaf/fuXQBlUPn9qg/6qgPPnj1Tpg/JlSuXslyn08lgsxAZ7ODBgyxZsoR27drh7e1NoUKFePr0KS1atCBLlizkzZuXtWvXYmhoyLJlyzA0NMTExARPT08sLCxo0qSJBPD+RzqdDhMTE/LmzcvKlSvJmTMns2bNwsDAgJiYGOzs7JR1J02axMOHDz+ouiP+NzqdjpcvX7J+/XoKFCiAl5cXTk5OALi7u1OkSBHy5cuHl5cX8+fPp3r16jg6OmJnZ0fPnj0xMzOjU6dO2NjYSAUkIYQQQoi/IN/sCiGEEEIIIUQa0mq1n1ymVqvZtWsX2bJlo3v37qmCO+Hh4XTt2pW6desSGRnJkydPgHcBnnbt2vH69WtMTU3TvP1CCJERUla9mDFjBj179mTHjh0cOHCAffv2YWRkxNy5c+nWrZuyjT7AExAQwLlz5+jfv79SESO96QeP161bx9u3b1MNVuorr2m1WurUqcPUqVM/2F6qqAmR8c6dO8etW7fo2bMnhQoV4vXr11SrVo1Hjx4RHh7O3r17cXV1ZdWqVXTo0EGpsmNqakrfvn3JmzdvhvVBmYVKpcLe3p7OnTvz6NEjgoKC0Gq1XLp0CXt7e+DdtfaaNWtYtmwZVatWpWbNmhnc6sxFpVLx4sULTp48SaFChXByckKn06HT6QCwsrKiefPmdOrUiejoaHbv3g1A9uzZGTp0KF5eXkqFUAnuCCGEEEL8OQnvCCGEEEIIIUQa0g9Ajx07llu3bqVaFh8fT2xsLDY2NpQsWRK1Ws2+ffto3bo1P/74I5s3byY6OprOnTsrX4QDrFy5kqtXr+Lg4CCDQkKITCsqKorFixfToUMHBgwYQIkSJXj69Cnt27fH0tISZ2dnli5dSq9evZRtLCwsGDhwIFOmTFEqYaQV/cDlxzRt2pSSJUuye/dutm/fTmJiIgBJSUmoVCq0Wi3h4eH8/vvvwJ8HPYUQGaNcuXKsW7eOypUrk5iYSPv27Xnw4AFjxoyhTp065M2bl+7du2NoaMhPP/1Eo0aNPrguk7DC/0bfNw4aNIhu3boRHx9P/vz5uXXrFhqNBrVaTWhoKMOGDUOn0zFv3jyyZcsmfeoXZmVlRfbs2Xn69CnwYcDUxsaGZs2aAXD8+PFUz7/+s5BUCBVCCCGE+GtyxSSEEEIIIYQQaWzNmjWMHDkSPz8/ZSoVeDegU6lSJc6ePUvHjh1p0aIFbdq04eDBg0yaNIl9+/YRERGBWq1mypQpJCcnk5ycDIC1tbVMqyKEyLR0Oh1nzpzhzp07H616sXDhQnbv3o2joyMLFy5MVYEnS5YsDBw4ME2rXmg0GmXw8tmzZ1y5coW7d++SkJCQqg0ajYbAwEBWrlzJmzdvMDExAd5V5Jk6dSqOjo506NBBBjWFyED6IN77gTx3d3fq1asHwOHDh4mKiqJ169Z07NgRCwsLAJydnXF0dMTR0ZGYmBieP3+erm3/2v1ZCBL+CHwYGRkxatQounbtysGDB/nmm28oW7Ysrq6uDB8+HEtLSw4cOECePHnQaDTSp35B+qmuihYtytGjR1myZAmAEkTVv8+6u7sD714zef6FEEIIIf4ZuYoSQgghhBBCiDT23XffERISQmRkJL6+vty+fRt4VyGid+/eNGjQgA0bNnDq1Clq1qzJyZMnGTx4MGXLlqV169bY2tri4OCAsbExxsbGyn5lWhUhRGalUqkoX748q1evplKlSiQmJvLDDz/w4MEDRo8eTe3atXFzc6NHjx4YGBiwZs0aWrRo8cF+0iLgmHLqj4kTJ1KjRg0KFy5MgQIFKF++PNu3bycpKYmWLVsydOhQ4uLi6N27N/Xr12fYsGE0atQIDw8P3rx5w8aNG7G3t5cqEUJkkJRBPIDExMRUgRIzMzMArl+/zps3b+jWrRvm5ubK8t27d1OhQgXOnDnD1atXlemBxF/TarXKc6+v6PJnnJycWLRoEQsWLKBNmzYYGBjg7u7OhAkT2Lt3Ly4uLkrQRPx9nwq7GhoakjVrVjw8PAAYNmwYkZGRwLugjqGhIVqtloiICABKly6dPg0WQgghhMiEjDK6AUIIIYQQQgiR2dnb29O9e3dUKhV+fn7Ex8ezcuVKLCwscHd3Z/HixcTFxWFhYYGjo6NSmUGj0bBo0SKePXtGxYoVlcEkCe0IIf4L9KEdgAMHDnDgwAG6dOlCx44dlcFzJycncufOjU6n48CBAzx9+pQcOXKkWZt0Op1SUWDIkCFMnTqVihUrEhISwp07d9ixYwedOnXC09MTX19fevfuTfHixZk0aRIHDx7kyJEjODk5Ub9+fSZOnKhUiZDBZiHSX8pzb8mSJezfv58rV66QPXt2OnToQOXKlXF1dUWn0ynXYBEREbi7u6NSqVi/fj1Hjhyhbt26ZMmShSxZsqDVaqXqyGfSP08+Pj4kJiYyffr0P33+dDodKpWKHj160KNHD5KTk1OF2lMGK8Xfk/K5W716NRcvXsTOzo5SpUpRpUoVAJo3b87EiRPx9fWlc+fOBAcH06pVK3LkyEFERASzZ8+mcOHCNG3aNCMPRQghhBDiq6bS/VVtSiGEEEIIIYQQX8Tjx4+ZP38+xYoVo1mzZn+6rk6nY+XKlYwbNw5DQ0P27t1Lzpw506mlQgiRPt4PrugHZ983a9YsPD09OXnyZKq7+jt27AjAzJkzSUxMJGfOnOkyeL58+XJ69+5Nly5dGDRoEG5ubiQnJxMcHMzo0aMpX748Bw4cSFWh4/r167x69QpXV1dMTEwwMzOT4I4QGSRlXzN48GDCwsKwt7fH0dGRx48fc+fOHRo2bMjQoUOpUqUKb968wd3dnYsXL1KnTh3Mzc05dOgQlpaWHD16lNy5c2fwEX2dnj9/TpkyZTA3N+fMmTMYGRl98n0A/njd9IEqAwMDCUx9QT4+PkyZMkX5WaVSMW3aNDw9PZXHpk+fzoABA4B3NygYGhoSFxdH3rx5lQpI8poIIYQQQvwzUnlHCCGEEEIIIdKJnZ0dvr6+qe4Sfp9arebly5cMHz6crVu3YmxszP79+8mZM6cM8gohvnr6AT39VDX6Pm3FihX8/PPPPHz4EBsbG7p06UKBAgWwt7dHp9Mp09CsXr1aCe+sX7+en3/+mYYNG5I9e/ZU+09ru3fvxtraGg8PD9zc3EhKSmLLli0sXryYAgUKsHXrVszNzVO1J3/+/Kn2odPppE8XIoPowyHTpk0jLCwMDw8P+vfvT6FChXj69CmdOnVi+/btWFpaUq5cObJkycL27dvp2LEjR48excrKCnd3d+bNm0fu3LnlGu0fsrKyonXr1kyePJmZM2cyYMCAP60wqV+mUqmU/0tI5MvYsGED4eHhdO/enS5dunDz5k1CQkLw9vbm4cOHjB07FgAvLy+KFSvGtm3bOHr0KLa2tpQuXZq+ffvi4OAg54IQQgghxP9AKu8IIYQQQgghxL/I/fv3adeuHb/++it16tQhLCxMBoWEEJnC3r17mTp1KqtXr8bKykp5fPDgwUydOhUDAwNMTExISEggW7ZsNG/eHF9fX4oUKcKTJ08oX748v/32G02aNMHExISoqCgsLS05cuRImla9eD8Q9Pz5c4oVK0aZMmX46aefSEpKYtOmTfj6+mJgYMDx48exs7MD4NChQxQsWFAqpwmRwT52HXX//n1atGhBcnIyK1eupHDhwqjVajZv3szQoUPR6XScOHGCHDlyKFM0JScnc+HCBbJkyYKDgwOWlpZyjfY/unnzJqVLl6Zq1aps2bIlo5vzn6GvYqR/jxsxYgQ//fQTGzduVMKmR44cYeTIkURFReHv78/YsWM/Gq7S70vOBSGEEEKI/43E0oUQQgghhBDiXyRXrlxMnDiRNWvWsHjxYgnuCCEyBa1WS1hYGDt27KBjx468evUKgPDwcMLDw/Hy8uLkyZPcu3ePZcuWUaZMGZYuXcqQIUO4cOECNjY27NixgzJlyrBnzx4OHjxImTJlOHjwoNJPplW79cGdQ4cOodFoyJ49Ozly5ODBgwc8fvyY7du3fzS4A9C9e3c8PDzSrH1CiD939uxZ3r59i6Gh4Qfn4ZMnT4iNjaVRo0YULlwYjUbDhg0bGDx4MBqNhmPHjpEjRw4A7t69y9OnTzE2NqZkyZIUKFAAS0tLqaD1mfTV097/WaPRkC9fPtq3b8+2bdskvJNO9NXv4I/KRRqNhh9++IH8+fOTnJwMQOXKlQkODua7775j/PjxjBgxItU+3n9d5VwQQgghhPjfSHhHCCGEEEIIIf5l3N3dqV+/PlZWVjIoJITIFAwMDFi1ahXNmjVjy5YtfP/99wA8fvyYQoUK4e3tTcmSJcmePTsdOnRgxYoVtGrVih07drBgwQJevXpFoUKFOHToEIcPHyY6OpoNGzbg5OSUpgFH/aDmoEGD6NChA6tWrQKgRIkSnD59msDAQLy9vTE0NCQmJiZVcGfChAk8f/6cevXqybQuQmSA2NhYSpYsSZMmTT4a4Hnx4gVJSUnY2NgA76bl+1gQ7+nTp3Tu3JmYmJgPfsefTfEk3k0Hq9PplD7wxo0bqX7W992NGjUC3k2HmJiY+EEoRHw5Wq1Wed4XLlxIr1696N+/P5s2beLs2bOo1WqMjY2V16BChQpKgGfcuHGMHDkSePfa6V9HOQ+EEEIIIb4M+eZACCGEEEIIIf7F5MtwIcTX6v3BV0tLS1asWEGjRo3YuXMn9erVY/v27dSrV498+fIBf0y9kStXLsaNG4e7uztr1qzhxYsXAJibm1OmTBkKFy6cpgHHlG3fsmULS5cupVmzZnz77bcA+Pr6kjdvXubMmUNCQgJRUVHY29sr26xevZqFCxfyzTff0Lp1a+nLhcgALi4uVKlShaioKNq1a6cEeNRqtbI8T548LF68mOXLlxMQEPDRClqTJ0/m1KlTmJiYZNShfFX27NlD9erV0el0GBkZKf3p8OHDKVOmDJ07d+bcuXM8e/ZM2aZRo0ZKuPP333/HwMAAnU6XUYeQqekDN76+vvTq1YvFixcze/ZsLl++zJUrV4iNjVXWez/AU6dOHcaOHcvEiRMzrP1CCCGEEJmZhHeEEEIIIYQQQgghxBenHyA8fPiwMgibJUsWVq9eTcOGDdm/fz8nT57k9u3b6HQ6kpOTU4VcXFxcqFevHg8ePGD79u0f/R1pEYpJWRVCq9Vy5coVrK2t6devH25ubgDkz58fLy8vnJycMDMzY/fu3Zw9e5bff/+dUaNG4ePjQ2JiIsuXL8fGxkaqSAiRzjQaDVmzZmXbtm3Uq1ePLVu2KAEefaDE0dGRGjVqEBsby4ABAwC4fPmyEtzR6XREREQQERFBnTp1cHd3z8Aj+jqo1WqWL1/OoUOHqF27thKwjIuLw9bWFmdnZ1auXEnFihXp2LEjmzdvVqZRbNu2Lc+fP2fixIkfvB+I/13K96EDBw6wePFi+vTpw5kzZzh8+DDff/89Z86cISgoiN9++w34MMAzfPhw2rRpo1TPE0IIIYQQX5ZKJxF2IYQQQgghhPhs+qoQQggh/lqbNm3Yu3cvixYtonnz5kr/+fr1a77//nt27NiBq6srR48exd7eXpkCS61WY2RkxPHjx6lQoQIzZsygX79+6dr2IUOGEBkZScmSJXFxcWHy5MnodDol3PPkyRPWrl1LaGgo169fx9zcXFlWsmRJIiIicHZ2TtNpvYQQn6Y/916+fMn333/Prl27aNKkCatWrcLCwgKAV69eUb16dU6fPk2LFi3YsGGDsv2MGTOYPn06Wq2WgwcPkjt3brRarUyD9xfi4uLw8fFh6dKlVK1alQMHDih9f1JSErt372bDhg1ERESQnJxMxYoVadWqFZ06daJq1apkz56dXbt2kS1bNrnuTgO3bt1i48aNzJkzh23btimh1OvXrzNjxgxmzJhBs2bNmDJlilIVL+XffVJSEiYmJsr7tBBCCCGE+HIkvCOEEEIIIYQQnynlAMLvv/9O7ty5M7hFQgjx73bgwAHatWtH9uzZGT9+PM2aNVP60Tdv3tC+fXu2bNlC5cqV2b59O1ZWViQnJ2NsbIxWq2XMmDEEBQWxbt06WrVqlaZtfT9k4+npycKFC0lISOC7775j06ZNZM2aNdU2ycnJPHr0iPDwcOLi4jA0NKR69epUr16dHDlySHBHiAz2OQGemzdv0rp1a3799VdsbW0pUqQIDx484LfffiN//vxs374dFxcXOZ8/gz7k8eTJEwYMGMDKlSupXLkyBw8e/CCEc/DgQY4ePcq0adN49OgRhQoV4uXLl9y/f5/JkyczaNCgDDqKr9v7AbOUf7ejR48mPDwcd3d3cubMyaxZs1Cr1RgaGqJSqbh16xbTpk1j+vTpfxrgEUIIIYQQaUPCO0IIIYQQQgjxmfThnZo1a2JtbU1ISIhyt6oQQoiPO3r0KG3atMHAwICpU6fSunVr5Y79N2/e0K5dO7Zu3UqZMmWIiIjA0dERS0tLVq9ezahRozA0NOTQoUPY2NikWRtThjN9fHzIly8fvXr1IiAggKVLl2Jubk5ERASVKlX67H3KQKcQGeNT596fBXji4+MZM2YMJ0+e5Nq1a7i5uVGtWjV69OhBzpw5JbjzmbRaLSqVCpVKRVxcHAMGDCAiIiJVBZ7ExERMTU2Vbe7evcuePXuIiIjg4MGDJCcnU7VqVdavX4+tra1U3vmHFixYQK9evYB3AR6NRsOCBQsYM2YMjx49onz58uzbtw9LS8tU54w+wDNjxgxatGjB+PHjKVCgQEYeihBCCCHEf4aEd4QQQgghhBDiM/z88888e/aMOnXq0L9/fxYsWECPHj3w9fWVAI8QQvyFQ4cO0bhxY9avX0+dOnWAP6oBpKzAY29vj6OjIxYWFly7dg1bW1u2bt2Ki4tLuoRhgoODGTlyJM2bNyciIoJXr14xYcIEwsLCKFmyJJs2bSJPnjx/ug+Z5kWIjJMyZBMZGcnx48cZNGiQEv5LGeBp3Lgxq1evVgI8eo8fP8bOzk45lyW483Hv98kp+77bt2/j5OREXFwcAwcOZOXKlVSpUoXo6GhUKlWqCmsp97Fx40YiIyOJiIhgx44d1K5dO92PKzMYPXo0gYGB+Pj4MGHCBOXxly9fsnHjRoKDg4mLi2PWrFm0bt0aExOTDwI8M2bMIDQ0lO7duzNv3jw5B4QQQggh0oHc/iOEEEIIIYQQf2Hfvn1UrlyZjRs38vbtW+bNm8eQIUNYvHgxQUFBXL58+ZPbvn+/hFarTevmCiHEv07VqlW5efOmEtwBMDQ0RKPRkCVLFiIiImjSpAmPHj3i4cOH1K1bl40bNxIVFaVMV5MWwR2NRqP8//nz50RFRdGlSxemTJmCqakptra2+Pv74+3tzcmTJ2ndujV37979031KcEeIjKHVapWAQUBAAL1792b8+PHs27cPeHe+Z82alTVr1lCvXj22bt1Ku3btePv2LQBqtRrggypfElr4OH2fHBgYyKVLl5S+r2/fvtSsWZM7d+5ga2tLWFgYHTp04PDhw9SoUQOdToexsTFqtVrZh/76uGXLlnTo0AGtVsvMmTNJSEjImIP7yjVv3pzWrVszadIk/P39lcezZs1Ky5YtGTZsGKampowbN46oqCjltdC/Ds7OzvTt25eRI0cycuRIOQeEEEIIIdKJhHeEEEIIIYQQ4k9cunSJbt26UbVqVdq3b0+2bNkAmDhxIr169SIiIkIZ9PkYlUrFgQMHmDBhAklJSTKFihDiPytHjhxA6lDj+wGexo0bc//+fW7dukWlSpWws7NLNSD/pen3u3jxYjZv3syRI0do2bIlLi4uwLvB/hw5cuDv78+QIUM4fvz4ZwV4hBDpT3+NFRAQwIQJE2jatCmnT5+mbdu2wB/9TcoAz5YtW5QAj5GRUarqIxLE+2tz5sxh9OjRjBw5klevXuHl5cXcuXNp2rQpJiYmwLu+f9q0aXTo0IFDhw4pAR4jIyMlMGVgYKC8N9StWxd3d3euXLmiLBd/T4kSJQgKCqJt27ZMmDCBgQMHKsuyZs1K69atGT9+PI8ePcLHx4e9e/d+EOBxdXVlxIgRODk5pQq6CiGEEEKItCPTZgkhhBBCCCHEn1i8eDF9+/Zl/vz5dOrUCYCVK1fSoUMHAM6dO0exYsU+uf2tW7coVaoUb968YdWqVbRq1UqmVBFCiPfop6V59eoVnp6eBAYGKgGatBYdHc13332Hm5sbarWaqKgoZbDS0NBQ6bOfPn1KSEgIU6ZMoWLFikRERODk5JQubRRCfJ6dO3fSrl07GjduzJgxYz7aj+jP7ZRTaFWtWpWdO3dibm6e/o3+il25coUFCxYwbdo0XFxcuH79OkOGDGHgwIHkypUL+GN6rSdPnjBgwIAPptBSq9UYGRkp+3zx4gWNGzfm8ePH7Nq1C2dn54w6vK/e+fPnGTZsGB07dqRNmzaplr169Yr169fj5+eHg4MDEydOpHbt2hgZGclnFSGEEEKIDCK3fAohhBBCCCHER+jvMDUyMiIxMZHHjx8D4OXlRadOnVi/fj2AEtz51H0RWbJkYdiwYZibmxMZGQnIndxCCPE+Q0ND1Go1VlZWLFmyBBcXl3SruFCkSBFCQkJ49eoVN2/eZOXKlUqb9AOYOp2OHDlyMHToUHx8fDh69Cj9+vWTqRCF+Jc5efIkCQkJ9O7d+5MBQH1/o6/A8+2333L27Flev36dvo3NBAoWLMjIkSMpVqwY169fx83NjRYtWijBHZ1Op1RzsbGxUSrwHD58mFq1aikVePQSExOJjIzkyJEj1K5dW4I7/6OiRYuycuXKD4I7AFZWVrRu3ZoJEybw4MEDhg0bxvbt21Gr1fJZRQghhBAig0h4RwghhBBCCCHec+jQITZu3IhWq6VkyZIULlwYHx8fatSowcyZM/H19cXd3T3VNp/6ktvW1pYOHTrQqFEjduzYwdmzZ9PjEIQQ4quTcgD3Yz9/afrQpb29PV27dsXHx4fs2bOzcuVK9u/fD6AEd1IGeHx8fBgzZgyzZs2SqRCF+JfQarXodDqioqIwMzPD1dUVnU73QcBO/7P+ui1r1qzs2bOHy5cvK9P0ib8nJiaG27dvU6ZMGa5du0ZoaCg3btwA/nieDQwM0Gg0SoCnc+fOHDhwgNatW6fal5GREXfu3KFnz57MnDkT+HRAXnweS0vLTy7TB3gmTZrE6dOnmT59ukxVJoQQQgiRgWTaLCGEEEIIIYRIYf/+/dSuXZsffviBsLAw7OzsOH78ON999x0JCQnUrFmTyMhILCwslGkXPod+WpY1a9Z89O5XIYQQaUs/dcunPHjwgBUrVhAYGEi5cuUYN24clSpVAlACPPp96H/+O+8DQoi017dvX+bOncv27dupX79+qmX68/fFixd4eHgwe/ZssmfP/sFy8fcdOHCALFmysG7dOqZMmULTpk0JDQ0lX758wB/Prf7fhw8fEhwczKBBgz6okJSUlISJiUmq7UTaevnyJTt27KBy5crkyZMno5sjhBBCCPGflba3MAkhhBBCCCHEV+TKlSv06NGDKlWq0KtXL+zs7AA4duwY8fHxmJubs3//flasWMGPP/6IoaHhZw8qVK9enWXLltG8efM0PgohhBDvSxmyOXLkCJcvX+bevXvkz5+fatWqkTt3bhwcHOjYsSM6nY7AwECGDRumBHj0lXf0/b2+moQEd4T4dylbtiwAU6dOxdXVlYIFCwKQnJyMsbExAOHh4axZs4ZOnTrRoEEDZVsJify1TwUWa9SoAYCdnR1JSUnMmDEDlUrFlClTcHZ2VkKPhw4dwtbWlqJFixIWFoZKpUKtViuV1nQ6nRLcSdnnirSVNWtWvv/+e+DTr7EQQgghhEh7UnlHCCGEEEIIIf7fmjVr6Nq1KzNmzKBnz57Au8GfFy9e4OjoiKmpKT4+Pjx9+pSpU6fi6ekJ/PVdwfoKDXopBymEEOJr8LF+7v2+7d8qZdsDAgKYNWsWL1++VJaXLVuWjh074u3tDcDDhw9ZtmwZgYGBlC9fnvHjx1OhQoWv4liFyMw+t8/5/vvv2bhxI3379uXHH3+kaNGiyrINGzYwbNgwHB0d2bRpU6rKO+LTNBoN8EdgcePGjVy4cAETExPKlClD7dq1lXV/++03wsLCmD59Ok2bNmXKlCm4uLiwZ88ePD09yZEjBwcPHsTIyEjCOUIIIYQQQqQg3xYLIYQQQgghxP8zNjYmMTGR69evA+Dl5cWsWbOYOXMm7dq1w8rKCjs7O7p06cLAgQNRqVT0798/1TQAH/P+QJMEd4QQXxt9/9arVy+aNGlC06ZNlWo0//ZQi77tI0aMICQkhPbt29OpUyeSkpLYu3cvS5cuxc/Pj2fPnhEYGEjOnDnp0qULAMHBwfTp04dFixYpFT2EEOnn7NmzqNVqSpcu/dl9zuDBg3ny5AkzZsxg//799OnTB0dHR/bv38+mTZswMDBg6dKlZM+eXaZl+hN79uwhKiqKcePGparE4uPjw5QpU1KtO2bMGAICAgBwcXHB29sblUrFrFmzePToEU5OTsTExBAfH8/OnTuV6jpCCCGEEEKIP0jlHSGEEEIIIYT4f1evXqVjx46cOHECd3d3jh8/jp+fHx4eHjg5OSnr7dixg86dO/Ps2TOmTZtG//79gb+uwCOEEF+znTt30rBhQ4oXL86kSZOoW7cu8HVU4Dly5AjNmzenWrVqhIaG4uzsDMDbt285fPgwHTt2JCEhgalTp9KjRw8Anjx5wsyZM1mxYgVHjhzB3t4+Iw9BiP+c3377jUKFClGpUiVCQ0MpXbo08Nd9jk6n49y5c8yYMYPw8HDl8axZs1KmTBmWLFmCk5OTTA/0J169ekXNmjU5efIkAQEBjBkzBoC5c+cyaNAg2rZtS4cOHbh//z4TJkzg4sWL+Pj4EBISorw2t2/fZuXKlUyaNImkpCRKlCjBqlWrcHZ2liqUf8PX8B4rhBBCCCG+DAnvCCGEEEIIIQR/BG+uXr1KuXLlePPmDRUqVGD79u1kzZoVtVqNgYGBEs6RAI8Q4r9o8eLFDBw4kHz58hEcHEzDhg0/ut77g40ZPUi+cuVKOnXqxIYNG2jRosUHffXGjRvp1q0b1apVIzIyUln27NkzjIyMsLKykv5diHR2//59pk6dyqxZs6hZsyZBQUGUKVMG+PxAw549e3j69CkPHjzA3d2db775hmzZsmV4n/Q1OHr0KIMHDyYmJgZfX19CQkLo27cvsbGxLFu2DFdXVwCOHTvGqFGj2LNnD0OGDGHChAnKa6PRaHjw4AEPHjzAzc2NrFmzynP/N6T8O09OTsbY2DiDWySEEEIIIdKSxNuFEEIIIYQQgj+mVdm6dSuvXr0ia9asHD16lFmzZuHv74+RkREajUb5Er1BgwYsW7aMzp074+Pjw9u3b/H19ZWBXSFEpqQPrnTr1g2AHj16MGDAAMqVK/enFWliY2MpVKgQpqamGRJ+0f/Oy5cvA/D8+fNUj+tVrVqVSpUqsW3bNn755Rfc3d0BsLa2Bt4NoEr/LkT6ypUrF4MGDcLMzIyQkBAAJcDzV1No6ZfVqVPng2VarVbCI5+hUqVKhIWF0bdvXyZOnIharebZs2d069YNV1dXJYRToUIFxo4dC8DkyZMBlACPgYEBuXPnJnfu3IA893+X/u+7YcOG1KpVi379+mFmZpbBrRJCCCGEEGlFvnUQQgghhBBCiP+XmJiIoaEhM2fOZOnSpTg7OxMQEEBQUBAAhoaGaLVa9AVMGzRowPLly0lMTGTmzJm8fv06I5svhBBpxsDAAK1WC0C3bt2YO3cuU6ZM+WRwR6VScejQIUqVKkWzZs1ITk5Ol/CLvo0p2w3vwjmGhob8/PPPABgZGSnr6nQ67OzsqFGjBsBH+3KZskSI9HHkyBF2796t/Ozg4ICHhwdDhw5l165djBo1ilOnTgEoAZ6P+bNzVoJ4n8/d3Z3Zs2dTpkwZQkND2bhxI48ePQJSP//ly5dn7Nix1KlTh8mTJzNs2DBlnZTkuf/7bty4wYsXLxg5ciRLly4lISEho5skhBBCCCHSiFTeEUIIIYQQQvxn6e/K1v9rampKr169UKvVWFlZYW9vT7t27QgKCkKlUjFy5EgMDQ3RaDQYGBigUqmoX78++/btw83NDUtLy8+exkEIIb42BgYGSqWFH3/8UXn8U/2ehYUFdnZ27N27lz179tCwYcM07SNTTsVy/Phx4uLiaNCgASqVCldXV4oUKUJ4eDilS5fGw8MDAwMD1Go1Rkbvvh67dOkS2bJlw9HRMU3aJ4T4c3fv3qVatWoYGxuzZcsWpWpOrly58PDwAPjbFXjE36evTKb/193dnRkzZjBgwABOnDhBbGws8Md7gv6aWB/gMTIyYsKECVhaWhIQEJDBR/P1c3V1Zc6cOYwYMYIhQ4aQlJREr169PlmB5/3zQc4PIYQQQoivh0TdhRBCCCGEEP9JGo1G+SI7Pj6eJ0+eEBcXh7m5OVZWVgBUrFiR1atX4+TkRGBgIKNHjwY+rMDz3XffkSdPnlT7FEKIr937VWx0Ot1Hpzv5VL9XtmxZVq1ahYWFBTt37vzTdf9XKadiCQ4Opm3btgwaNIgDBw4AkD9/fmVal379+jFt2jQAJbizefNm9u3bh7u7O3nz5k2TNgoh/lyePHkYP348AB07dkxVgUcf4Pm7FXjE36evjnPlyhXlsYoVKzJt2jTKli3LmjVr8PPzAz68Ji5fvjwBAQG0a9eOjh07pn/jMxmNRgNAiRIlGDt2LO7u7nh7e3P//v1PbqNSqThx4gR79uxBrVbLZxMhhBBCiK+ISiefbIQQQgghhBD/MSmrM8ybN4+tW7dy/vx5DA0NadasGbVr16Z+/frK+seOHeOHH37g9u3bBAYGMnLkSOCPO5OFECKzSdlPrl27lhMnTnDmzBmKFy9OrVq1aNiw4Wft5+XLlwQEBLBlyxZ2795NwYIFv3hbU1YV8PHxISwsjKZNm+Lt7U3VqlVTLV+1ahUdOnQAoGbNmhQuXJgnT56wf/9+jI2NOXr0KE5OTlKpQIh0lvKaKiwsjEGDBmFra8vy5cupW7eust79+/eZM2cOISEh1KtXT6nAA1Jh5H+V8vkbMGAA4eHhLF++nBYtWijrxMTE0K9fP06dOsXQoUMZN24cQKoKPADJyckYGxunqm4m/tz7nyv0zyG8m87R0tKSM2fO8PbtWypWrPjJ/cTGxlKqVCmKFSvG5s2bcXV1lXNDCCGEEOIrId8yCyGEEEIIIf5TUlaOGDx4sDIAkStXLt68eUNoaCgdOnRg7ty5yjYVKlRQKvAEBQXh6+sLIMEdIUSmlLKKjY+PD127dmX+/Plcv36dWbNm0bhxY8aMGcOjR4/+cl9Zs2alUaNGVK5cmQIFCqRJe/UDksuWLWPWrFn07t2byZMnU7VqVWW5vopQu3bt2LlzJw0bNuTs2bPMnj2bw4cP8+233/Lzzz/j5OQkVdSEyAD6aZoAvL29CQ0NJS4ujk6dOkkFnnTwfr9nb2/P27dvGTlyJJs3b1Ye//bbb5k9ezZlypQhJCSEYcOGAX9U4NHTh04kuPP59J8rpk6dyvXr15XnsHfv3gwYMIA3b95QsmRJJbjzqb91U1NTmjdvzrlz51iyZAmQdlXvhBBCCCHElyWVd4QQQgghhBD/SXPmzMHT05OBAwfy448/4ubmxo0bN9i2bRve3t4AzJ8/n549eyrbxMTEUKtWLaysrLh8+TJZs2bNqOYLIUSaCwkJISAggN69e9O9e3fKlSvHzp078ff358yZM0yaNInBgwd/1r70d/2nZcWyDh06sHfvXvbs2UOJEiU+2QaAJ0+e8ObNGy5fvkyBAgWwt7cnS5YsqSoOCSHS3z+twDN69GhKly6dUc3+qqV8zkNDQzlw4AA6nY5du3ahVqvJly8fU6dOpWnTpso2x48fp1+/fpw8eRJ/f3+Cg4MzqvmZyvDhwxk3bhx9+vRhypQpjBo1ismTJ+Pp6UlQUBDZs2f/rP2cP3+e5s2bkz17dg4ePIiZmZkEeIQQQgghvgIS3hFCCCGEEEL8p+h0Ol69ekWbNm24ePEi+/fv/6AaxIYNG2jTpg358uVjzZo1lCtXThnYOHXqFA4ODjg6OkoJeiFEpnXr1i3q1KlDvnz5mDVrFgUKFECn0/HTTz/Ru3dvTE1NOXnyJLa2tso2GdknPnv2jOLFi1OkSBH27Nnz0fb8VfukTxfi3yFliO5zAjxTpkyhTJkyzJkzh2LFimVUs796fn5+TJ8+nfr169OhQwfu3LnD2bNnWbJkCU5OTkyfPv2DAI+3tzcxMTEEBwfj7++fga3PHB49esSPP/7ITz/9RNGiRTl//jwjRoygZ8+e5M2b92/ta/369bRt25Zjx47h7u6eRi0WQgghhBBfktR4F0IIIYQQQvynqFQqEhISuHDhAoULF1aCOzqdTik/36pVK4KCgrh58yYnT55UttXpdJQpUwZHR0eZVkUIkandvXuXa9eu0bFjRwoUKEBycjKrV6/G29sbc3NzJbiTmJjI/fv3gYydlkM/Xcv169e5ffv2B+3RarWoVCqePHnCtGnTPjrdiPTpQqS/lFMt6RkaGpKcnAz8MYXW48ePPzqFVt++fenTpw93794lZ86c6dbuzGbXrl1MnjyZli1bMm3aNFq3bs3AgQNZsGAB06ZN4/bt23h7e/PTTz8p27i7uzNlyhQaNGhAu3btMrD1mYe9vT2bN28mV65cXL58meLFi9O4cWPy5s2LVqv9W9PCNWnShHHjxlGmTJk0bLEQQgghhPiSJLwjhBBCCCGE+M/RaDTAu0He33//Xam2oB+41el01KhRA4CdO3cq66cc2JVpVYQQmYW+j0vp7du3AMr0gBs3bmTo0KGoVCqOHz+uVNx5/fo1zZo1IyYmJv0a/P9SDmLa2NhQq1YtHj58yLFjx1Ktp9FolClhRo0axbJly7hx40a6tlUI8aGU52ZsbCz79u1j7dq1vHjxAmNjY2U9b29vpk6dSlxc3AcBHgcHB/z8/Dh9+jR2dnYfDQOJdx48eEB8fPxHl127dg2dTkfXrl1xdnZW3hcMDQ3x8vJi/Pjx3Lp1i8GDB7Np0yZlu0qVKrF582ZcXFxQq9XpchyZ3c6dO3ny5AkODg6cPXuWxYsXc+fOnb895aSpqSlDhw7FyMhIXhshhBBCiK+EhHeEEEIIIYQQ/zm5cuWiSZMm/Pbbb+zduxeVSqUMAqvValQqFeXLl8fc3BwbGxsMDQ3/9hfmQgjxb/T+wLZarVbCiNu3b1cet7S0BGD37t0sXboUX19fDAwMOH78OHZ2dsp6I0aM4ObNm+kSaHy/7fpApf7xRo0aYWJigp+fH6dOnVL6dX3b1q9fz7Zt2yhYsCC5cuVK8/YKIT5Nq9Uq5+aYMWNo1KgRderU4YcffqB06dKEh4fz6NEjZX19BR59gEc/PR68q1aSLVs2dDqdXK99wq+//oqrqyvTp09XqhrBH/1nXFwcAE+fPgX+6Df1/eiPP/5I1apVuXHjBv7+/mzdulXZh35dIyOjtD+QTCjle5tOp6NixYpERkaya9cumjZtyty5cxk9ejR3795VPrP83ZCavDZCCCGEEF8H+TQjhBBCCCGE+E+qW7cu5ubmdOvWjT179ihfhuvvTl2yZAnx8fEULVoU4G+VqRdCiH8r/cB248aNiYiIUAb0PD096dixo1LRomLFijRr1ozw8HAGDx6MgYEBJ0+eVII7Op2O5cuXs2PHDurVq0eRIkXStN0pK3SsWbOGSZMm4ePjw+XLl0lISACgefPm9OzZk1u3btGsWTNmzpzJr7/+yosXL5g8eTL+/v4ATJkyBQsLC+nXhcggKUM2vr6+jBo1Cjc3N8LDw1m8eDE5cuRg1KhRzJkzhwcPHijb6QM8L168oF69ekRHR6far0x992kPHz4kW7ZsnDlzJlXfp38dypYtC0BUVBRv3rxRluuvj62trSlSpAgODg5cuXKFkSNHcuLEiVT7EH9fyve2devW4enpyb1795T31cWLF9OwYUMWLlyYKsCj3+bcuXOpzhEhhBBCCPF1k8i1EEIIIYQQ4j+pefPmBAUF4ePjQ7169ZgyZQpVqlShTJkyLF68mJkzZ1KwYEE6dOgAyICQECLz2Lt3L3v27OHgwYPkzp2byMhIZs2axeDBgylevLiyXufOnbl+/Trnzp3D398fa2tr4N3A+8KFC5kwYQImJiZMmDCBLFmyKFMQpgV9ZQdfX18mT56MgYEBWq2WiIgIvLy8aN++PXnz5mXMmDGYmZmxfPlyvL29ATA2Nkaj0VC0aFEiIyPJnTs3Go1Gpj8UIoPo+4l58+Yxf/58PD098fDwoHDhwjx//pyxY8dy//59QkND0Wq1eHh44ODgALwL8Lx9+5bZs2dToECBjDyMr0r9+vXZsWMH+fPnx8TEhJiYGIoXL46FhQUA7u7uVK9encWLF1O9enXatWunbKt/vR4+fEi/fv2wtramX79+bNu2jfLly6dp35+Zpaw+FRAQwLx58wAoWbKkEoi1trZmxYoVdOzYkfDwcADGjh2Lvb09W7ZswcvLi9atWxMSEiLvaUIIIYQQmYBKJ7cZCSGEEEIIITIRrVb7l3cAp1xn+vTpTJw4kXv37gGQNWtWXr58SYECBdi9ezcuLi4yyCuEyHRWr17N8OHDuX37Nmq1mqFDh9K3b1/y5MmTqo+cPXs206ZN4/fff6dkyZKUKVOG2NhYzp49i42NDXv37k23fnLu3LkMGDCAFi1a0KFDBy5evMiGDRs4ffo0gwYNonfv3jg7O5OcnMzp06fZuXMnV69excLCggoVKtCoUSPs7OykTxfiX+Du3bt06dKF5ORk5syZQ9GiRXn58iXu7u68fPkST09PNm3axKVLlxg4cCC9e/fG0dFR2f7169dYWlrK+fwnLl26RM6cOZXgpd7MmTPx8vJi1qxZdO3aFXNzcwCWLl3KgAEDiI+PZ9asWTRq1EgJTf30008MHDgQDw8PevXqhbu7O0lJSZw4cQJbW9t0P7bMZMSIEYwfP55u3brh7e1NsWLFPljn+fPndOrUiW3btlGrVi2KFy/Otm3bePz4MSdOnCB//vwZ0HIhhBBCCPGlSXhHCCGEEEIIkWmkvPP3999/J3fu3J+8GzjlYM/Ro0c5fvw4u3btws7OjmLFitGlSxdy5swpg0JCiEwlZTCnVq1aHDhwAENDQ+bOnUv37t0/ut6+fftYt24dK1asIDk5mUKFClGrVi18fX3JlSvXF+sn3++vU7YhISGBIUOGcPv2bcLCwsiXLx9qtZrLly8zePBg9u/fz6BBg+jbty9OTk6ftU8hRMa5ePEi7dq1Y/To0TRt2pS3b99SvXp1bt++zZQpU2jZsiU7duygbdu2ODo60rNnT3r16pUqwCMVXz7t119/pWzZsrRq1Yr58+djbW2t9NWRkZGMHTuWS5cuMXnyZDp27EiWLFmAd6H2cePG8fz5cypWrEjVqlW5e/cuO3fuxMTEhKNHj+Lo6Ei9evU4deoUv/zyC87Ozhl8tF+vvXv38sMPP1C3bl3Gjx//p89lfHw8PXr0YPXq1Zibm1OoUCE2bdqEs7MzarVamQZTCCGEEEJ8vSS8I4QQQgghhMh0vL29Wb9+PWfOnPnTu4HfH8RNSkrCxMREGQyS4I4QIjNSq9U8e/aMKlWqkC9fPs6dO8ezZ8+IiIigWbNmynrv95EPHjxAp9Ph4OCgTPfxJftJ/e9LTk7G2NhYeXzkyJFoNBoiIyPp168fHh4eqX7v1atX8fLyYt++fQwaNAhPT09y584NyOC+EP9msbGxlChRArVajY+PD/PmzWPs2LF4eHhgbm7O/fv3KVeuHAkJCTx79oyJEycyaNAgCeB9Bo1GQ9myZYmNjaVTp05MmzYtVQWeXbt2MXLkSGJjY5k2bRodOnTA0tISgPXr17Np0yZWrVoFgLm5OSVKlCAiIoJ8+fJx69YtqlatSt68edmyZQs5cuTIkGPMDKZOnYqvry/bt2+nTp06n1wv5XvZ3r17MTc3p3DhwtjY2MjnFSGEEEKITETi2EIIIYQQQohM586dOzx+/JjLly9ja2v7yUoL7z/2/h2r8kW4ECIzMjQ0xM7OjiNHjqBSqdi3bx8+Pj60b9+eVatW0bRpUwBloFA/aOjg4IBGo0GlUin955fqJ6Oiohg8eDDR0dFYWVkpj9++fZvNmzdz8eJFjI2NMTU1VX6vvl1ubm5Mnz4dLy8vQkNDMTQ0pE+fPuTNm1eCO0L8C+nP3RIlSgDvgntHjhyhWLFiDBo0SFnPysoKIyMjhg4dSmxsLG3btpXgzmfQV2E5deoU1apVY/ny5QCpAjz16tVDp9MxatQoBgwYAKAEeFq3bk2rVq0YMmQIcXFxZMuWDTc3N6ytrbl//z5z587l7t279OnTR4I7/6Njx45hampKkSJFAD4I4ug/w6jVaiXUWrt27VTL5fOKEEIIIUTmIZ92hBBCCCGEEJmGvrDokCFDMDIyYs6cOcCHIZ1P0a8ng71CiMxEq9Wm+lnfx9na2mJjY0Pbtm0ZM2YMdnZ2tGvXjp9++inVenv37mXGjBnAH2GdL9lParVa1q5dy+nTpwkKCkq1zMnJiTlz5tC4cWMSEhLYsmULd+7cUdqg7/f1AR791CMrVqz44LiFEP8O709ld+vWLU6dOkWOHDlITExUli1fvhyNRkPdunVZtmwZTk5OaDSajGjyV8XIyAi1Wo2BgQHR0dFUqlSJ5cuXM2DAAJ49e6asV79+fYKCgihRogQDBgxg5cqVvHnzBnj3GpUuXZo6derg7u6OtbU1sbGxTJgwgWnTpvH9998zbNgw4I/rb/H32dvb8/btW3bv3g2kDsTqdDoMDAx4/fo1DRs25MqVKx9sL2E2IYQQQojMRSrvCCGEEEIIITIN/WBQwYIFKVWqFBs2bKB3795UrVo1g1smhBAZI+Vd/FFRUdy8eZO7d+9So0YNSpYsSbZs2QDo3LkzKpWKESNG0K5dO1auXEnt2rWJiopi6NChaDQaOnXqRPbs2b94Gw0MDJg4cSKlS5emTZs2AKmmzqpUqRKDBw8mPj6e7du34+7uTq9evbC1tVUCPPoKPJMmTSJ79ux06NBBBjWFyECfO2WdgYEBbm5uNGrUiAMHDrBmzRoqVqzIgQMHCAsLI0+ePOTLl0/Zl1QZ+Tz6AI+RkRHR0dFUr179oxV46tevD5CqAk/nzp0xNzdX9qXVajl37hydOnXi6tWrdOrUifnz5yvLpK/9c392LjRu3JhZs2YRERFBhQoV+Oabb4A/pvIFWLBgATExMZw/f56CBQumW7uFEEIIIUT6U+kkGi+EEEIIIYT4Cr1fVl7/s/4L8p9++okWLVowbtw4/Pz8MrClQgiRMVIOqg4fPpw5c+YoVRfMzMxo0qQJU6dOxdHRUdlmxYoVBAYGcufOHYoUKcLdu3cxNjbm6NGj5MuX77MH5P+O9/vzgQMHsm7dOi5duoSlpaXy+M8//0xAQADHjh0jMDCQ7t27Y2trC6QeHNXv7/39CiHSxvsBjoSEBMzMzAB4+vTpn06tlPK6LSAggAsXLmBhYUF8fDyurq7s2bMHZ2fnNOl7MqNPhWk0Gg3Vq1fn6NGjdOrUKVWAB2Dnzp2MGjWKixcvEhQUhIeHh/IaAjx48IADBw5gYmJCy5Yt//R3iT+8/z708uVLzMzMlGDOmzdv8Pb2ZsmSJfTs2ZOePXtSrlw5Zf0NGzbg7++Pg4MDkZGRqV4zIYQQQgiR+Uh4RwghhBBCCPFViIuLw9LSEjMzs1QDOLt27aJWrVoYGb0rLKrValGpVNy+fZuGDRvy8OFDjhw5QqFChTKy+UIIka5S9pMBAQGMHz+eVq1a0b59e6pUqUK3bt3Yvn07FSpUYO3ateTJk0fZdsuWLaxcuZIzZ85QuHBhpk+fTt68edMlDJOcnEyXLl1YvXo1pUuXJjo6+h8FeIQQ6W/hwoX06NFD+dnPz49Hjx4RGhr6l6EDjUbDyZMnWbduHTdv3qRgwYJ4enqSK1cuCeJ9ppTPU3R0NA8fPqRJkyZKFZ2/CvDs2rWL/v37o9PpOHPmDFmyZEm1/5RhHQnu/LWUr8e8efPYtWsXJ06cIFeuXNSuXZtBgwZha2vL6dOnGTFiBNu3b6dIkSK0bt2a4sWLs2fPHrZs2YKBgQFHjhzByclJnnchhBBCiExOwjtCCCGEEEKIf72ff/6ZDh06MG7cOJo3b67cCRwYGMjo0aMpWbIk7dq1o0mTJhQpUkTZLigoiKCgIBYtWkTXrl1l8EcIkal9rI9btmwZvr6+tGzZkoEDB+Lm5gZA8eLFuXXrFq9fv6ZChQqsW7eO3LlzK9slJSXx4sULsmTJgoWFRbr0n/rwzZs3b/D19WXOnDmUKFGCw4cPfzLAM3r0aDp37oy9vX2atk0I8ef69+/P7NmzCQgIYMyYMQwcOJCwsDBGjBjBwIED/3TKvT8L3sm12+dJ+TyNHTuWWbNmkSNHDubPn0/lypWVKbT+KsBz4MABihQpQs6cOSUQ+T9I+dwNHjyYGTNmkCdPHkqWLMn169c5d+4cderUwcfHh9q1a3PmzBlWr17NlClTUKvVAGTNmhV3d3fCw8NxcnKSc0EIIYQQ4j9AwjtCCCGEEEKIfzWdTsf69evp3bs3dnZ2jBs3jkaNGmFmZsapU6dYunQpBw4c4OzZs2TJkoX+/ftTuXJlGjduzIMHD/juu+/IkiULMTEx8oW3ECJT+vXXX7G2tsbFxSXVgOGzZ8/o3Lkzd+/eZfny5RQrVozXr19Trlw5Xr58ycSJE1m7di1bt25NVYHn/QHC9BzA1VcVePPmDUOGDGHevHmfDPCMHDmSffv2MXPmTDw8PGSQWYgMFB0dzdChQ4mJiaFMmTKcOnUKPz8/+vTpg7Oz82ftQ8Ii/0zK583Hx4ewsDCaN2+Ol5cXVapUUdZLTk7G2Nj4LwM8IJV1vpSZM2cycOBA+vTpQ58+fShatCg3btxg1KhRrFy5ko4dO7J06VLl9YuNjeXWrVs8efKEMmXK4OLiQtasWSW4I4QQQgjxHyHhHSGEEEIIIcS/Xnx8PNu3b2fw4MEYGBgwadIk6tevT5YsWVCr1SQmJrJgwQK2b9/O3r17AWjVqhWtWrUiIiKC/fv3M3v2bDp37iwDQ0KITOXq1asUKlQIOzs7zp07h52dnbLsxYsXDB06lAoVKtClSxfi4+OpVasWV69eZcKECXTv3p2EhASKFSvGjRs3qFixIhEREZ890J5WPjfAc/DgQWbOnMmUKVPImzdvBrZYCAFw48YNKleuTFxcHGXLliU6OhpTU1Ol6otIW7NmzcLX15cff/wRLy8v8uXL98l1UwZ4mjZtytKlS8mWLVs6tjZz0+l0vHnzhvr16/P27VtWr15NwYIFUavVbNmyBS8vL0xMTIiJiVGmfvwUCVIJIYQQQvx3SHhHCCGEEEII8VVISEhgy5Yt+Pj4KAGeBg0aYGFhkWqdo0ePsnDhQnbu3IlGowHg5cuXeHh4MGvWrIxqvhBCpJlmzZqh1WqJiIjAysoK+GOw7969ezg6OqLVapkwYQJBQUEEBgYyaNAgTExMiI+P57vvvuPevXvcvXuXxo0bs3nz5gwfKPzcAE9SUhImJiZSlUCIDKT/ennVqlV07NgRBwcHHjx4oEyhBTL91ZfwZyGO58+f06pVK+7evcumTZv45ptvlGWrVq3i7NmzPHjwgCFDhlCkSBFUKhUajYYSJUpw584dbt68iY2NTXodSqb0/utz/fp13NzclPMgMTGRTZs24efnh4GBASdOnMDW1ha1Ws21a9coXLhwBrZeCCGEEEL8G0h4RwghhBBCCPHViI+PZ+vWrakCPPoptOCPaQPi4+N58uQJEydO5MyZMxw6dAiAvXv3UrNmzYw8BCGE+GJSDobHx8djbm7OjBkzaNiwIfnz509VaUyr1dKsWTPOnDnD9evXMTY2VvZToUIFWrVqxZMnT+jTpw8uLi7p0ua/8rEAT5kyZYiKilJCSkKIf5f169djbGzMuHHjOHHiBL6+voSEhADvzn8DAwOpgPg/Wrp0KV26dEn12IMHDyhdujTlypVjy5YtaDQaYmJimDNnDitXrsTExISkpCRy587N1q1bKVmyJPCun338+DE5c+aUCi//g5Tvbbdv38bJyYn79++TJ08eBg8ezMSJE1m3bh1DhgzBwMCA48ePK5XykpKSyJMnD6NHj6ZPnz4ZeRhCCCGEECKDydW4EEIIIYQQ4qug0+kwNzenSZMmTJo0Ca1Wi4+PD9u2bSMhISHVuubm5uTJk4fp06ezefNm5syZA0BUVBTwbqBCCCG+doaGhqjVauBdv7dhwwa8vb1p27Ytt27dUiorALx69Yq4uDg0Gg2PHz8G3g02Llq0iKtXr+Lu7k5ISAguLi7KPtOqzfBu8Pm3334D/qja8T4DAwO0Wi1ZsmRh8uTJ9O3bl1OnTtGqVas0a58Q4vPoz9v3z9/WrVvTrFkzZsyYQdmyZZk4cSJDhw4F3p3/+uDO2bNn+f3339O30ZnAhAkT6NatG6NGjUr1uE6nw9nZmW3bthEYGEj37t1p1aoV27dvZ/jw4ezYsYMRI0bw+++/M378eACSk5MxMDCQ4M7/SKfTKe9tvr6+dOnShV9//RVTU1McHBzYvXs3kyZNUqb/jYmJUYI7Op2O0aNHk5ycTO7cuTPyMIQQQgghxL+AXJELIYQQQggh/pXeD9joB3vMzMxo1KjRRwM8Ke/k1g8mWVtb0717dypUqMCyZcuIi4uTwQkhRKag0WgwMjJSfm7VqhXdu3fn119/pXXr1vz2228YGhqi1WrJli0b5cqV4/79+wwZMoRffvmFKVOmMGHCBPLkyUPRokWV/aTcZ1rYvHkz3bp14/DhwwB/WoUjZYAnJCQEPz8/5s+fn6btE0L8OY1Go5y3jx494tq1a9y7d4+kpCRlHXd3d2bPnq0EePz8/JRl27dvp1evXkyaNEkJGIrP06BBAzp37syYMWMIDAxUHs+VKxdDhw7FxcWF0aNHExkZScmSJTl27BgjRozgu+++Y+DAgWTJkoWsWbMCpKrAJtfG/5z+XFi4cCFTp06lWLFiZMuWjRw5cuDr60tsbCwjR44E4OTJk9jb2wPvPqusWbOGVatWUaVKFapVq5ZhxyCEEEIIIf4d0vbbGCGEEEIIIYT4B1KWnr9x4wbPnz/n5cuXlC9fHlNTUywsLGjQoAEAPj4++Pj4AKSaQkv/RbpOp8PY2JjSpUtz/Phxbt26ha2tbQYclRBCfDkp7/Tv27cv5ubmTJkyhfDwcAwMDAgPD6dNmzasW7dOmQZr8uTJ3Lhxg9WrV7N69WoAChcuTGRkJLa2tulWecHGxgaABQsWULduXWUg81MMDAzQaDRYWloqFSPUanWah4yEEB9KeY0WGhrKihUruHDhApaWljRv3pwuXbpQtWpVAMqXL8+sWbPo378/kyZNIi4ujpw5c7J+/XoeP37MmjVrPnsaPfFOiRIl8PPzQ6VSMXr0aBITE5V+sXnz5hQoUIBHjx6RNWtWihUrplwXazQali1bhkajoUyZMgCpplYUf9/775kHDhygSpUqDBw4EFdXVwAaN27ML7/8wqpVqyhfvjwPHjwga9asaDQaZs+ezYwZMwCYM2cO2bJlkwpIQgghhBD/cfIthxBCCCGEEOJfRavVKgM5wcHBrFixguvXr6NWqylatCjt27enV69e2Nra/mWAB96FeOLj40lMTMTCwkLu8BZCZAr6AdfQ0FDmzp1Ls2bNuHfvHo6OjkplmvcDPKampmzbto05c+bw4sULsmfPTqtWrbCzs0s1IJ/Wqlatyo8//siyZcu4ffs29vb2fzlg+X7bJLgjRPpLeY3m4+PDlClTKFmyJL169UKtVjNv3jxOnjzJqFGjaN68OfBHBZ5Ro0axbNkyVCoVRYsWZffu3Tg7O6dr35NZFClShMGDB5OQkED58uWBP4I4xYoV+2B9nU7H2rVrmTdvHkWKFFGmHpTgzv9G/541ZMgQjIyMePjwIe3atcPV1VX5u86fPz/9+vVDp9MRERHB7t27KVq0KE+fPuXevXu4urqyZcsW8uTJI+eCEEIIIYRApfvUxOJCCCGEEEIIkYH8/PyYNGkSNWrUoHHjxuh0OlasWMHFixepWbMmS5cuxc7OjoSEBLZs2YKPjw8mJiaMGjWK1q1bY2pqCrwbaNqyZQstWrSgS5cuLF68OIOPTAgh/rn3B/fatm0LwPjx48mfP3+qijS9e/dmwYIFlClThnXr1pEvX76P7jM97/TX/67t27fTuHFjWrRowZo1aySMI8RXZMqUKQQGBtKtWzf69etHoUKFAHBwcODx48e4uLgwdepUmjZtqmxz+/Ztbty4wZMnT6hRowY2NjYSVvgfvX79GktLy08u1+l0vH79mrFjx7J69WpUKhXR0dE4OztLhZf/Qcq/20ePHlGxYkVu3rwJQFBQECNGjABSVzZ6+PAh+/fvZ+7cuTx79ow8efJQs2ZNOnfujL29vZwLQgghhBACkPCOEEIIIYQQ4l9ow4YNdOrUiY4dO+Lj44ObmxsAy5cvp0uXLpQqVYro6GisrKwAiI+PZ/v27XTp0oXixYuzf/9+zM3Nlf3FxsayceNGAgMDgfQdqBZCiLQwbtw4EhMTmTVrFmFhYXTo0EFZlnIQMGWAZ/369bi4uCjL02rKFJ1Ol6pCR0r6/jchIYFatWpx6dIloqOjKVasmPTNQnwFzp8/T7t27ShSpAijR4+mUKFCvHjxgm+//ZY3b95Qu3Ztli5diouLC6GhoUoFnvfJ+Z72bt26Rc2aNbl37x61atVi7ty5UuHlf5Ty7zY0NJQffviB+/fv4+/vT1RUFE2bNmXu3LnY2dkBH05N9rHnXs4FIYQQQgihJ7c1CSGEEEIIIf419F9wR0dHY2pqiqenJ25ubqjVatauXUtgYCDOzs7s3r0bKysrEhMTMTQ0xNzcnPr167N69WrKlCmTKrgDUKJECUqUKAHIF+RCiK/flStXGD58OLly5SJLlizkypULgOTkZIyNjTE0NFQGCOfNmwfAggULqFmzJtHR0eTNmxf4slOmnDhxgrdv31K9enVUKpUyODlmzBisra2pWLEiZcuWVfpfMzMz/P39adq0KWvWrKFYsWLSNwvxFXj48CHPnz+nS5cuFCpUiDdv3lC9enWePXvG5MmT6dSpE05OTowZMwZ/f3+AjwZ45HxPe87OzoSGhvL69WsaN25MtmzZJLjzP9L/3Q4fPpxx48Zx48YNZs6ciY+PDwkJCWzatIkSJUowatQo4MP32ZQ/6z/3yLkghBBCCCH0pPKOEEIIIYQQ4l9Dp9ORmJhI1apVSUxMJDY2loSEBCIjI/H19cXAwIDjx48rd7NeunSJe/fuUa1atVRTrsjAhBAis9NXG3vy5Am9evVSQjopA4op+8J27dqxe/duzp8/j4ODwxdrh06n486dO7i4uJA3b16WL19OtWrVANi7dy8tW7ZUpnbx8PCgQYMG1KhRA4CbN29So0YN1Go1e/fupUiRIl+sXUKItHHv3j0uXbpEzZo1SU5OpmfPnmzevJmQkBC6d++OqakpmzdvpmXLlhgZGWFmZsaGDRuoU6dORjf9PyVlxRf9/yXA/s+lfD+9ceMGjRo1ok6dOqmmjYuKimL48OH8/PPPjBs3jqFDh2Zkk4UQQgghxFdIrtaFEEIIIYQQ6Uar1ab6OTExUfl/cnIyKpUKMzMzHBwcePPmDQC7du36aHAH4IcffmD+/PloNJpU+5XgjhAiM9L3dTqdjoYNG7Jq1SqyZ89OeHg4c+bMAd5VBdD3tfoKPACrVq3i6tWrODg4fNAX/y9UKhVOTk6MGDGCO3fu0LdvX6KjowGoXbs2Z8+eJTw8nIIFCzJ9+nTq1q1LgwYN2L9/P3ny5GHSpEncv3+fM2fOAB++Twgh/l0cHR2VgN7169c5cOAANWrUoEePHpiamgJQrlw5ypUrR7du3TAwMOCbb77JyCb/J6Ws8KL/vwR3/jn9Z4vDhw9z6NAhbt++TadOnShUqJDyvvXdd98RHBxMxYoVGTZsGCEhIRnZZCGEEEII8RWSK3YhhBBCCCFEutEPGvz0008kJycrgzw+Pj4sXbqU+Ph4dDodZcqU4ebNm7Ru3RpPT08MDQ35+eefUwV3QkNDefDgAe7u7hgbG2fI8QghRFp6P5ioH4DV/1u7dm1WrVqFpaUlwcHBLF68GPh0gCdHjhxfvPKC/vcEBQURHBzMhQsX6NevH/v37wfeTdvSvXt3tmzZwoEDB6hbty4nTpygdu3alCpVimPHjmFqasr48eOJi4uTwWUhvgL6aocPHjzgzp07lC1bFhMTEwDUajUzZ84kLi6OefPm8fDhQ3Lnzv1BfybE12bRokVUq1aN1atXU65cOcqXL68s009uUKNGjVQBnkmTJmVUc4UQQgghxFdIvhERQgghhBBCpKs2bdrQvHlzZZDZx8eHKVOmcOvWLaWsf+fOnbGxsWHjxo0kJCRw8uRJZZoXnU7HunXrmD17NgUKFKBTp04y2CuEyHRSTtGxbNky+vfvT7Vq1QgICGDt2rXKevXq1WP16tW8fPmSESNGfDLAo/el+8uUv8ff318J8Hh5eSkVeADs7e359ttvWbduHYcPH8bPz4+EhARmzJhBYmIi9+7d48SJE8qxCyH+/aytrVGpVGzZsoUjR44AsHbtWn766SdKlChBfHy8EuqRqogf0gc+xNehSJEiNGzYkF27dnHo0CEOHjwIvHsfVKlUHwR4qlatip+fH7NmzcrIZgshhBBCiK+ISiefEoQQQgghhBDpaNeuXXTr1g2tVkvhwoU5ePAgAQEB9OzZE2dnZ6UqxIEDB2jatCmvX79m6NChtGnTBjMzMxYtWsSaNWvQarUcPXoUJyenL15JQggh/i2GDBlCaGgoFhYWqFQqZUrBzp07s3jxYqUKz86dO2nbti1Zs2YlODiYLl26pGs7U/bD48ePJyAggG+++YZZs2ZRvXp14F1FDn3FDoBLly5x/vx5QkNDiYmJoUWLFqxbty5d2y3Ef9n710/vn6OfY9y4cQwfPhwrKyvy5MnDtWvXcHBw4NChQzg5OSnBbJFayufl9evXWFpaZnCLxKekfK2OHz9OaGgoa9eupVu3bgQGBpI3b96Prrt7925mz55NWFgYzs7OGdJ2IYQQQgjxdZHwjhBCCCGEECLd6L/QvnDhAmXLlkWtVlO9enUWL15M3rx5lUoT+vWOHDlCx44duXXrlnJHq7m5Oe7u7ixduhQnJ6dU1SmEEOJrl3Lgb8WKFXh4eNCpUyc8PDywsrJSqtrcuHGD5s2bs3HjRmXbnTt30qFDB968ecPSpUv5/vvv07XdGo1GGfj/VIBHp9Oh0+lSBQbu3btHx44dOXLkCAcOHKBixYrp1m4h/qtS9jXnz5/Hzc1NqZLj4+ND8eLF6dy581/uJyEhgYiICCZPnoy1tTVubm4EBwcrU2XJNdqfa9y4Md9++y39+/fH2to6o5sj+DDU9r6YmBjGjh3Lrl27GD58OL179yZnzpzK8pTnVmJiIqampv8oGCeEEEIIIf575NZUIYQQQgghRLrRf5F94sQJEhMTMTY25tdff2Xnzp1otVoMDQ3RarVKUKdy5cocOXKEiIgIhg8fzpgxY/jpp5/YvHmzBHeEEJlOygG/hIQEYmJiKF26NL6+vhQvXhxnZ2caNmzI9u3bqV69Ops3b8bT01PZvn79+ixatAgXFxeqVKmSpm3VT5Wlp1KpUg1M+vv7M3bsWC5cuEC/fv2UKbRUKlWqKhxarRZHR0d8fHxITk7mzJkzadpuIcQ7+vOwVatWVKtWTTn3BgwYwJQpU7h69Srx8fF/uR8zMzO6d++uTCM0b948Ce58pocPH/L8+XMmTpzI0qVLefbsWUY36T9Po9EowZ1r165x6NAhtm7dyt27d0lMTATg22+/ZcSIEdSqVYvg4GDmzp3LgwcPlH2knELL1NQUQII7QgghhBDis0jlHSGEEEIIIUS6O3r0KL/99htmZmb07dsXtVpNYGAg/fr1Q6VSKYPCf3bXq0yVJYTIrHx9fbl79y5Pnz6lWrVqDBs2TBlQ1FetuXjxIt999x2JiYns2LGDChUqKOGfhIQEzMzM0mzwPOV+t27dyrlz5zh//jwVKlTg22+/pVy5csq6+il1PlaBRz/AqVKpOHHiBN9++y2DBw9m4sSJMs2OEOkgPj6e0NBQZsyYgYODA25ubmzYsAFfX1/69+9Pnjx5PntfKcOHMlXW57t16xbe3t5ERUUp08jmyJHjo+u+/7zK8/xlpfxsMXbsWBYuXMitW7cAyJ49O+3bt6dz5864u7sD76bQCgwMZN++fQQEBHxQgUcIIYQQQoi/SyLfQgghhBBCiDT1sZBNpUqVqFChAgYGBtjb29OqVSsCAwMB6NevX6r179y5g7W1NZaWlsAfAxUS3BFCZEaPHj3i8uXLbNmyBQB7e3sAJSyjDzgWKVIEPz8/Bg8ezI0bN6hQoYIyiGtmZpZqmy9JXyUNwM/Pj7lz55KYmIiFhQUrV67E3NycuXPn0qlTJwCGDRuGSqUiICCAfv36MXv2bKpVq6a0VaVS8erVK6KiogBwdXWVwWgh0om5uTlDhgwhd+7ceHh4cPbsWdq1a0e/fv3+VnAHSHXeyjn81/QhSGdnZ2bMmEG3bt0YOnQorVq1+mR4R6VSERMTw++//07Lli3lef7C9J8tfH19CQ0NpUaNGgwaNIirV69y/PhxZs+ezfHjx5k0aRLVq1fH3d2doKAgACZOnMjr16/x8fHBzs4uIw9DCCGEEEJ8xeTbbiGEEEIIIUSaSVl6PjY2lj179rBx40Z+/fVX5fEqVaqwYcMGDA0NCQwMZObMmcr2kZGRtGzZksjISOUxGagQQmRm9vb2jB07lh9//BFzc3NOnjxJTExMqnX0RZTz5csHoFQGSA/6vnvMmDFMmjSJtm3bsnfvXh4/fkx4eDhZs2alS5cuREREKFXU/P39CQ4O5sKFC3z//fccO3Ys1T7v3bvH5MmTad68OR4eHul2LEL81+l0OkxNTblx4waJiYmYmJhw4sQJHj9+/MHUeOKfe/+5TExMVEKQDx8+JG/evCxcuJCjR4+SP3/+T+7nwoULVK5cGX9/f2WaMymq/2VFRkYyffp0evbsSXh4OJ6enkybNo2IiAj69evHL7/8wvDhw5Xnv3z58owZM4bixYuzfv16ZZosIYQQQggh/gmpvCOEEEIIIYRIEymrMwQGBrJw4UJ+//13AKysrGjQoAGLFy/G3NxcCfC0bt2agIAAfv/9dxwcHFiwYAE3btygcuXKGXkoQgiRJt6f8kRfqax48eL07duXpKQkli5dyqxZs8iVKxdOTk7AHxV1fv31VwwNDSlQoEC6tvvChQvMmzePpk2b4ufnp/z+nDlzkpycTJ48eahbt64yzZdKpcLf3583b94QFhaGs7Nzqv0VKlSIjRs3UqVKFUCmRRQivej7n6JFizJ69Gh0Oh3Tp0+nY8eOLFiwgEqVKn1yKiyZsunz6fuzkJAQunTpQq5cuQDo378/8fHxhISE4OzsrPSNn3puVSoVHTp0YPny5fz000+ULFlSXoMv7JdffkGn09G7d29cXFyUCkmurq5MnTqV+Ph4Fi1axJ49eyhZsiQAZcuWZd68eeTKlYusWbPKuSGEEEIIIf4xlU7i+UIIIYQQQog05O/vz4QJE2jRogXNmzcnV65chIaGsnPnTooVK8bevXuVaWFOnDhBq1atuHv3LoaGhri5ubF9+/ZUX54LIURmkLJP0+l0vH37Fp1Op0wRCO8qlk2ZMoXly5fTqlUrevbsSb169QBYv349fn5+WFhYEBUVha2tbbq1fdu2bTRp0oTIyEiaNGmCWq1m3bp1+Pv7o1KpOHHiBLa2tsTHx/P69etUU4i8fPmSrFmzKsf/qQCTECJtfCpYoNFo0Ol0zJ49m9GjR5MzZ07mz5+fKsAD7yplOTo6pmeTM4XBgwczdepUunTpwuLFi/Hz82PSpEkMGjSI4cOHkz179s/az5kzZ+jatStarZb9+/djY2OTtg3/j9BXC23YsCG7d+/m/PnzFC5c+IP1Tp48ScOGDcmRIwcnTpxI9Z4N8h4mhBBCCCH+N1J5RwghhBBCCJFmduzYwezZs+nevTvDhg3D1dUVgNu3b7N7927u37+PmZmZsn758uWJiYlh06ZNWFpaUr9+fezt7SW4I4TIVFL2aYsXL2b//v38+uuvmJmZ0b59e2rWrEmpUqUoUaIEgwcPBmDlypXs3LmTMmXK8Pz5c16/fo2RkRFbtmzB1tY2zQYMP7bfuLg4ABwcHADYsGEDQ4cOxcDAgOPHjytBohcvXtC3b1/GjRunDIJaWVmh0+mU438/RCCDnkKknZR9z6tXr3j69ClarZZ8+fIpj3fp0gWVSsXo0aPp1asX8+bNo2rVqsC74F5gYCA+Pj60bds2w47jaxQSEsKZM2dYunQpx44d4/Lly4wcOZJu3bp9dnAHoGTJkgwaNIguXbrw22+/SXjnH3o/xKb/+y9fvjy7du3i0qVLFC5c+IPPIGXLlqVo0aKcPn2ax48ffxDekfcwIYQQQgjxv5DwjhBCCCGEEOJ/kpycjLGx8UeXnT59mjdv3tCzZ09cXV1Rq9WsXbuWMWPG4OzszPHjx8maNSvx8fGYm5sDkCtXLvr27avsQ4I7QojMJGVwZfDgwcyYMQMrKyty587Nr7/+yqlTp6hYsSKenp788MMPSoDH2NiY1atXc+nSJTw8PChbtiyVKlXCxsbmi/WT7wd1EhISlIDlzz//TMWKFQGwtrYG4PDhw/z222/4+voqwZ2UVXZ8fX05ceJEqt8hU4kIkTFS9hPTpk1j/fr1nDhxAgMDA6pVq0bPnj2pWbMmNjY2dO7cGYAxY8bQu3dv/P39efHiBfPmzeP+/fu4u7tn5KF8dfTXyvpqkzdu3KBIkSK0atUKZ2fnz+7D9YGTTp06cevWLWXaJvH3pHy+b9y4gU6nI3/+/MC7cI5KpcLb25ty5cqRJ08eNBoNKpVKeX9MTk4mV65cynuhEEIIIYQQX4pEwYUQQgghhBD/WFRUFP379+f+/fsfLNPpdJw9e5bs2bNToUIF4N00L/7+/gAcO3ZMuVv4ypUrhIaGfvR3SHBHCJGZ6MMrYWFhTJs2DU9PT6Kjo4mNjSUqKooBAwZw8uRJAgMD2bhxIwAlSpTAy8uL77//nsePH/Py5UsqVKiAjY1NqjDQ/0o/MNmyZUtOnjypBHf69+9Pz549iYmJAaB69eqULVuWwMBAvLy8MDQ0TBXc0el0LFq0iOjoaOrVq4ezs/MXaZ8Q4p95PzTo4+PDs2fPaN++PUWKFOHIkSN4eHgwceJEHj58SLZs2ejSpQujR48mMTGRLl26MGDAAOLj4zlx4oQynan4PMbGxmg0Gg4ePEhcXBzW1tZcvHiRKVOmEBcXh6GhIVqt9i/3kzL8OHz4cIyMjFCr1WnZ9EwnZXAnNDSUtm3b0qNHD65fvw5As2bN6NSpE3fu3KFNmzbcunULQ0ND5f1xw4YNnDt3Dnd3dywsLDLsOIQQQgghROYklXeEEEIIIYQQ/8jbt29ZsmQJy5cvx9DQkFGjRpEzZ07g3SARgJmZGS9fvuTkyZNcv34dPz+/j1Zn8PHxIT4+nq5du5IjR44MOR4hhEgPOp2Op0+fsnbtWgoXLoy3tzdOTk4AVKtWjRIlSuDi4sKQIUMIDw+ncuXK5MyZkxIlSjBw4ECSkpKYNm0ar1+/ZsyYMUq/+6WsWrWKzZs3c+jQIc6ePcv06dOZPXs2gwYNIl++fABky5aNDh06MG7cOB49esTatWtT9enLly9n/PjxWFpaMnr0aMzNzT+YokQIkX70597ixYsJCwvDy8uLfv36kT9/fh49esTevXuZOHEiU6dOxdTUlMGDB5MtWza6du1K1apViYyMxMrKirZt25IzZ06pivgPGBoaUrRoUQ4fPkzevHnp2rUry5YtQ61WExYWho2NDVqtFpVK9bf6SiMj+Xr/c70fYps9ezYVKlTA19eX/PnzK9XnlixZQlxcHNu3b6dChQr069ePIkWK8Msvv7B27VqsrKwYM2YMJiYm8t4mhBBCCCG+KJVO/626EEIIIYQQQvxNZ8+eZdq0aSxevJhevXoxevToVAPJO3fupGnTplSvXp1r164BfBDcmTNnDkFBQXh4eBAQECCDEEKITOf9ge5r165RokQJGjduzNq1a9HpdOh0OuXO/ocPH+Lr68vy5ctZt24drVq1UrY9f/48EyZMYOXKlfTq1YuRI0fi6Oj4Rds7adIkhg4diomJCYmJiQQFBdG5c2ecnZ2VwU2NRsOwYcOYOXMmlpaWfPfddxQvXpyjR48SExODlZUVUVFRSoUOGegXIuPoAwY//PAD0dHRHDlyBFdXV+V8VqvVnDx5km7duvH69WvWr1//wdRY+n3I+fx53p+GUE+tVmNkZIROp6NGjRocOnSI9u3bKwEevXPnzmFmZkaBAgXSs9n/CTNmzGDw4MH069eP/v37K1NmwR+vD4CXlxcbNmxQKoxaWFhQokQJVq1a9bemOxNCCCGEEOJzSXhHCCGEEEII8T+5cOECEyZMYPny5R8EeG7evEn//v3ZtWsX5ubmnDt3LtX0KatXr2bEiBFky5aN7du3Y29vn1GHIYQQaSLlAO6dO3fImzcvd+/epXTp0pQqVYo9e/YAfHD3/oYNG2jTpg2DBg1i8uTJqfaTst9duHAh3bp1++JtLVWqFJcuXcLAwICtW7dSs2ZN1Go1hoaGStBIq9WyePFiNm3axPbt2wFwcXGhVq1aBAUF4ejoKIObQvwLaLVaXr9+TdGiRcmaNSvnz5//IFySlJTE3LlzGTBgAN27dyc8PDwDW/x1S9nvbdmyhfv375OYmEizZs1wdHRMFVSvUaMGBw8epH379sybN48sWbKwZcsWfH19adiwISEhIRgbG2fUoWQ6L168oH79+rx9+5YNGzZ8NByVMsBz+vRpLl68SFxcHIUKFcLd3Z3s2bPLe5sQQgghhEgTEt4RQgghhBBC/M/eD/AEBQXh4OAAwP79+/H19eXUqVP06tULd3d3SpUqxaJFi4iMjESlUnH48OFUFR2EECKz6devH3PmzOHs2bM4OTnRqFEjDh8+nKqyjk6nQ6PRYGRkxIMHD3B0dFTCO+87c+YMx44do3fv3l+0ncnJyTx8+BAXFxdKlSrFqVOnsLa25uDBgxQtWlQZsHy/v758+TJarRYnJydMTEwwNjaWwU0h/mVq1arFhQsXiI2Nxc7O7oNz9Pbt25QoUYJSpUqxc+dOzMzMMrC1Xz8/Pz8mTZqk/FyiRAl69uxJz549Uz23+gBPpUqV+Pbbb4mMjCQuLo5Tp07h6uqaEU3PtC5dusQ333yDv78/wcHBn/zs8WfTYcnnFSGEEEIIkVbkKlMIIYQQQgjxP/vmm2/w8/OjU6dOLFiwgFGjRikl5mvWrMnUqVNp3bq1Eu4pX748S5cupUSJEhw9elQpPS9fhAshMouU90qtWbOGtWvX8sMPP2BgYICVlRW9evUCwNfXl7179yrbGBkZodVqWb16NQAlS5b86P5LliypBHe0Wu0Xa7exsTF58uTh1q1bbNu2jYkTJ/Ls2TOqVq3KhQsXMDQ0RKPRfLBdoUKFKFKkCFmyZMHY2BidTifBHSH+JdRqNRqNhhIlSvDw4UOGDRsGoJzP+j4kV65cWFhYkCVLFkxNTTOyyV+90NBQpk2bRosWLQgPD2f48OE8ffqUYcOGERoaSnx8vLLugQMHaNWqFTExMcybN4/s2bNz5swZXF1dP9rfin8uMTERgLt37370s4f++b537x5Hjx796D7k84oQQgghhEgrRn+9ihBCCCGEEEL84f07UfV3n37zzTf4+voCsGDBAgBGjRqFo6MjVatWpWjRojx69IhffvkFgDJlypA3b16srKykOoMQIlNJ2U8+ffqUa9eu4ebmRkhICE5OTgB06tSJa9euMWbMGDp06MCkSZNo1KgR1tbWLF++nHnz5vHNN99Qv379v/x9/8tA4vsVBDQaDTqdjty5cwMwZMgQEhISGDlyJFWqVOHQoUMULVpUWf/QoUM8fPiQ1q1bp9rvpyoWCCHSTsrrqZTntn4KoMGDB7Nu3ToWLlyIo6MjQUFByvo6nY6IiAiePn2Ku7u7nMN/U8rnW6vVcvHiRZo2bcrEiRPJly8fGo2Gli1b0rZtW8aOHYtOp2PQoEGYm5sDsG7dOqKjo1GpVBQtWhQbGxu5Pk4DTk5O5MyZkzNnznD9+nUKFiyoLEsZOvX39+ft27cULVqUbNmyZVRzhRBCCCHEf4xMmyWEEEIIIYT4bCkHEZKTk0lKSkKlUmFhYaGsc/bsWSZPnqxU2QkMDCRXrlyf3OeflaUXQoiv2dChQ4mNjeX+/ftUr16dadOmKXf16/vS4OBgRowYAUDu3LkxNDTkwYMH5M6dm3379uHi4pJmU3Sk7NPXrFnDL7/8wrVr18iePTu9evWiUKFC2NjYpGpn9uzZlQDP1q1b8fHxwd7enm3btmFpafnF2yiE+Dwp+4klS5Zw9OhRdDodxYsXx8vLS1nv559/pmnTpjx58oQ2bdrg7e2NnZ0d27dvZ86cOSQnJ3P48OE/vXYTnzZ27FiSk5OZN28ewcHB9OjRA/jjevfChQu0aNGCO3fuEBAQkCrAk5JMzfTPfeq501eY8vPzY8qUKfTu3Zvp06djbGycar0VK1bg4+NDu3btmDBhwgfLhRBCCCGESCsS3hFCCCGEEEJ8lpRfhM+cOZO9e/dy7do1rKys6NChA7Vr16Zw4cLAhwGeoKAgHBwcMrL5QgiRrt68eYOHhwcrVqzA0NAQDw8Ppk+fDnwYWtyyZQs7duzgyJEj5MqVi9KlS+Pt7Y2Dg0OaVV5I2af7+Pgwffp0TE1NyZ49Ow8fPsTY2BhPT0++//57SpUqBcD48eMJDAzE1NSUihUrEhsbi0aj4dixY7i6un7xNgoh/r4hQ4YQGhqa6rEGDRqwYsUKrK2tATh16hTt27fnypUrGBoaYmhoiE6nw83Nja1bt+Li4iJVX/6B3377jTJlyqBSqTA1NWXZsmXUrl0btVqtVD8CUgV4RowYwYABAz4a4BF/X8q/20OHDvH27Vt0Oh1169ZV3vNOnjxJ//79iYmJoVOnTvTu3Zvy5ctjaGjIkiVLmDhxIiqViv3790uITQghhBBCpCsJ7wghhBBCCCH+Fv2gkI2NDXny5CE2NhadTkeNGjXo378/LVu2BODcuXNMmjSJ5cuX06dPH4YPH46jo2MGt14IIdLPo0ePmDp1KhMmTMDW1pa1a9dSo0YN4F2AR6fTpaoOoB/g1Qdr0mPwfMKECfj7+9OnTx969+5NyZIlOXjwIEOHDuXYsWOMGjUKf39/TExMAJgzZw7Lly/n7t27FCpUiIULF+Lk5CQD/UJkkJRBvNWrV9OnTx/at29Pt27dMDc3Z9SoUWzatInq1auzevVqcubMCbzrnzZv3szp06fRaDSUKlWK1q1bY2dnJ+fzP6TVajl8+DCDBw/m5MmTtG/fnrlz52JpaflBaPPChQu0adOGixcvMm3atFTVkcQ/k/I5DggIYNq0aSQkJKDT6WjTpg29evWidu3aABw+fJiRI0dy8OBBLCwscHV1JTExkVu3bpE7d2727NkjITYhhBBCCJHuJLwjhBBCCCGE+FMpB4UiIyPp2rUrPXv2pFu3bnzzzTdER0ezfv165s+fT6FChRg3bhyNGzcG3g1MTJ48mSVLluDj48P48eNlCgAhRKajHzD82DSA9+/fJzQ0lClTptCsWTOCgoIoUaJEqu30/wc+uZ8v1caUbty4QZMmTciZMyfz58+nQIECwLsptAYNGoShoSGnT58mR44cqSpHPHz4kPj4eGxsbLCyspLBTSEyyPvn9axZs5g1axaRkZG4ubkB787X4OBgZs6cSbVq1VizZo0S4PkYma7p86Ts91MGMdVqNT///DP9+/fn+vXrjBs3jh49epAlS5YPXq/Y2Fj69+/PypUryZs3b0YdSqYzduxYRo0aRY0aNahevTonTpxg//79uLm5MXLkSFq0aIFKpeL8+fMcOXKERYsW8ezZMxwcHKhSpQqenp5pWvlOCCGEEEKIT5HwjhBCCCGEEOKzPHjwgOjoaIKDg4mMjCRfvnzKsri4OBYvXkxAQACNGjVi0aJFytQMsbGxLFq0iEGDBuHk5JRRzRdCiDSRcnDv2bNnJCcnY2ZmRpYsWZTH79+/T0hICDNmzOD777/H39//owGetHDjxg1y5MhB9uzZP/hdR48epUqVKsyYMYN+/fqhVqtZv349Q4cORaVSceLECWxtbUlOTub169dKv55SWrdfCPHO+6GalD97enqyd+9eypUrR8GCBRkxYgQ6nQ6tVouhoSGPHj0iODiYGTNmpArwpOy/5Fz+fO+HOt6+fYuFhYXymiQlJRETE8OPP/7IkydPGDVqFF27dv1ogEcfjJSgyD+X8rl7+/Ytbdq0IWfOnIwcORIXFxceP37Mtm3bGDBgAHnz5iUwMJCWLVsqr0NycjLx8fFYWlqiUqlQqVTyegghhBBCiAwht1EIIYQQQggh/pK3tzfFixdn0qRJ5M+fn3z58imDQgC2tra0b9+e1q1bExkZycGDB5VtS5QoweTJk5VpVYQQIrPQD4wDhIaG0rhxY0qVKkW5cuXw9vZm165dAOTKlQt/f3/69+/PmjVrGD9+PLGxsQBpOlh+8uRJChYsSGBgIC9evEClUin9NsCLFy8AyJ07NwBr167Fz88PlUrF8ePHsbW1Bd5V7qhTpw6XLl364HfIYL8Q6UMf1Nm9e7fys0ajIT4+nlu3bnH58mVWr17N9evX0Wq1Sv+k0+mwt7cnICAAT09PDh48SIcOHbh//36qcIKcy58nZahj4cKFfP/99xQvXpxGjRoxfvx4njx5gomJCe7u7syfP58cOXIQFBTEkiVLePPmjVKtR09f0UyCIn+f/nOF/rmbN28ea9eu5dChQ7Ro0QIXFxcA7OzsaNeuHbNmzeLOnTsEBgayadMmZT9GRkZkzZoVAwMD5TyQ10MIIYQQQmQECe8IIYQQQggh/pKtrS1Pnjzh4sWLJCYmAih3purlzp2bli1bAnDs2DEAZZBYBiaEEJmRfjDdx8eHIUOGcPfuXQoXLgzA7Nmzad68OeHh4QA4ODgwbNgwJcAzceJETp06labts7Ozo0CBAsybN4+QkBCeP3+OgYGB0jfnyJEDAwMDIiIiWLJkCf7+/hgYGHD8+HHs7OyU/UyePJkLFy7w/PnzNG2vEOLj9GGP5s2bU79+fZYuXQq8u64yNzdn8eLFdOzYEYBffvmFp0+fYmhoiFqtVsIi+gDPgAED2L9/P15eXkhB9r9Hp9Mp17KDBw+mT58+HD9+HGdnZ65du8aIESNo0KABd+/exdTUlMqVKxMeHq4EeJYtW8arV68kKPU/2L9/P5MmTQJSf674+eef8fDwIDg4GDs7O6W6nVqtBsDU1JQ2bdooAZ5Ro0axefNmQIJrQgghhBDi30PCO0IIIYQQQoi/NGLECKZNm0Z8fDw7d+5kzZo1AMqAkP6L8apVqwIQHx8PkGp6ByGEyCxSVhE7dOgQq1atwsfHh+joaPbv309MTAxhYWEkJiby448/KgPtDg4ODB8+HG9vbyIiIli4cKHSf6YFJycndu/eTdGiRZk0aRITJkxQAjwA3377LbVr12b9+vX4+vqiUqk4d+6cEtzR6XSsXLmSTZs20bx5c4oXL55mbRVCfJo+XDBq1Cisra0ZNmwYy5YtU5bb2NgwdepUfvjhBy5cuEDz5s3RarUYGRl9EODx9fVl+PDhTJkyRUILf5P++Zo2bRrTp0+nT58+7N69m/379xMbG0vdunX55Zdf6Nq1KzqdDgMDAypVqkR4eDg5c+akX79+bNy4MYOP4uv14sULPD098fPzIzIyMtWyQoUKERYWRnJyMjdv3mTNmjVoNBqMjIyUkJqJiQlt2rRh9uzZPHjwgL59+7Jjx46MOBQhhBBCCCE+Sr5JF0IIIYQQQihSTqeil5SUBICXlxczZswAYMCAAcqX3TqdDiMjI7RarRLqKViwYDq1WAgh0p/+bv+7d+/y9OlTkpKS6NKlizJFh7W1NZ6enixfvhyAoUOHcvz4cQDs7e0ZOnQow4cPx8/PT6lMllacnJzYuHEjpUuXThXg0Rs7diwlS5YkLi6Odu3akSVLFmXZvHnzCAoKwtTUlMmTJ5MlSxap1CFEOrp69Sr37t0D3oUGS5cuTXR0NElJSfTv35+FCxcqy2xsbAgLC6Njx44cPXqU6tWrfzTA4+DgQFBQkExn+g89f/6cNWvWULx4cfr27Yubmxvx8fHs2bOH8+fPU7hwYVatWqU83wYGBlSoUIGwsDCqVq1KrVq1MvoQvlrZsmVj4sSJeHl5KTcM6OXIkYMOHTrg6+uLra0ty5YtIyoqCq1Wm2qqMhMTE1q3bk1ISAjZs2enVKlSGXAkQgghhBBCfJxKJ9+6CCGEEEIIIXg38KMfkL506RIJCQm4ubmlGsgFCAsLY+DAgVhaWhIWFkb9+vWxt7dn8eLFTJ06lYSEBI4ePUrOnDkz4jCEECJdjB8/noCAABo0aICJiQmbNm0C3gUa9QO2AOPGjWP48OFMnz6d/v37K9vrdDpUKlWqvjct3b59m5YtW3L69Gl8fHzw8/Mje/bsvHnzhl27djFy5EguXLhA8eLFKVeuHBcvXiQ2NhZHR0d2796Ni4tLurVVCAE7duygUaNGzJs3j65du2JsbKycg7GxsXz77bds3LiRBg0aAO8C2AYGBjx9+pQBAwawYsUKKleuTHR0NAYGBqjV6jQPC2YWFy9exMnJ6YNrYHh3jfzNN98QFBTEiBEjUKvVbNiwAV9fXwwMDDhx4gS2trYAnD17lnz58mFpaYlWq0WtVmNiYiJ96T+gf8+EPz6zeHt7kz17doKCgpT1nj17xsqVKxk+fDiFCxcmJCSEatWqYWBgkGofycnJJCcnY2FhIa+HEEIIIYT415DKO0IIIYQQQgh0Op3ypXVAQAAVKlSgTJkyfPPNN4SEhHDz5k1lXW9vb8LCwnj9+jU9evSgZs2auLm5ERwcjLm5Ofv27SNnzpxyN7cQIlOzsbHBzc2NnTt3cuTIEc6fPw+8m1bFwMAArVaLVqulXr16GBgYsG3bNuCPKbf0A4jpMWCo0+lwcnJiw4YNSgWekJAQnj17RpYsWWjYsCGbN2+mffv2vH79moiICNRqNR4eHhw8eFCCO0Kks+joaJo2bUqtWrUoVqwYxsbGwLv+QqPRUKJECR49eqQEdwCl38mRIwfTpk2jY8eOHDlyhFq1aikVeMRfu3z5MkWLFqVBgwYkJiZ+sPzt27fAu35Vo9Gwdu1aJbhz/PhxJbgTHx+Pl5eXMk2WgYEBJiYmQPr0+5lNyineDA0NuXHjBhEREYwZM4bJkycry6ytrenQoQNjx47l4sWLDB06lIMHDyoVePRVRo2NjbGwsFD2J4QQQgghxL+BhHeEEEIIIYQQyhfiISEhhISEULJkSTp37oytrS3Dhg1j2LBhnD17Vlnf09OTmTNnAvDy5Uvq16/P8ePH2bt3rwzyCiEyNX0B4x9//JFhw4ZRvHhxnj17xq5du1Ktp6+CUbRoUbJly0a2bNmAtB8k/Nj0h/o+3tnZmfXr11O6dGkmT57MhAkTePbsGWZmZhQoUIAVK1Zw+vRprly5wvHjxxk/fjwODg7SpwuRjp4+fYqXlxcajYZixYpRsWJF4I/gn/5ctLS0BEg1ld37AZ4uXboQHR1N69at0/kovl7GxsbUrl2bHDlyoFarP1heoEABXFxc2LdvHxs2bCAgIEAJ7tjZ2SnrjR49mhMnTpA7d+70bP5/hqurK2vWrKFo0aL4+voyceJEZZk+wBMcHKwEeA4fPqy8LwshhBBCCPFvJVerQgghhBBCCAASExPZtWsXHTt2ZOXKlSxZsoSffvqJHj16sGbNGkaMGJEqwNO3b1+mTJnC/fv32bt3L+fOnSN79uypposRQoiv3fthmJR3/3fp0oWBAwfi7OxMQEAAa9euVSo1GBkZoVarWb58Oc+ePSNfvnxotVrScvZyjUaj9L+nT5/m8OHDbN26lQcPHpCUlAT8EeApVaqUEuB5/vw58C4EYGVlpQw260MCEtwRIv3kyJGDcePGUaxYMcLCwpg2bRrw7jxM2R/p+6KUfRKkDvBMmjQJT09PQkND0639XztXV1eWL19OREQEWbJkISIigtevXwPv3g/Mzc1p2rQphw4domfPngDExsamCu6sWLGCNWvW8N1331G+fPkMOY7MTP8+WrNmTaZPn07hwoUZOnToBwGejh07EhwczLVr1+jZsycxMTEZ1WQhhBBCCCE+i0qXlt8aCSGEEEIIIf613r/7NC4ujgIFCrB+/Xpq166tPJ6cnIy3tzdz586ladOmjBkzhuLFiyvLp02bxqBBg3Bzc2PmzJnUqVMnXY9DCCHSSsqKM1evXiUuLg5DQ0NcXV2VqVEAli5dSmBgIA8ePMDHx4dq1apRpUoVFixYwIIFC3j58iU///wzuXLlSrO2puzTR40axbx583j69ClqtZpChQpRs2ZNxo4di7W1NQC3bt2iVatWnD59Gh8fH4YOHUq2bNnQ6XQfhAGEEOkj5fm3d+9e+vfvz5UrV5g8eTKDBg0CPrx++xR9/6Xfp1qtlqmz/qY1a9bQrl07WrVqxZIlS8iSJQsAV65c4fvvv+fMmTM0b96cjRs38vbtWywsLJg1axZTp04F4MCBA+TJk0cqvqSBlOdKVFQU/fr149KlS4SEhODr66us9/z5c+bPn8/ixYs5cOAAOXPmzKgmCyGEEEII8ZckvCOEEEIIIcR/UMoB6ePHj/Ps2TOcnJzo168fM2bMoGjRoqkGedRqNV5eXkqAZ+zYsRQrVkzZX8oAz+TJk2nSpEmGHJcQQnwpKQdbR44cybx583j8+DEANjY2DBkyhEaNGil94dKlSxk7dizXr18HoFSpUjx8+JDChQuzcOHCdJtScOTIkQQHB1O5cmXq16/P7du3iYqK4urVq5QvX55t27YpwaNbt27RunXr/2PvLgOiSt8+jn9pUUBRVFQEVKy1de11rVXXXrsVW+ygQUFQsVHEwk7ETgwkXGxdu9ZV11rXBFEUiZl5XvjM+Q/GpoCy1+eNMie4zwxznzNz/851c+nSJfr378+UKVOwsLDI0PYJIf7YpwzwiH/n559/xt/fn/Xr19OpUyeWLVumTFd2/vx5Bg0axKlTp7C2tsbGxoZnz57x4MEDihcvzp49e2Qq2Qz2VwM8CQkJGBgYYGZmJu8dIYQQQgjxWZPwjhBCCCGEEP8xul90e3p6Mnv2bGU6FYCFCxcyePBgIP3gkG6A59tvv2XRokWUKVNG2W7u3LmMGTOGcePGMWPGjEw8IiGEyDjaMEyDBg1o1aoVV69eJSYmhlu3btGmTRtcXV2pVasWACtXriQoKIhz584xfvx4hg4dSs6cOTE3N8+wqhe6A8O//fYbzZs3p2HDhowdOxY7Ozs0Gg0JCQl07dqVAwcOUK9ePXbu3Enu3LkBuHv3Lg0aNEClUnHhwgXlcSFE1pEAT+bTfT61UxDq6elx8+ZNJk6cyNq1a98L8Pz666/s3LmTnTt38uTJEwoXLkyjRo1wdHSkQIECEtz5F/7qc/exAM+MGTMYN27cR9cVQgghhBDicyThHSGEEEIIIf6jAgMDcXd35/vvv1cqRISEhGBubs66deto1aoV8H6Ax9HRkf3793P16lWsrKzSLd+/fz/NmjXLsmMSQoh/S3fAMD4+ntq1a9OoUSPc3Nyws7MD4NixYyxcuJC1a9fStWtX/P39KVGiBPA2wDNp0iQePnxIWFgYLVu2zJRB9rCwMOzt7WnXrh2bNm2ibt26AEpoKCUlhSZNmhAbG8uUKVOUqgT6+vr89ttvGBgYYG1tLYObQnwmdN+LkZGRDB8+nJ9//jldKEECPJ+Gbr+/bds2rly5QtOmTalWrRr6+vp/GOABlJBknjx5lMfktfk0fvzxR6pXr46JiclHn893AzyjRo3i0qVLzJ8/Hycnp8xsrhBCCCGEEP+KhHeEEEIIIYT4j3j3DtauXbvy+vVr5s+fT9GiRQEIDg5m5MiRFC9enHnz5tG8eXPg/buRX7x4gaWlpfL4uwMUMmAhhPjSrVq1CjMzM1xcXNi8eTNVq1ZN14/euXMHZ2dndu3axbJly+jRo4ey7dq1a/H29ubx48ds2LCBNm3aZGhbt27dSseOHbGzs0OtVnPixIl0QRxtu2/fvk316tWpVq0a+/bte28/0ncLkfU+FqCTAE/G0H3+PD09WbJkCa9fv2bz5s00btwYY2NjgPcCPMuXLydXrlzy/GeguXPn4uzszIMHD8ifP/8fhkt1l+3fv5+pU6eyYsUK7O3tM7HFQgghhBBC/DvyyUIIIYQQQoj/CO2As6urK25ubty/f5/evXtTtGhRtJn+4cOHExwczK1btxg+fDh79+4FUAI62v1YWlqi0WiUwYp3By1kEEMI8SVbv349ffv2ZcqUKaSlpZEnT573Bmjt7Ozo27cvADNmzCApKUnpJ3v27MmkSZMoVKgQ3bp1Y8OGDRna3mrVqtGvXz+SkpK4d+8e4eHhqNVqZSDTwMCAtLQ0ChUqRLFixTh27Bg3b95U2qslfbcQWUs3gPDLL79w9+5dZVnjxo0JDg6mdOnSuLi4MGvWLODt+zYtLS1L2psdaPs9b29vpk2bRufOnTl8+DDNmzdXgjsAJUqUwMfHh549e7Jp0yb69evH69evpd/MIGq1mqSkJFQqFUFBQenOaR+ip6enfJ5p1qwZ+/btw97eXt4bQgghhBDiiyKfLoQQQgghhPgPuXHjBjNnzmTBggX8/PPPyuMqlUoZxB06dCjBwcH8+uuvDB8+XKnO8O7ghEyrIoTIrrp37067du04e/Ys9+/f55dffkFfX593ixe3aNGC6tWr8/jxY2UQVzfAM3HiRNRqNRERERnWVo1Gg52dHRMmTKB169YYGRmxbNky7t+/r6yjUqkwNDTExMQEIyMjbG1tKViwoAw6C/EZ0Q3uREREMGjQIIKCgkhOTkalUgEfD/AYGhoCkJCQkDWN/wL8UfH5gwcPEhwcTPfu3XF1daVKlSofXO/dAE/79u1JSkrKqCb/p+nr6+Pk5ESxYsU4ePAgKSkpwB+/jroBHhMTE+B/7w0hhBBCCCG+BPItjRBCCCGEEP8hDg4O7Nmzh9y5c/Ps2TMOHz4M/O+L7Q8FeLp06UJ0dHSWtVkIITJTamoqAFu2bKFnz57A26pk2gCPWq1WBtLhbb9pbm6u9KPvBngOHjzIsmXLPknbdH+vlnaw39bWlgkTJtCnTx+OHTvGoEGDuHr1KikpKRgYGKBWq9m0aROnT5+mTJky6aZRFEJkLd3gTlRUFOPHj+fQoUN07doVExOTdEG7dwM8c+fOBWDw4MEEBATw8uXLLDmGz522X/5QJZYzZ87w4sULhg0bhp2d3R/uRxvgad26NWfOnOH169cZ0t7/OpVKRe7cuenZsycnTpxg/fr1wJ/fPCA3FwghhBBCiC+ZhHeEEEIIIYT4D9BoNMqgRfPmzVm5ciVWVlYEBQUxc+ZM4H+VdXQDPDNmzEBPT4/SpUtnTcOFECIDvTttFICRkZFyh//q1avp0aMHN2/eZMiQIVy7dg19fX0l+LJ582bOnz9PpUqVMDU1VfahG+CpW7fuR3/X322r9veGhYUxYcIEFi5cyI8//qisU7RoUcaPH8+AAQM4cOAAPXr0wN3dnUOHDjFixAgmTZpEgQIFmD17Nqampn9YwUAIkTneDe64u7tz8eJFjh8/ztdff82LFy84fPgwN27cULbRDfCMGTOGGjVqsGTJEkDCCx8SFRVFs2bNePLkSbpKLCqVCo1Gw+HDhzEwMKBAgQIf7Be1wcnnz5+jUqkoUaIEQUFBXL16lXz58v3r/v2/SPucfSiUqtFolPPd999/j56eHhs2bOD58+dy3hJCCCGEENmankaueIUQQgghhMh21Gp1uru0dQeGtA4ePEiPHj14+vQps2bNYvTo0cq28L8wT2JiImZmZqhUKqnUIITINnT7tOvXr5OQkEBCQgKNGzd+r7/s1q0bYWFhFCxYEA8PD4oUKcKJEyfYvXs3L1++5OjRoxQtWvSDfe2npjtVDkCuXLlwc3PD29tbeezevXv4+/sTGhrKq1evqFixIubm5pQpU4bx48dja2srfboQn4EPBXcuXbpEdHQ0NWvWJCEhgRUrVjB9+nTc3d0ZOXJkumu8qKgoBg0axJs3b3B0dGTYsGEUKlQoKw/ps9SjRw9CQ0Px8fHBx8cHSP/cu7m5MWPGDA4dOkS9evXSPcfa9VJSUnBzc2PgwIF89dVXyr7fveYWf+yXX37BxsYGU1PTdOehDRs2UKZMGUqWLEmuXLnSvT4jRoxg2bJlxMbGUq1atUw51wohhBBCCJEVJLwjhBBCCCFENqP7RfiWLVs4c+YMx44do0aNGnz99dd07NhRWXf//v307t2bJ0+eMHv2bCXAo1Kp0NfXR09PT7nDVb4kF0JkF7qDrQEBASxfvpzbt2+jUqmoVasWrq6ufPfdd5iZmSnb9O7dm7Vr12JoaEiePHmoUaMGFhYWTJ06NdPCMJs3b2bgwIF06tSJTp06kZCQgKurK7dv32b48OEEBQUp6969exd/f382btxI+fLl2bt3LxYWFsDbaWN0q08IITLfXwnurFmzBi8vL8qWLcvx48c/uu2VK1f44YcfsLGxyZJj+VydOHGCly9f0qBBA4KCgujbty+WlpYkJSVhampKamoqRkZGrFy5kn79+lG9enV27txJwYIFUalU6OnpKeeKqVOn4uPjw65du2jatGkWH9mX6dSpU9SsWRNHR0fmz5+vVKxbtmwZAwcOxMLCgurVq+Pj40OxYsUoUqQIAFu3bqVjx460b9+eVatWkStXrqw8DCGEEEIIITKMhHeEEEIIIYTIRnQHpF1dXQkODsbAwACNRsPr168BGDVqFC4uLhQuXBiAAwcO0KtXL548ecKcOXMYOXJklrVfCCEymu6gt7aKTa1atejQoQNpaWksXLgQU1NThg0bRp8+fTA3N1e27dmzJ+vXr6dBgwasWLECOzs7IOPDMNo2T5gwge3bt7N161YcHBwAOHv2LCNGjODo0aMfDPBMmjSJpUuX0qpVK1auXEnevHkzrJ1CiL/mrwZ3PDw8qFq1KocOHQLS9zW6+0hOTsbExCRrDuYzFRERQbNmzRg0aBBz585Vnp8xY8Zw6NAhDh48mK4//P777zlw4ACdO3cmMDAwXQWjzZs3M2HCBAoWLMi2bdvIkydPZh9OtvDixQsqVqzI3bt3lel5TU1N+fnnn7l16xaLFi1i3759AJQvX54BAwbQo0cPLCws6NChA0ePHuX48ePY2dlJxSMhhBBCCJEtSXhHCCGEEEKIbGjy5MlMmDCBAQMG0L9/f6ysrDh+/Dh+fn5cv36dXr16MXv2bPLlywe8HeDo27cvDx48ICQkhAEDBmTxEQghRMaaP38+Hh4e9OvXj8GDB1O2bFmePXtG9erVuX37NoULF8bNzQ1HR8d0AZ5OnTqxZcsWmjdvTlBQECVKlMiQQUTdSj7aAXvtlDje3t7pqkKcP3+eYcOGfTDAc//+ffz8/JQAz+rVq2XgWYjPRFRUFB4eHly8ePFvBXfEH7ty5QpNmjShVKlSTJw4kW+//RZ42682bdqU6OhomjRpQmhoqBLgefr0KW3atOH48eOULl1a6W+joqLYuXMnenp6HD58GFtbWwmO/APav98XL15Qt25dLl++jJOTE9OnT09XSefw4cPs3buXxYsXExcXh4ODA61atcLa2hp3d3ecnZ2ZPn16Fh6JEEIIIYQQGUfCO0IIIYQQQmQzt2/fplGjRpQsWZIlS5Zga2urLDt58iR+fn6Eh4fj5eWFv7+/smz37t14e3uzc+fOdNsIIUR28+uvv9KzZ09y5sxJUFAQZcuW5cWLF9SoUYMXL17Qt29fQkNDefPmDe7u7jg6OipTTgF07dqVjRs30rJlS4KCgihWrNgnbZ/uwPDy5cs5fvw4efPmJTIyEgcHB1auXImJiUm6aQ11AzyjRo0iMDBQ2d/9+/eZNGkSISEh1KxZk8jISHLmzPlJ2yyE+HtiYmJwdnbmypUrxMTEUKNGDQnu/EvaakRLly5l6NChLFy4kP79+wMQGRlJ48aNSUlJoVevXmzatIlGjRqxceNGJcDz8uVL+vfvz65du0hOTgbAzMyM6tWrs2LFikybIjG7+liAZ+bMmcoUWloXL17kzJkzzJ49m8uXL6NWqwGoXLkymzdvpnjx4llxCEIIIYQQQmQo+dQnhBBCCCFENvPgwQNu377NsGHDlEEGbXWGGjVq4OHhwfHjx5k8eTLff/89devWBaBVq1Y0adIEExMTGZgQQmRrT5484cmTJ8ydO5eyZcvy+vVrGjRoQHx8PLNmzaJNmzbY2tri5OTE4sWL0dfXp3fv3kqAZ8OGDejr67NhwwaePHnCrl27yJ8//ydrnza4o53WS5darebevXs4ODigp6eHRqNBo9FQqVIl5s+fz6hRo5g7dy5mZmb4+/uj0WiwsbHB29ubuLg4DA0NJbgjRCb5UIUWtVqNWq0mIiKCX3/9lejoaAnufCLaacTS0tJIS0tTKkwOHDiQZcuWERUVRYMGDVizZg0qlYqtW7fSuXNnJcBjbm7Oxo0bOXLkCHfu3OHly5dUrFiRcuXKYWFhIdfH/5KhoSFpaWlYWFhw5MgR6taty8KFC9HT01Om0EpNTcXIyIgKFSpQoUIF2rVrR2RkJNu2bWPXrl2cO3eOEydOSHhHCCGEEEJkS1J5RwghhBBCiGzm6NGjfPPNN7i6ujJ16tQPrjN16lQ8PT3ZvHkz7du3V+5U1v4rhBDZ3dGjR6lTpw6pqamMGjWKVatWERAQwODBgzExMeHy5cvUqFEDfX19Xr16xdKlS+nXr58ysAjQrFkzTE1N2b59+ydpk+5Af1RUFF27dqVLly4MGDAAfX19xo8fz86dO2ncuDFhYWFKtQjdvvunn35i0qRJzJ49W6kIpF0eFxf3wW2EEJ+ebtDj5MmTvHz5ksaNGyvLnz59yvPnz3FwcOD58+esXbsWT09PqlSpIsGdf+DYsWNcu3aNvn37cvnyZbp168a9e/f49ttv2bVrFyNHjsTZ2RkbGxsAUlJS6N69O1u3bn2vAs+HyFRZn472PKpbgWfo0KFKgEf73nn3Od+5cyc9evSgZMmS7N+//5OGZoUQQgghhPgcyCcOIYQQQgghvlDa8vHvypcvH3p6emzdupVTp06lW5aamgpAiRIlALh16xbwvzuVZSBXCJGdfKifTEtLA6BOnToAvHr1isOHD1O9enVGjhyJiYkJAIUKFaJAgQJ4e3vTpEkTmjZtCoCRkZGyj/379yvBnU9xb5R2kPLx48dcuXKFXLlyMXz4cCpVqkSFChUIDg5m8ODBREZG0q1bN549ewaQrgJPtWrV2LRpE8WKFVPaqe3bJbgjRObQDe4EBATQuXNnXF1dOXv2LPC2b7KyssLBwYHXr1+zYcMG3NzcJLjzDx08eJC6dety7Ngxnj59Srly5fD19UWtVrNr1y46dOjA+PHjleBOWloaxsbGrF+/nvbt2xMVFUXnzp2Ji4sD/ne9rEuCO//Mh87D2gCshYUFR48epXz58ixYsABnZ2dev379XnBHu482bdrQp08fLl++THx8fOYdhBBCCCGEEJlEPnUIIYQQQgjxBVKpVMoX2vv27SMkJEQZxC1dujQeHh7cuHGDVatWcfv2bWU77ZflZ8+eJVeuXFStWjXT2y6EEJlBt5+8d+8ely5dIikpSQnZqNVqNBoNt27d4tKlSxQpUkTZVqPRsGrVKtLS0mjXrh3h4eHY2NigUqmAt1N/6A5IfsowzMSJEylXrhyHDx+mVatWlC5dGrVajUqlwsbGhvHjxzNkyBAiIiLo3r27Mtisp6entEE74P+xgX8J7giRcTQajRLccXZ2xtfXlwoVKjBr1iyqVKkCpA+C6Ovrc/z4cerWrSvBnX/g559/pm/fvtSvX5+ePXtiZWUFQGxsLC9fvsTc3Jx9+/Zx8eJF4O3ro52+ydjYmNDQUDp06EBUVBTdunXjyZMnyvWy+Hd0z8OnT59m165drFq1imPHjinnUHNzc44cOUL58uVZuHAhLi4uvH79Gn19fWUdfX19JYxau3ZtUlNTOX36dNYclBBCCCGEEBlIwjtCCCGEEEJ8YdRqtTIo5O3tjaOjI0OGDOH8+fPKF9udO3emWbNmLFq0iIkTJxIVFaVsu3nzZjZs2ED58uWVQSQhhMhOdPtJX19f6tSpQ8WKFalevTp+fn78/vvv6Ovro6enR8WKFalZsyaxsbHs3buXhw8fsmrVKhYvXkyJEiUoUqSIsi/tv5B+8P1ThWFSU1PJlSsXJiYmbNy4kWPHjvHs2TP09fUxMDBAo9FQuHBhvL29lQBPr169ePr06Sf5/UKIf0/bH8yZM4cFCxYwbNgw5s6dS4MGDT64fo4cOZgzZw4HDhwAJLjzd/300088fPiQzp078+233wIQFhaGtbU1bm5ueHt7kzt3btq2bcu+ffvShRzT0tIwMjJi/fr1dOnShYiICIYPH/5JKqn91+mehydMmEDr1q1p27Ytffv2pVGjRjRv3pwnT54AHw7wJCUlpQvwGBoakpiYyIULF4C3lUaFEEIIIYTIbvQ08mlECCGEEEKIL5KHhwczZsygf//+9O/fnxo1aqRb/uOPPzJ79mx27tyJqakp3333HfHx8Vy9ehVTU1NiY2Oxs7NLV5ZeCCGyk/HjxzN58mSqVatG0aJFuXbtGteuXaNDhw4EBgZiY2NDSkoK69atY/z48cTHx2Nubk58fDw2NjZERUVhZ2eXqdNMJSYmsmrVKubNm8ezZ89YsmQJrVq1UgbztW158OABAQEBzJ8/n27durF27VqpqCPEZ+LRo0e0atUKlUrF+vXrKVOmjLJs+/bt/PrrryQkJDBixIh0IQS5Jvv7wsLC6NatG0uWLKF///4MGTKEkJAQtmzZQuvWrTE0NGTZsmVMmDCBxMRENm7cSLNmzZTttWGplJQURowYgYeHB/b29ll3QNmMp6cnU6dOpVOnTnTo0AF7e3tmz57Nxo0bKVKkCLGxsRQtWhQDAwNevnxJ3bp1uXTpEj169GDJkiXkyJFD2Vd4eDht2rShU6dOhIaGZuFRCSGEEEIIkTEkvCOEEEIIIcQXaOfOnXTv3p2uXbvi7e2dbpBBd5D5119/ZdeuXcyePZv4+HgKFSpErVq1mDRpkjIFjG4lCSGE+JLp9mlPnz6lXr161KtXjwkTJmBjY8OzZ89wdHRkz549tGnThqCgIGxtbXnx4gXHjh1jxYoVxMXFUapUKTw9PSlcuHCG9ZN/FAh6+fIlq1evZtKkSZibm7Ns2TLq1q2rDOprt71//z4LFixg8ODB2NnZffI2CiH+mRs3blCpUiV69+7NwoULSU1N5cKFCyxYsIAVK1agp6eHRqOhVq1abNu2jYIFC2Z1k79Yv/zyC46Ojpw+fZqGDRty4MABxowZw5gxY7CxsVHWW758OePHj//DAM/Hfhb/zMGDB+nUqRPt2rXD29ub4sWLA7B27Vr69++PhYUFN27cIHfu3Mq59uXLl5QpU4akpCRu3ryJpaWlsj+1Ws2cOXMYO3as8rOE3YQQQgghRHYi4R0hhBBCCCE+Q7qDuh8a4HVzc2PevHkcPXqUypUr/+H2APHx8aSlpWFmZoahoSFGRkYS3BFCZFtbt24lNTWVcePGsXPnTqpWrZquX/zhhx/YuXMnbdq0Ye7cue8FX7QDghnVT+ru98WLF8TFxaHRaChWrJiyjjbAM3HiRPLmzcuSJUvSBXi0bdT+K4PNQnw+rl+/TrNmzXjy5AkLFizg5MmT7N69m4SEBPr27UudOnXYtWsXa9aswdnZmenTp2d1k79okZGRdOnShbi4OFq2bMmKFSuwsrIC0ve3fxTgEZ+eNmhz+PBh6tSpg0qlIiwsDC8vL/T19Tl58iT58uUjKSmJHDlyKOfoxMREXr58SaFChZRz97vnYwnuCCGEEEKI7EiucIUQQgghhPjM6A4wv3nzBj09PdRqNfB2ACI1NZWIiAgsLCwoXrw4KpXqg/sAeP36NQCWlpbkz58fU1NTjIyMACS4I4T4op05c4ZTp07x7j1Ja9asoWPHjoSEhGBtbU25cuWUfjItLQ14O21NmzZt2LlzJ6NGjeLevXsASl+rHRDM6OBOYGAg33//PaVKlaJMmTJ89913hIWF8fTpU8zNzenduzc+Pj7ExcUxcOBAjhw58l4btf9KcEeIzKd9P77bD5UqVYqhQ4eir6+Po6Mjy5Ytw9bWlsOHDzN58mQ6duyoBBjEP6d93iMiIoiLiyNfvnzs27ePH3/8UVluYGCgvE79+vXD398fc3NzevTowc6dO7Os7dnNu+8BjUajTNVbu3ZtADZt2oSHhwcAx48fV6aMu3XrFn5+fqjVatRqNWZmZhQqVAiVSqV8Jnr3fCzvHSGEEEIIkR3JVa4QQgghhBCfGe2X1C1atKB37968evVKqa5gYGCAkZERZcuW5dWrVzx+/DjdoAT8707UFy9eMH36dF68eJFVhyKEEBnit99+o3HjxjRr1oybN2+mW9amTRtatWpFdHQ058+f5+rVqxgYGKCnp4ehoeEHAzy9evXi999/z/DBQO1AMsC4ceNwdXXlxYsX9OrViwoVKnDixAmcnJyYNm0av//+uxLg8fX1JS4uDicnJ6Kjo98bJBVCZD6VSqX0Gb/99htXrlzhypUrPHr0CAAXFxc2b97Mxo0bCQ8PZ//+/ZQrVw5TU1NUKhXbt2/HxMREqaAo7+u/T09Pj+TkZEqUKMGECRPw8/OjRIkSdOnShfXr1yvX1NrraHgb4Jk0aRIvX77E1dWVN2/eZOUhfJF0P3fA/242APj999+Bt6+NqakpSUlJHDlyhE2bNuHm5qZU3MmfP7+yvbOzM3v27OHZs2fpzsNyo4EQQgghhPivkfCOEEIIIYQQnxmNRkNycjLx8fHs2bOHsWPHKgGe1NRUAEqUKMGrV6/w9vbm+fPnyqCEbgn5WbNmMWXKFK5cuZKVhyOEEJ+cmZkZw4cPp1WrVhQsWDDdsty5c7N27Vq6d++OSqXC29ubW7duKcvfDfB8++23nDp1SqlKlpG0g5srVqxg7ty5jBgxgh07drBs2TL27dvHkiVLKFasGHPmzGH+/Pk8f/4cc3Nz+vTpg5+fH1euXMHPz085FwghsoY2UA0wbdo0mjdvTqVKlahSpQpt2rRh1qxZADRt2pSOHTvSsGFDTE1NgbfXeZs3b2blypVUqFBBmbrp3SlSxYe9G3IyMTGhV69eeHh44OTkhJ+fH8WKFaNPnz6EhoYq6+kGeBwdHVm1ahWRkZHkyJEjU9ufHeh+1gCU59DFxYWRI0cqAZ7OnTtjamqKq6srLi4uHwzuLFq0iHPnztG0aVPy5MmTuQcihBBCCCHEZ0ZPI7d1CCGEEEII8VmIi4vD0tJSGbxJSkqic+fOxMTE0K5dOxYsWICZmRkAycnJ1KlTh4sXL+Li4oKzszOWlpbKvjZv3sz48eOxtbVl06ZNWFhYZMkxCSHEp6KdUlD77+vXrzEwMMDExIS5c+fy9ddfU6dOHaUPffHiBf369WPr1q307duXCRMmYGdnp+wvLS1NmWrq6dOnWFlZpQtAZuQxdOvWjejoaI4cOUKJEiWU35uWlsbZs2fp168f8fHxbN68mVq1agGQmJjIli1b+O677yhSpEiGtVEI8de5uLgwa9Ys6tevT/369TEyMiIoKIgnT57Qp08fVqxYkW79tLQ0/Pz8WL16NRqNhtjYWGxtbTO878kudKcdTEhI4OnTp+TNmxdDQ0PMzc2V9TZu3Ii3tze//vorq1evplu3bh/cx4d+Fn+Nq6srM2fOZMCAAYSEhDBu3DgCAwPx9PRk3LhxWFpa8vvvvzNq1Ci2b9+OkZER586do2TJkso+NmzYwPjx4zE3N2fv3r3vhXGFEEIIIYT4r5HwjhBCCCGEEJ+BvXv3Mm/ePCZNmkTVqlWVgYSkpCRl+peffvqJKlWqKMuOHj1K//79+fnnn2nUqBEjR44kd+7chIeHExYWhkaj4fDhwxQtWlQGhYQQX7QP9WHaIExsbCz169enQoUKLF26lK+//jpdgKdPnz7s2LEDR0dHfHx80gV4dAdtM6OfVKvVJCYmUr58eXLlysWVK1fQaDTpfm9KSgohISGMHDlSGfzXHuuH2i2EyBqhoaH069eP/v37M27cOIoVKwbAzJkzcXV1xdLSknv37pEzZ04ALl26RMuWLXn27Bl16tRh2bJlFC1aVN7Pf5FuHz1z5kxCQ0M5e/Ys5ubmlCpVitmzZ/PNN98ofeWmTZvw8vL6YIBH/Hvnzp3D3d2dAwcOULZsWa5evYqnpycDBgzA3t5eWe/06dOMHTuWw4cP06lTJ+rUqUOVKlVYu3Yte/bsQV9fn8OHD2NnZyefV4QQQgghxH+ehHeEEEIIIYTIYpGRkTRp0oS6deuyePFivvrqK+B/g7OvX7/m/Pnz1K5dO912Go2G8+fPM3z4cI4ePao8bmpqSuXKlVm/fj12dnYyKCSEyDY6depE1apV8fDwUB6Lj49n2bJlTJ06laJFixISEvK3AjwZ6d3QjVbTpk25cOEC58+fp2DBgu/10/fv36dixYqUL1+eAwcOyLQuQnyGnJycCAsLIzo6mkqVKpGamsrmzZvx8vLC0NCQY8eOkS9fPpKTkzExMQHeTjNkZmZG586dsbS0lGu0v0i3L3V2diYwMJCaNWvSrFkzfvvtN7Zv386bN2+YNGkSffv2VarwbNq0CW9vb+7du8f8+fPp27dvVh5GtqF9PZ49e0aVKlV4+PAhZcqU4ciRI5ibm5OWloaBgYHymp0/f56goCC2b99OfHw8AObm5tSvX5/58+dLiE0IIYQQQoj/J+EdIYQQQgghstC1a9eoU6cOr1+/ZteuXTRp0iTd8ne/yP7YQPCGDRt48OABiYmJVK9endq1a5MnTx75IlwIkW1cv36dqlWrYm5ujpeXF8OHD1eWJSQksGzZMvz9/bG3t//DAE/79u2ZM2cONjY2Gdpe3QoCcXFxGBsbY2ZmRlpaGm5ubgQGBuLo6Mjy5cuBt/29np6eMn2Wvb09FSpUUCoTCCE+H69fv+bbb79FX1+fkydPolKp2Lx5M66urspj+fPnB95WKLl//z6tWrUC/tc3SJWRv2/lypUMHTqUfv36MXr0aBwcHABwd3dn+vTpVK5cmWPHjmFsbKz0/1u2bGHQoEGYm5tz5coVpRKS+HdUKhXh4eG0bduWokWLcu/ePWUKLe1y3c8gL1++JCEhgZMnT6LRaKhcuTLW1tbkypVLPq8IIYQQQgjx/wyzugFCCCGEEEL8lxkbG9OkSRO2bdvG4sWL+eabbzA1NVWWv/tF9rvBHe2X3V27dn1v32q1Wr4IF0J80V69ekWuXLkAKFWqFDExMXTr1o2AgACSk5MZN24cALlz56Z///4A+Pv7M2jQoHQBHgsLC1avXk2bNm2IjY3N8MFb3YHIkJAQwsPDKVWqFJ6enuTJkwdnZ2e2bNnCypUrKVSoEJMnT1bW12g0rF+/nqdPn1KjRo0PBjaFEJnnY8FpQ0NDJZCwd+9e3Nzc0NfX59SpU1hZWQFvr8WcnJwoW7YsjRs3xtTUVAnsSHDn79u/fz/58uVj0KBBODg4kJyczO7duwkNDaVkyZLs378fExMT1Gq18pp16NABExMTqlatKsGdT8jAwIDWrVuzZ88eTExMmDZtGkuXLiUtLY3ly5djYGCQLpRqZmaGubn5e8FZjUYjn1eEEEIIIYT4f/IpUQghhBBCiCxUvHhxpk2bRs+ePdm6dSvdu3fn5cuXf3l73cFeLe3/ZVBICPElO3DgAB4eHty5cwd427d9/fXXhIaGolar8fPz49atW8oybYBn/Pjx3L59m0GDBnH69GmlTzQ3N2fXrl1cvnyZvHnzolarM6TdusFJZ2dnnJ2duX79OnXr1sXCwgKVSkWhQoXYtGkTBQoUICAggA4dOhAbG8vPP//M3LlzmTJlCkWKFGHIkCES3hEiC+mGQJKTk5XHc+bMScuWLbl69SpeXl54e3ujr6/PiRMnlOAOwNy5c/n555+pVq2aTH/3LyUkJBATE0PNmjWpWLEiqampbN++nbFjx6Kvr8/hw4eVakdHjx5Vzg8ArVq1onDhwqhUqqxq/hfv3XOm9tzavHlzGjVqRGBgIA0bNmTlypX069cPePs5Rft55Pz58/zyyy/v7VfOcUIIIYQQQvyPTJslhBBCCCHEZ+D27dv4+fmxcuVKfvjhB1atWoW5uXlWN0sIIbLEwYMHadq0KY0bNyYkJIRixYoB/6uAceLECRISEmjatKmyjXbZu1NoLVmyhKpVq6YLNGbGdDUBAQGMHz+e4cOHM2TIEMqUKfNeWy9evEi3bt24cuWKMsipp6dHyZIl2bVrF/b29jKdiBCfAXd3d9RqNV5eXuTOnRuAqKgoevbsycOHD8mfPz8PHjxI917duHEjHh4eWFtbs2PHjnShHvH3JSUlUb58eYoUKcKWLVv48ccfleCO7jRlAFWqVKFSpUosXboUQ0MpPP9v6Z6HYmNj+eWXX4iPj6dgwYJ06tQJAwMDDA0NuXLlCiNHjiQqKoo+ffqwYsUKAMLDw5kwYQJVqlRhwYIFGBkZZeXhCCGEEEII8dmS8I4QQgghhBCfCQnwCCHE27vzq1evTv78+dm8eTO1a9dOt/zd4I3ulDbvBngCAgIwNTVl586dVK5cOdOO4caNGzRr1oxixYqxdOlS7O3t32ur9jiePXvGtm3bOH/+PGlpaVSuXJn27duTP39+Ce4I8Rm4e/cunTt35uTJk/j5+TF8+HDy5MkDwLx583Bzc+PNmzesWrUKBwcHihcvTlBQEOvWrUOlUnHkyBFsbW0zJTSY3XXv3p3w8HAGDx5MWFgYenp67wV3pk2bxrRp05g5c6ZSAUb8c7p/t56engQHB5OYmKgsr1OnDmPHjqVZs2bkypWLa9euMWLECCIjI2ndujXFixdn165dPHv2jLNnzyrnQyGEEEIIIcT7JLwjhBBCCCHEZ0QCPEKI/7qIiAg8PDw4d+4cU6dOxdnZOV3o5c/oBniCgoIIDQ0lKioKa2vrT9bGPxuE11YOWrlyJb179/5o+/8onCMD/UJ8Po4ePcqUKVMIDw/H19eX4cOHkzdvXgBCQkKYOnUqt2/fVtY3NDSkZs2arF27Fjs7Owni/Uva/jAqKgpHR0fu379PwYIFOX36NEWKFFHW27BhA+PHj6dIkSJs3bpVeY3Ev+fj44O/vz+9evVi0KBBFC5cmB07duDv74+JiQlz5syhY8eO6Ovrc/36dXx8fNi9ezepqamUL1+ezZs3SzU5IYQQQggh/oTUDRVCCCGEEOIzYm9vz4QJEwBYuXIlffr0kQCPEOI/pUmTJhgZGeHp6YmrqytpaWm4u7v/5e319PTQaDTkzp2b0aNHM3r0aMzNzT/pgKE2VBMREUGTJk2Ux7Uhnfv37wNvp3nRtkmXti2PHj1CX18/XbBIuw8J7giR+d4N2mlDI3Xq1MHT0xOVSoWvry+AEuAZNGgQtWvX5sqVK5w9e5acOXNSp04dvv76a/LkySNhhT/w8OHDvxSs1PaHNWrUoG/fvixZsgRTU1NOnTpFQkIC1tbWzJ8/n6VLlwKwZs0a8ubNKyHIf+BDYdOjR4+yZMkS2rdvz/jx43FwcAAgf/78pKWlYWZmRrNmzdDX10ej0VCqVCnmzp3LmDFjePbsGTVr1iRv3rzyXhBCCCGEEOJPSHhHCCGEEEKILKL75fizZ8/Ily8fIAEeIcR/l7ZfbNCgAZMnT8bLywtPT0/09PRwc3P7y/vR9q3aflOj0XzyAUMXFxdmzZrFqlWr6NWrV7rfW7x4cQDOnTun/H7tct22jBs3jipVqjB27FgMDQ3T7UMIkbE+FtTRpa+vny7AM378eAB8fX3R09Nj2LBh5M2blwoVKlChQgW6dOmSbnu1Wi1hhY84deoUNWvWZP78+Tg5Of3p+hqNBjMzM0aPHo2JiQkhISG0b9+eHDlyKMsrVapEWFgYRYsWlaDI36T9LKI9T+m+N3755RceP35M3759cXBwIC0tjc2bN+Pl5YWVlRUnTpwgd+7cpKamoqenh6GhIQUKFKBAgQLKPuS9IIQQQgghxJ+TWw+EEEIIIYTIArpfikdFReHh4cHevXuVZdoAj6OjI9u3b6dPnz68fPkyK5sshBAZTjtoCNCwYUMmT55MrVq18PDwYNq0af9qv5+KWq0GoHnz5jRu3Jj+/fuzdu3adOuUKlWKmjVrsnjxYrZu3Yqent57A6JLliwhPDwcIyMjqQwhRBbQXldp+xzt+9DFxYWJEycq62kDPIAS4KlXrx4+Pj6EhIQQFxenrKvdl+624sN+//13bG1tGT16NMuWLfvT9bV9qKWlJWPGjGHfvn14eXnRs2dP+vfvz5o1awgPD5dpyv6B6OhoGjVqxLZt24D052KAixcvoq+vT7Vq1QDYuHEjbm5u6Onpcfz4caysrAC4dOnSRyvlyXtBCCGEEEKIPydXzUIIIYQQQmSyd4M7zs7OrF+/Hnt7e+B/g8zvBnh69+5NSkoKAEFBQURGRmZJ+4UQIiNlVIDn3zp9+jS3bt1SBiAbNWqEr68vDRs2pHfv3qxYsUJZ19ramoEDBwLQsWNHVq9eTUJCgtK/b9iwgcDAQIoXL06PHj1kUFOITBYdHU358uWJiYlR+hyNRsPNmzfZvn07EydOZPbs2cr6xHtzYwABAABJREFU7wZ4Ro8eDYCnpyeLFy/m+fPngFTO+jvatGlDcHAwpUqVYuDAgX8rwGNqakqZMmXw9/cnJCSEefPm0bFjR2WqLAnu/D03btzg4sWLTJo0id27dwNvn2uVSgVAyZIlSUtLY9u2bezYsQMPDw/09fU5efIk+fPnV/Yzbdo0li9fzp07d7LkOIQQQgghhPjSybRZQgghhBBCZKJ3gzvu7u5cv36dgwcPUrZsWV69esXTp0+xs7MD3p9Cy9HRERMTE1atWsXy5ctJTU3FyMgoy45HCCEygm6VGm2Ax8vLCw8PD4C/NYXWpxAREUHLli0ZOXIkkyZNwsTEBD09PerWrYunpyevX7/G0tIS+F8/369fP168eIG7uzuOjo7UqVOHsmXLcufOHU6fPo25uTnh4eEUKFDgg9P1CCEyzrFjx7h//z79+vVj9erVfPPNNwCUKFGCRYsWMWHCBJydnVGr1Tg7OwPpp9Bq164dDRs25Pbt23h5eZEjRw7GjBmTlYf0Rbh+/TpmZmYULlwYgFatWqFWq/Hy8lICj/379//DfWivo9+d2klL+tK/b+DAgejr6zNw4EA8PT2Bt6+NNgRVp04djIyMmDJlChqNBiMjI86ePUuePHmUfSxdupTDhw/To0cPrK2ts+IwhBBCCCGE+OLpad6t5yqEEEIIIYTIEB8K7ly6dIno6Ghq1qzJ8+fPWbduHTt37sTX15fatWsr29y+fRt/f39WrFhBzpw5GTFiBCNHjqRQoUJZfFRCCPHp6PaTSUlJmJqaKsuio6Px8vLi+PHjBAQEZFqAJyIigmbNmlGrVi0WLFhA5cqV32trXFwcefPmVbbRDeOEhYWxc+dOduzYwZs3b7C3t+ebb75h8uTJFClSRKZ3ESKLTJ06FU9PT4oUKUJoaKgS4AGIjIzEy8uLkydPMn36dCXAo33fp6SkUKZMGb7//ntevXqFv78/tra2WXUoX4QLFy5QuXJlevXqRUBAgBLgAdi5cydeXl5cvnyZJUuW/GmAR2SMJUuWMHjwYMqXL8+UKVNo1aqVsmz27Nl4eHiQmprKhg0b6Ny5s7Js9erVTJ48GWNjYyIiIrC2tv5ouEoIIYQQQgjxcVJ5RwghhBBCiEzwZ8GdhIQE1q1bh7OzM7Vq1aJ27dpA+im0PD09MTMzI3/+/PTt21eCO0KIbEW3nzx06BCRkZH06NGD0qVLA3y0Ak9Ghl+SkpIICQkB3k4boh1s1rZV+69ucAfSV+jo0qULnTt35u7duyQmJlK4cGFy5cqFsbGxBHeEyALa9527uztqtRpvb2+6deuWLsDTuHFjALy8vHB1dUWtVjNq1ChMTEwACA0NxcTEhNGjR1OiRAkMDAzk/fwnbGxsqFOnDmvXriVnzpyMHz9e6VPbtGkD8Lcq8IhPT/vcDx48GE9PTzQaDa1btwagXbt2PH78mFmzZuHh4cHhw4cpV64cBw8eJDIyEgsLC/bv34+1tbW8F4QQQgghhPiHpPKOEEIIIYQQGeyvBHfWrFmDh4cHX3/9NdHR0QDpvvjW7iM+Ph5AmZ5FCCGyg3f7yXHjxvHbb79x7NgxSpQokW7dmJgYvLy8OHbsGJMmTVKm+NiwYQMVK1bkq6+++qRtu3btGh4eHuzZs4dx48bh5OT0lyts/FHlAalKIETW0b3GmjJlCt7e3h+twOPt7c2JEyfo3bs3DRo04O7du6xYsQILCwtiYmLkmuxviIuLo3v37hw4cIDBgwenC/CAVOD5XOhW4Jk0aZISrvrtt98IDw/H29ub+Ph40tLSsLW15ZtvvmHatGlSTU4IIYQQQoh/ScI7QgghhBBCZKC/E9ypWrUqhw4dAiAtLQ1DQymUKYTI/j7UT165coWDBw9Sq1YtkpKSSE5OJk+ePMo2ugGeoKAgzp8/z7Jly9ixYwctWrT45AOHN27cYOTIkURERDB27FhGjBiBjY3NJ/0dQojMpTu93R8FeH788UeCg4PZuXMnKSkpADg4OBAREYGdnV26/Yg/Fx8fT/fu3dm/fz+DBg1i/PjxFClSRFm+a9cuvLy8uHTpEiEhIQwYMCALW5t9vVtBDtK/J5YuXcqgQYMoX748/v7+tG3bVtn23r17PH/+nAcPHlC5cmUsLCwwNTWV4I4QQgghhBD/koR3hBBCCCGEyAQHDx7Ew8ODq1evEhkZKcEdIYTg4wHHmJgYatSowfPnzwkLC+PEiRP4+Phga2urrK8b4DExMWHkyJGMGTMGa2vrDGnrjRs3GDVqFAcOHJAAjxBfMN1+JzU1FSMjI+CPAzwPHz7k8uXLxMbGYmdnR4sWLShYsKCEFf4hCfBkLd2/28ePH5OSkoKhoSFWVlbpPoPoBnh0K/B8iFSTE0IIIYQQ4t+T8I4QQgghhBCfyLtfWmvvXr137x5jxoxh586dxMbGSnBHCCH4a5XJ1q5dy9ixY2nUqBF79+59b7uIiAhCQkIoW7YsQ4YMSTf9SkaQAI8QX6Z3q+PoXm/p9ikfC/B8qLqOBHf+nb8a4Ll27RqzZs1ixIgRWdja7EP3b3nGjBls2LCBu3fvYmxszHfffUfr1q3p2LGjsr5ugGfy5Mm0bt0akLCOEEIIIYQQGUHCO0IIIYQQQnwCul+E37t3j4IFC2JsbAy8/XI7ODiY+vXrU7FiRZ4/f866detwd3eX4I4Q4j/pr04p6OnpSdWqVYmJiQH+19fqbn/z5k0sLCzInz9/prRdAjxCfFl0Qzbr168nOjqaW7duUbRoUcaMGYODgwO5cuVS1v9QgEemxvpnPhbw0F7zxsXF0aNHj48GeHbv3s3gwYPR09Pj559/Tvc6iX/H1dWVmTNnUqJECb766ivu3r3L+fPnAQgICMDNzU1ZVxvgqVy5Mp6enunCPUIIIYQQQohPR8I7QgghhBBC/Eu6g0Lz589n06ZN2NnZsWzZMvT19dMN9rx8+ZIVK1bg6enJ119/rQxIS3BHCPFf8VeDO39WmSwz7vp/93dof5YAjxBfBt3QjYuLC4GBgZiammJtbc2DBw/InTs3rq6u9OzZEysrK2U7bYDH3t6eZcuW0bBhw6w6hC+W7vXx8+fPSUhIwMjISKmQpu1P/yzAExERQfny5SlUqFCWHEd2oft6HD9+nC5dutC1a1ecnJywt7cnMTGRXbt20adPH9LS0pg1axZjxoxRtl++fDkDBgygUaNG7N69mxw5cmTVoQghhBBCCJFtyS0jQgghhBBC/AtqtVr5ItzFxQU3NzeSk5P54YcfMDQ0fO8u7Vu3brFixQoqV64swR0hxH+SNgxz8OBB3N3duXz58t8O7ujuJ6PoBnfOnj2b7nc6ODgwd+5cmjZtyuzZs5k3bx73798H3g6QCiE+D9rrsIkTJzJnzhwGDBjAkSNH+OWXX9iyZQsPHz5k9uzZhISE8OzZM2U7T09PpkyZwu3bt3FzcyMlJQW5//Gv0w2KBAYG8v3331OqVClq1KiBo6Mjz549U/rTvHnzsm7dOpo1a0ZISAj+/v48ePBA2VeTJk0oVKiQ9K3/kvb1uH79Oi9evCAxMZGePXtib28PQK5cuejWrRs7d+4EwMfHh+joaGX7fv36sW7dOpYvXy7BHSGEEEIIITKIVN4RQgghhBDiE/D398fPzw8nJyecnJwoW7bsB9fTaDRs2rSJzp07AxLcEUJkbx+rXHP9+nWGDRtGdHQ0R48epUaNGn8ruJOZ7Y6KiqJ///6ULVuW8PDwdJU83q3Ao61gAPDkyZNMm8pLCPFxBw4cYODAgTRp0gR3d3ccHBxISkqiatWqxMXFYWpqSnx8PG5ubgwaNChdBZ558+bRtm1bbG1ts/AIviy6feS4ceOYO3cupUuXpnHjxty5c4ddu3bRuHFjZsyYQaVKlZS+VluB58CBA3Tt2pVZs2ZhbW2dlYeS7cyaNQsXFxcaN26Mvr4++/fvB1CCadrXYtGiRQwdOpRJkybh6en53rlcN5wlhBBCCCGE+HSk8o4QQgghhBD/0qlTpwgODqZdu3aMHTs2XXDn559/5uTJkzx8+BC1Wo2enp4S3FGpVBLcEUJkW9o+D94OygLpKi2UK1eOkydPKsGdtWvXfpbBHQ8PD54+fYqnpyfwv0oeGo3mvQo8S5YsISkpiVGjRtG1a1fi4+Mzte1CiPRSUlI4cuQIGo2GAQMG4ODgQGJiItWqVSM+Pp4ZM2awaNEirKysWLBgASEhITx9+lTZfsSIEdja2pKWlpaFR/Fl0faRs2bNYuHChQwfPpywsDCCgoIIDAykQIECREZGMmzYMM6dO6cER/Lmzcv69eupUaMGUVFRmJiYZOVhZEs2NjZUqlSJQ4cOcfz4cU6fPg28PTfr6emh0WhQqVTUr18fU1NT9uzZQ2pq6ntVpyS4I4QQQgghRMaQ8I4QQgghhBD/0q1bt3jy5AldunTB3t4etVrNw4cP8fDwoG7dutSqVYtq1aqxdu1a0tLSUKvVgHzxLYTIvlQqlTKAu2zZMgYMGMCMGTOAt6EXKysrZsyYQdWqVXnx4gUrV67Ew8ODatWqfVbBHXd3dy5evEhkZCTffPMN8fHxbN++HUg/hdacOXOUAE+1atUIDg6mdu3aMs2OEJns3amVjI2NKVasGL6+vtSqVYs3b97QqVMnHj9+zOTJk+nVqxfff/891apV48GDByxatIjAwMD3gncStv57zp07x9q1a2nZsiVDhgyhfPnyJCQk0KpVK/T09OjQoQMnT55k1KhRnD17Vrk2trS0ZO/evVy4cAFLS0vpQz8R7fPYpUsXxo8fT9WqVXn58iW7du1K957RVtQpU6YMFhYWWFlZYWRk9N40wEIIIYQQQoiMIVfeQgghhBBC/EvaAYcbN27w/Plz5s+fT+vWrZk5cyY1a9akd+/eqNVqPD09efDggXwBLoTI1tRqtRJOdHd3Z8yYMdy6dQs7Ozvgf6EX7WD42bNnCQwMpHz58sTExABZH9yJjIzE3d2dS5cuER0drVQHWr9+Pe3bt2fMmDHKNgAlS5Zk7ty5NGjQgJs3b+Lm5sawYcPImzdvph6DEP912r5nypQpbNu2DQBHR0d69eoFwI4dOzhy5Ai9evWie/fuynu+WbNmVKtWDX19fcLCwiRg/S/duHGD8+fPM2zYMMqUKcOrV6/49ttviYuLY+7cucycOZMffviBw4cP4+bmxoULF5T+NE+ePOTPnz9d9Tbx92g/m2jpPo/t27fHzc2N8uXL4+/vz5IlS3j16hXw9ryclpbGypUrefToESVKlEClUkmISgghhBBCiEyip5GrbyGEEEIIIf4S3YFdtVqthHAuXLhAr169uHjxIvr6+qjVakqUKMHChQupVq0alpaWuLm5MWPGDEJDQ+nSpUtWHoYQQmSKCRMmMHnyZAYPHszQoUMpX778B9d78OABYWFhSiAmq4M72oo72uBOzZo1SUhIYM2aNXh5eVGtWjWioqI+uJ+bN29y8uRJ6tati62tbWYeghDi/506dYqaNWvi4uLCtGnT0r2/vb29CQgI4NixY9SoUUPZplWrVqSmphIUFISlpSUFChRIt534+/bs2UPLli1JSUnB0dGR8PBwAgIC6Nu3Lzly5GDfvn20adMGfX19bGxs2LFjB+XKlcvqZn/xtNVzAGJjY3ny5AnFixfHxsYGKysrZb3t27fj7e3NlStXGDFiBPXr16dx48YsWbKENWvW8Pz5c44dO0bhwoWz6lCEEEIIIYT4z5Gar0IIIYQQQvwFul+EA7x584acOXMCULFiRYKDgzlw4ACPHj2iUqVK9OzZk9y5cyvrx8XFUaRIEapUqZLpbRdCiMwWERFBcHAwffr0wc3NTam6A/Do0SNev35NsWLFAChcuLAS3FGpVJ9tcMfDw4OqVasqwZ0PhYxKlCiBvb29VO0QIgs5ODhQr149Fi9eTJ8+ffjqq6+UZQYGBmg0Gi5cuEC1atUwMDBg48aN/Pzzz/Tt25fSpUsD6UPa4u/RXjO3bNkSgHv37hETE8N3333HoEGDlP7R0tKS3Llz07ZtWw4fPky+fPmystnZgkajUZ5f7Y0D8Pbvvk6dOgQHB1OhQgUAfvjhBwB8fX2ZN28e8+bNo3Llyjx9+pSvvvqKHTt2ULhw4fc+AwkhhBBCCCEyjoR3hBBCCCGE+BO6X1ovX76cAwcOcPz4cSpWrEiVKlWYOHEi9erVo3bt2h8cdN64cSP79++natWqcveqEOI/4dKlSzx//pyePXtiZ2eHRqPh1atXBAYGsnbtWm7evEnjxo2ZMGECdevWVQbKs2KAUBvciY6O/tPgzqFDh4A/rg4kg5xCZC1LS0vatGlDbGws69atw8/PD3j73mzTpg1hYWFMmDCBCxcu8OLFCyIiIjAxMcHR0VHZhwR3/rl3+8Br167x8OFDGjZsmG7Z7t27sbW1xdfXlzx58mBmZiahqX9Jez5bsGABQUFB/PDDDzRp0oTDhw8TGhpK/fr12bdvn1J16ocffkCtVjNz5kxOnDhBo0aN8PLywtjYmFy5cmVJJTwhhBBCCCH+y+TTkBBCCCGEEH9A9w7WcePG4eTkxLFjxyhZsiSXL1/G39+fxo0b89tvv33wy+2goCA8PT0xNDQkODgYMzMzZOZaIUR29/z5cwAMDQ1JTU1lzZo1tGzZEh8fH/LkycM333xDVFQUPj4+n8VgbVRUFCNGjODatWv/KrgjhMg8H7qe0j42atQoqlevzsaNG3n58qVyLVexYkV8fX0pUaIEwcHBbNmyhWLFihETE6NUGRGfVsmSJcmbNy9hYWEkJSWRkpJCWFgYYWFh2NnZUbBgQeX6OKvPBV86tVqNWq0mJiaGxo0bM2fOHJycnFi3bh0BAQG8evWKBg0acPLkSWWb9u3bM27cOMqXL8/s2bPZvXs3uXLlQq1Wy7lOCCGEEEKITCafiIQQQgghhPgD2jtYAwMDCQoKYuDAgezbt4+IiAhiY2Np1qwZ0dHRuLq6KgNGKpWK2NhYGjVqxIQJE8idOzfR0dEULVoUlUql7FMIIbIT3YH0atWqYW5uTuPGjbGzs6Nv3778+uuvbN68mV27dhEREUHz5s2JiorixIkTmdI+tVr9wcdVKhUPHz7k7t277N+/X4I7QnwB1Gq1cj2VmJioPK6np6dca7Vr146bN28qUwcBGBkZ0blzZw4ePMjBgwc5ceIEe/bswdbWVqYH+oh3+86UlBTl/38l7GRtbU3r1q05fPgwVapUoVatWgwcOJDU1FTmzJmDkZERgFwf/0O6r4++vj56enrExcXRq1cvbG1tldfLzc2NadOmoVKp3gvwdOjQAT8/P8qWLUufPn1YuXKlBKmEEEIIIYTIAnoaue1XCCGEEEKIP5SQkMB3332Hnp4eq1evpkyZMrx584bIyEgGDx6MmZkZsbGx5M+fX9lm165dTJs2jYYNGzJixAgKFCggg0JCiGzl3Yo5Go0m3eDrsmXL2Lt3L69evaJ69eqMHj2avHnzKsvbtm3L9evXOXLkSLrHM4Ju/xsTE4O+vj7ffvutsjwhIYGkpCSsra15/vw569evx83NTYI7QnzmfHx8uH79OkOGDKF+/frplv3+++/KlKX79+/Hysrqo9din0MFsM+Rbr9+7tw5KleurCybPXs2Dg4OtGzZ8qPXt9rtHz9+zLx58wgPDycxMZFKlSoxe/ZsbGxs5Pr4X9B97vbs2cOdO3cwNzdn8uTJjBs3joEDB7633pw5c3Bzc8PAwIBDhw5RvXp1ZX87duxg/PjxXL58maCgIIYNG5b5ByWEEEIIIcR/mIR3hBBCCCGE+BPXrl3jq6++Ytq0abi4uJCamsrWrVtxdXVFX1+fU6dOKQNCly9fpmLFigA8efKE3LlzY2xsLINCQohsRXcgcOPGjZw8eZKffvqJWrVqUbVqVTp16gTA69evMTIywtDQUBkA1mg0hIWFMXr0aJo2bUpISAg5cuTIlLb6+/sTHBzMy5cvOXv2LCVLlkzXN6ekpLBkyRJGjBhBgwYNiIqKAiS4I8Tn6Nq1a/Tq1YuffvoJPT09+vTpQ7NmzejSpYuyztSpU/H09GThwoUMHjw4C1v7ZatduzY///wzO3bsoF69eowcOZLg4GClKqWJiclHt9VeA6vValJSUkhNTcXExARjY2MJ7nwirq6uzJw5M91jI0aMYPr06cpr826Ax9vbm9evX3PmzJl0oawdO3YwYMAAmjdvzurVqzPtGIQQQgghhBAybZYQQgghhBB/KjU1FYA3b96QlpaWLrhz8uRJrKysgLeDu4MHD2b58uUA5M+fH2NjYwAJ7gghsg21Wq0MALq4uNC3b1+WLl3K/fv3mTt3Ll26dMHDwwONRkPOnDkxMjJKV5EnJCSECRMmYGZmRkBAADly5CCj7ivSaDRKW52dnZk0aRJNmzZl586dlC5d+r2+WaPRoNFoJLgjxBegdOnSnDp1itDQUNq3b09oaCjdunWjRYsWbNu2jWfPntG1a1fMzc1ZuHAhN27cyOomf7EaN27M8+fPGTZsGO3btyc4OBhnZ2fatGnzh8Ed+N81sL6+Pjly5MDc3BxjY+N0/bP4e3TPmWvWrGH+/Pn07NmTPXv24OfnR5EiRZg3bx7Lli1T1jMwMFCmORs9ejSenp7Y29uTL1++dPts27Yt+/fvl+COEEIIIYQQWUAq7wghhBBCCPH/Pnb37/PnzylVqhRVqlShW7du+Pj4KMEd3amyXF1dCQkJYcuWLTRu3Dgzmy6EEJkuICAAb29vBg0axIABA6hWrRqRkZGMGjWKK1euMHnyZDw8PIC3Icjz58/j7+/P0aNHKViwIHv27MHOzi5TKi/Mnz+fMWPG4OTkxMiRIylRosRH133x4gUWFhaABHeE+JzpTun06tUrLl26xOTJkzl+/DhPnz6lYsWKBAYGsmjRIvbt28e6deto1apVFrf6y6L7HIeEhDBkyBD09PTo2LEjK1asIGfOnFJdMpPpvibJyck4Oztz4cIFVq5cSbFixQDYtm0bvr6+XLx4kUWLFjFo0CBle91z7suXLzE3N1cee/e1lNdWCCGEEEKIzCXfQAkhhBBCCPH/tF9kr1ixgrJly1KrVi0A8uTJg6OjIzNnzuTo0aNYWVkpU2VprV27li1btlC/fn1q1KiRJe0XQojMcufOHZYvX07jxo0ZO3YsJUuWJC0tjeTkZJ4+fUqxYsUYMGCAsr6RkREHDhzg6tWrdO/eHQ8PD6ytrTMluJOYmMimTZsoV64co0ePVgY3AbZu3cqZM2cwNTWldu3aNGrUSAnuaDQaCe4I8RnT09NTggw5c+akZs2arF27lnv37hEYGMiGDRto2rQp+fPn5+XLl8yZM4eWLVsq24o/p6enpwQ4EhMTgbd945kzZzh37hx16tRBX18/XaBEZCzt8+zq6srDhw95+vQpHTt2pFixYqSkpGBsbEy7du0wMjLCy8uLIUOGACgBHt2Qjrm5eboKSO8GdSS4I4QQQgghROaSyjtCCCGEEELoiIyMpEmTJjRt2pTJkydTrVo1AE6fPs2oUaM4ceIEvXr1Yv78+RgaGmJkZMTChQuZPXs2ADExMdjY2MgghhAiWzty5Aj16tVj7dq1dO/endTUVDZv3oy7uzv6+vpKwDE5OZkHDx4ogZkbN25gY2NDjhw5MiW4A3D79m0cHBxo1aoV27dvB+D48eMsWLCAtWvXKuvZ29szb948ZXBfCPFlO3r0KIcOHcLX1xd7e3v279+Pvb19Vjfri6RSqYiIiODWrVvcv3+fqVOnUrp0aYKDg5Vqk9qvmOX6N+M9evSIfv36sXfvXuDttJDTp08H0lfW2b17N15eXly8eJGQkJB0oVohhBBCCCHE50fCO0IIIYQQQuh48OABISEhzJgxg/r16zNx4kSqV68OQFRUFD4+Phw5coTChQtTrFgxnj59yt27d7GzsyM8PBx7e/tMG5AWQoissm/fPlq0aMGWLVto164dYWFhuLq6vjel4PPnz+nUqROenp40bNhQ2T4zA44ajYZatWpx9+5dnJycuH37Nvv37ycpKYmRI0dSq1Ytbt26xfDhw3FzcyMgICBT2iWEeN+n6BvevQ67cOEChQsXxsrKSqbC+4u0r4Pu65GSkoK+vj6GhobMmDEDNzc3Spcuzfz582nUqFG6dZ88eUKePHkwMjLKysPI1s6dO0dwcDDr1q2jTp06LFq0iJIlSwLvB3h8fHw4e/Ysa9asoUePHlnZbCGEEEIIIcQfkE+rQgghhBBC6ChcuDBOTk7o6+szefJkAHx9falRowaNGjUif/78xMbGsnz5cp4+fUrhwoXp1asX/fv3p2DBghLcEUJkKx/r0woUKADA4cOHSUlJ+WBwB8DNzY3z58+TO3fudNtnRHBHOw3Ihx739PTE09MTX19fcufOTZUqVVi4cCElSpTA0NCQX3/9FUNDQ37//fdP3i4hxF9z6NAhrly5Qp8+fciZM+c/3s+7fVbFihWBt32BBHf+nG6/r1KpSE1NxcjICGNjY6W6jouLC/C2jx82bBjz5s3ju+++A2Dnzp2Ehobi6OhIs2bNsuYgsjHtua5y5coMGzaM5ORk1q1bx+zZs/Hz8yN//vwYGBgor2OrVq1ITk5m0aJF1KtXL6ubL4QQQgghhPgDUnlHCCGEEEL8J31oQFr3juGHDx+yePFipkyZQuPGjZUAj1ZqairJycmYmZn94T6FECI7CA8P56uvvlKmnElJSaFjx46Eh4eTJ08eLCwsOHHiRLrgzqpVq/D19aVOnTqEhISQK1euDGufbv979+5dHj16BECxYsWwsrICIC4ujujoaEqXLk2JEiUwNTVVtp87dy4TJkwgMDCQfv36ZVg7hRAfFh8fT4sWLThx4gQLFy6kd+/e6d6jInPo9qUrV65k37593Lx5EysrK1xcXKhcuTJ58+ZV1p85cyaurq7Y29szbdo0Xr9+zbRp0/j111+5efMmhQsXzqpDyRbe/Wzx+vVrNBpNuvPpuXPnmDFjBqGhoQwdOpQJEyYoAVvd7d+8eZOpU1YKIYQQQggh/r73b0kTQgghhBAiG1Or1cD/7speu3YtN2/eBFCmBwCwtrZmyJAheHp6cvDgQfz9/Tlx4oSyH0NDw3TBHd19CiFEdrJu3TpatWpFcHAw9+/fB8DY2Jju3btja2tLfHw8o0aNShfcWb58OZMmTcLExITp06eTK1cuMureIbVarfS/kyZNomHDhtSsWZOaNWtSpkwZAgMDuXXrFnnz5qVDhw6UL18+XShg/fr1LFiwgNKlS9OmTZsMaaMQ4o9ZWlri7e1N3bp1GT16NCtXriQpKSmrm/WfotuXuri40L9/fyIiIlCr1Zw7d44ffviBuXPncvv2bWUbZ2dnZs2axe3bt+nSpQsDBw4kLS2NK1euULhwYeW6W/x97wap+vTpQ+3atWncuDHz5s3j4sWLAFSuXBkXFxe6du3KggUL8PPz48mTJ8Dbzyba1yBHjhzKY0IIIYQQQojPk1TeEUIIIYQQ2d7Bgwe5dOkSo0ePBv5Xbn7Xrl20bduWtm3bEhgYqFSU0K3Ac/fuXSZMmMDq1avp0qULY8eOpXr16ll0JEIIkfnOnTvH5MmT2bVrF8OHD2fEiBHY2dkBMG/ePKZNm8bjx4+pVasWZcuW5erVq1y4cAErKysOHjyIvb19ptzp7+bmxowZM/juu+/o3r07cXFxhIeHc/z4cdq2bYuPjw+lSpVS1n/58iVTp05l7dq16OvrExMTg52d3Uen3xJCZAzd6679+/fj4+PDuXPnmD17No6Ojv94Ci3d/Yq/zt/fH39/fwYOHIiTkxPly5fn6NGjNGjQAHNzc/r27cvIkSOxtbVVttm2bRvnz59HT0+PQYMGUahQIanw8i/o/u2OGzeOefPmYWZmRqFChbh69SoA9erVY8SIEXTs2BF4e66ePn06GzZsYMSIEXh6elKwYMEsOwYhhBBCCCHE3ycTPQshhBBCiGwtPj6eYcOG8csvv2BgYMCIESOUQdmvvvqKESNGsGjRIvT19Zk1axb29vZKBR49PT1sbW1p3749q1evZtOmTdy+fZvFixdTsWLFLD4yIYTIHJUrV8bX1xd9fX3mzJmDnp4eQ4cOpVixYowYMYJixYoRHh7O2rVrOXPmDMWLF6dv3764urpm2gDuli1bWLBgAf3798fV1ZWSJUuiUqkwNjYmKiqKK1euUKRIEWX9K1euMGbMGCIiImjatClLly7FxsZGBpuFyAJ6enqkpqZiZGREs2bNMDY2Zvz48YwbNw4jIyO6dev2XrXDP6MbfkhKSsLU1FTe339BTEwMK1asoHv37owZMwYHBwdev35N//79yZcvH/nz5ycoKAiNRsOIESOU4Hu7du1o166dsh95rv8d7d/unDlzmDNnDqNHj8bR0ZEKFSoQHR3N9u3bWbhwIU+fPsXIyIi2bdtSuXJlPDw8MDIyUsI+kyZNkgCbEEIIIYQQXxAJ7wghhBBCiGzN0tKSwMBAXFxcGDVqFCqVSqnAU6JECcaOHYuenh7z5s0DSBfg0Q4kVatWjdKlS1OnTh1iYmIoVKhQFh6REEJkjA8Ntmqr0JQrV47x48ej0WgIDAwEUAI8rVq1olWrVvj4+JCWlkbBggXR09PDwMAg0wZwY2NjMTY2xsnJiZIlS5KSksL27duZMWMGxYsXJyIigly5cpGWloahoSGFCxdm4MCB9O/fn6ZNm5InTx4ZbBYii6hUKoyMjADYt28fV69exdjYmOTkZJydnQHo0aPHX67Aoxvc2bNnD7NmzSIkJAQHB4eMOYBsQqVScfLkSVJSUhg0aBAODg4kJiZSs2ZN4uPjmTFjBiVKlGDkyJEsX74cAwMDhg4dqgR4dElf+u9oNBqePXtGWFgYZcuWZdSoUUqlo4YNG1K5cmXs7e1xdXVl6dKl1K5dmwIFClChQgVGjx6NhYUFgwcPluCOEEIIIYQQXxgJ7wghhBBCiGxLO3jTokULjIyMGD58OGPHjgVQAjx2dnbK/+fNm4dGo2HGjBnY2dlhZGSEWq1mxYoVGBoa4u3tzZw5czA3N5dpVYQQ2YJuX6YdbI2MjKRatWrkyZMHfX19ZZ3y5cszYcIEAAIDA9HT08PJyYlixYoBfHB6jowewFWr1ahUKqKjoylWrBhVq1YlOTmZbdu24ebmhr6+PseOHcPKygqA06dPY2BgQPXq1Wnfvj16enro6emhVqtlsFmILKDRaJT3nouLC0uWLKFEiRKUKlWK+vXrc+jQIUaPHk1aWhqOjo6Ympr+6f60gYWIiAgmTpzI6dOnSU5OzvBj+dIZGBhQoUIF/P39qVOnDsnJyXTr1o3ff/+d6dOn07NnT/T09KhRowZnzpxh+fLlPH/+HB8fn3SVzcS/p6enx/Pnzzl37hw//PADtra2aDQaNBoN+vr6WFpa0rVrV3766SfWr1/PsWPHaNu2LQBVqlQhMDAQQ0NDCaUKIYQQQgjxhZHRBiGEEEIIkW1pp78CaNKkCcHBwZQsWZKxY8cyZ84cZT17e3tGjx7NiBEj2LlzJyNHjmTPnj0kJiayatUq1q9fT+nSpSlatCjm5ubKF+dCCPGl+vHHHzl16pQSztFavnw5TZo0Ye7cubx48QIg3Trly5fH2dmZmjVrEhQUxPLly7lz506mtVvbp2vp6+tjZGRE8eLFefLkCXfu3CEiIkIJ7pw8eZL8+fMr6w8ePJhx48bx5s0b9PX1lUF+6dOFyBra9+DChQuZNWsWffr0YevWrYSGhhIdHc2CBQuUSomrVq3i9evXH93Xu8EdNzc3rl27xrlz5yhXrlymHM+Xrnnz5vTq1QuAgwcPcvjwYbp160avXr2U57Zp06ZUrVqV4sWLs3fvXnLlypWVTc62cuTIQc6cOYmPj1ce062kU6hQIdq0aQPAkSNHAJRztaHh2/t1JbgjhBBCCCHEl0W+nRJCCCGEENna3wnwjBkzBhcXF2JjY2nXrh0lS5akf//+vHr1ilmzZilfhEsJeiHEl+z+/fs0adKEmjVrcvr0afT19VGpVAAUL16c7777jsmTJ380wFOjRg1atmxJSkoKgYGBBAQE8ODBgwxvt1qtVvrfxMREAKXd5cqV4969e4wePZqhQ4diYGDAiRMn0gV3Zs+ezcOHD2nVqhXGxsYZ3l4hxF8XHR2NlZUVQ4YMwc7OjrS0NACGDBnCpEmTsLGxwdnZmfXr1yvvf10fCu7cuHGD2NhYKlasmKnH8qXTXu9eunSJhIQE2rZti4mJibJ8w4YNGBsbs2nTJs6dO0eePHneC1aKv043QKtbISpHjhyULVuWAwcOsGPHDqVSnEajUd4f9erVS7cvCaIKIYQQQgjxZZMreiGEEEII8Z+g/WL8jwI8dnZ2uLi4sHnzZr777jsqVapEnz59OHr0KHZ2dsogsRBCfMlsbGyYOHEiOXPmpH79+pw6dUq5O79BgwZMnDiRRo0a4evr+16AJyUlBYBmzZpRuXJlqlevTnh4ODlz5szwdmsHJUeOHEmHDh14+PCh0m53d3dq1KjBjh07ePXqFeHh4RQoUEDZNiwsjEWLFlGiRAn69u0rA5xCfCY0Gg1JSUn89NNPWFhYYGtrq0z1ow2EtGnThgEDBvD69WvGjRtHaGgor169SrePjwV3KlWqlCXHlR3kzp0bgJ9++kmpeLRx40bOnDlD/fr1sbW1JV++fOmCleLvUalUyvlo48aN+Pr6EhYWBoCVlRX9+/cH3k4pFx0dDbz9TGNoaIharSY0NBRA/s6FEEIIIYTIJvQ0cmuEEEIIIYTIZtRq9QcHZnUfj4iIYPjw4fzyyy/Mnj2b0aNHf3BfaWlpGBoaKgNJQgjxJdPtBwMDA3F3d8fQ0JCYmBiqV6+urHf06FH8/PyIiIjAx8eHkSNHkidPHuDtQPmoUaM4ceIEy5cvp1ChQuTNmzfdAHpGqlGjBqdPn6ZLly4EBgZibW1NWloa+/fvZ/z48Vy/fp0BAwbQtm1b8uTJw7p161i/fj2GhoYcPnwYW1vbj54nhBBZo0ePHmzdupVDhw5Ro0YNpT/RvldfvXpFrVq1iIuL4/fff2fFihX06dMn3T4OHjyIq6urBHf+n26frFar0Wg0f+ta9vr16zg6OnLx4kXatGlDcnIysbGx5MyZk9jYWGxsbDKq6f8JuuchDw8PFi1ahLm5OXPnzqVly5ZKhThPT0+mTp2KtbU1M2fOpHnz5uTJk4c1a9YQEBCAoaGhUrlKCCGEEEII8WWT8I4QQgghhMhWdEM2hw4d4tatW9y+fZu2bdvi4OCAhYWFsu7BgwcZNmwYv/zyC7NmzWLMmDEApKamYmRkBJBpg9FCCJFZ/kmAx8XFhe7du1OxYkXWrVvH5MmTadq0KYGBgekG2DOSbn/cokUL9u3bR6dOnZg7dy7W1tYkJiZy7NgxfH19OXbsmDLVl4WFBdWqVWPFihXpqnoIITLXh66ptI/NnDkTV1dXGjVqxJIlSyhWrBgajQa1Wo2BgQEJCQmUKlWKdu3a8ejRI4KCgihatKiyn6ioKAYNGkRcXBzR0dH/+eDOH/XJq1at4uuvv6ZcuXJ/up99+/axePFiduzYgaWlJRUrVmTVqlXSl35CEyZMYNKkSQwdOpT+/ftTpUoVIP37xdfXFz8/PwCKFi2Kvr4+v//+O0WKFCEyMhJ7e3sJpQohhBBCCJENSHhHCCGEEEJkG7pfWo8fP54FCxYQHx8PgImJCf369WPw4MFUrFhR2UY3wBMYGMioUaOypO1CCJGZ/mqA59ixY0ybNo2dO3diaWmJjY0Nly5dwsbGhsOHD6cbPP/UPjTQr62GBm+n7oqIiKBjx47MnTuXQoUKKce2evVqnj17xuvXr/nmm2+oWrUquXPnlsFmIbKI7ntPpVKRnJwMkG7KvdatW7Nnzx769++Pu7s7JUqUUJZpq4xs2bIFBwcHjIyM0u3z6NGjdOrUiV27dlG1atVMPLLPW82aNbG1tWXTpk0AODk5sWrVKpYuXUqXLl0+2h++2/+eOXMGKysrLC0tMTc3l770Ezly5Aht27aladOmTJ06FVtbW+DD579t27axb98+jhw5QuHChalSpQpjxozB2tpaXg8hhBBCCCGyCQnvCCGEEEKIbEH3S24PDw+mTZtGmzZt6N+/P/b29kybNo1t27bx/fff4+3trdzVCm8DPKNHj+bKlSssXryYgQMHZtVhCCFEptEd7PujAM8vv/zC7t27CQwMJF++fNja2hIcHEzRokUzbMBQN1z04sULLCwslH7+YwGeoKAgrK2t/9I+hRCZR7efWLZsGVFRUVy9epUcOXLQt29f6tSpQ7ly5bhx4wb9+/cnNjaWihUr4u3tTZEiRTh69CghISHkyJGDH3/8kdy5c3/w9yQmJmJmZpaZh/ZZu3PnDi1atODq1auMGjUKIyMjZs6cyciRI3Fzc1MCj3+XVKX86/7svLN48WKcnJyIjIykYcOGH1zn3ec7JSUFY2NjZd8S3BFCCCGEECL7kPCOEEIIIYTIVlavXs24cePo2rUrw4cPp3Tp0iQmJlKzZk1u3LhBamoqrVq1ws/Pj8qVKyvb7dmzh2nTprF69Wrs7e2zrP1CCJERPjaA+FcDPABxcXFKxYUcOXJ80gHDZ8+eYWZmhomJSbrHXV1duX//PjNmzKBIkSIfDPA0btyY6OjodBV4dJcLIbKObvBg3LhxyjR39vb2PH78mJs3b9KiRQucnJxo0aIFd+7cwd3dnbCwMPT09NB+beng4MCBAwdkeqC/6caNGwwbNoyIiAgAvL29GT16NHnz5s3ilmV/un/7cXFxH3zOR44cSXBwMJcvX6Zs2bLvLdeeZx8/fkyBAgWA/53PJUQlhBBCCCFE9iOfdIUQQgghRLYRFxfHjh07KFy4MIMGDaJ06dK8fPmS6tWrExcXx6JFi+jZsye7d+/G19eXn376Sdm2ZcuWREREYG9vT1paWhYehRBCfFoqlUoZ6D579iwRERGsX7+euLg4dO/nGTNmDFOnTiUtLY0GDRpw6tQp4O1UVWq1GktLS4yMjMiRIwcajeaTBXeOHTtG7dq12blzpzKVjkajISkpiR9//JENGzYwefJkfvvtN2Uw39DQUOmrV61ahb29Pbt27WLMmDE8ePBAgjtCfCa04YKgoCDmzp3L0KFDiYiI4PDhw0RFRdGpUyf27NnDhg0bSEtLw87OjtDQUDZu3Mj06dMZPnw4CxYsIDY2Fnt7+3T9mfhjGo0GBwcHjI2Nlcdu3bqlhEhSU1Ozqmn/Cdq//bZt21KjRg0ePnz43jr58uUD3k5LBm/P11ra86xGo8Hb25v9+/cDKH//EtwRQgghhBAi+5FPu0IIIYQQ4oul+wU3QFJSEkZGRnh5eVGhQgWSkpJo2rQpz549Y+rUqfTs2VOZgiEyMpKAgABlcBpQKj7IoK8QIrtQq9VKyMbf35+2bdvSvHlzevbsSd26dVmwYAFxcXHK+u8GeE6fPq30iboDhZ9q0FCj0XDnzh0ePXqEj48Pe/fuJTk5GT09PUxNTQkPD6dFixYsWrQIf3//9wI8KpWKAgUKULhwYUxNTdm4cSP+/v6o1epP0j4hxL+j0Wh48uQJYWFhVKxYkeHDh1O2bFnUajVHjhzhxIkT2NraEhgYiKGhISkpKQB07NiRcePGERQUxJAhQyhYsKBMD/QPFSxYkPbt21OrVi3Wr1/PoEGDADAyMnrvWlp8Omq1mqSkJGrWrElcXBzt27dXAjzac9R3332Hubk5c+bM4dWrV0pYR6VSKefZqVOnsmnTJiXcKoQQQgghhMi+JLwjhBBCCCG+WNoBnJ07dwJQpEgRpk+fTocOHVCr1cyYMYMzZ84wZswYOnfujJGREcWLF8fGxgZzc3O2bt3K/Pnz5c5jIUS2pNFolDv03dzc8PHxoWTJksyfP5+wsDCMjIyYOnUqAQEBPHv2TNlOG+DR09OjRo0anD9/PsMqXejp6dGmTRuWLFlCYmIizs7O7N27lzdv3gCQN29e1qxZw/fff09ISAh+fn7cv38fPT09UlNTMTAwwNjYGBMTEwIDA+nXrx+urq5SmUOIz4Senh7x8fGcOXOGJk2aUKpUKVJTU9m4cSOurq4AnD59mnz58qHRaHj48CGvX7/+4L4kuPP3aAMiS5cuZdOmTaxfv55vv/2WpUuXMnjwYODtc6oNTAG8evUqS9qanfz666/A2wo5pqamjBgxAl9fX65fv06zZs34/ffflXNUhQoVaN68OT/99BOtWrXi4cOH6SrbbdmyhTVr1lC+fHm++eabLDsmIYQQQgghROaQb7OEEEIIIcQXberUqfzwww9ERUUBYGtri4GBAfr6+pw6dYrChQvj4eGBqakp8LaqTmJiIh4eHkyZMgVfX1+MjIyy8hCEECJDaO/aX7x4MSEhIYwYMYL58+czePBgWrRowZs3b3j48CELFixg6tSp71Xg8fDwwM7OTpliJaPkzJmT1q1bM336dFJSUt4L8FhaWrJu3Tq+//57lixZwsSJE7lz5w5GRkZoNBpWr17NpUuXKFu2LEuXLqVYsWIy/aEQmUR36j348FRMqampqNVqLCwsANi8eTNubm7o6+tz8uRJrKysAHj+/DnNmzfn4MGDGd/wbOjdimO6r4Wenh729vbMmzeP+vXrs2TJEiXAo51Wa//+/UrIRPwzBw4coHr16qxbtw54+/4wNzenb9++uLu7c+3aNeX51Wg0WFhYEBQURKNGjTh06BBNmjTB3d2djRs3MnDgQEaNGsXLly9Zu3YtefPmlapyQgghhBBCZHMS3hFCCCGEEF+0ypUrY2BgQHR0tPKYSqXi2bNnXL16FY1Go9wBC7Bq1SqePn1KyZIlcXd3x97eXgZ5hRDZ1m+//caGDRuoUKECQ4YMoUyZMrx8+ZJq1arx6tUrZs2aRcmSJZk3bx5Tpkzh6dOnyrZeXl5cuHCBokWLZvjUKqamprRt21YJ8Li4uHwwwNO8eXOWLVtG586dCQ0NxcPDA19fXwoUKECJEiWU/cn0h0JkPI1Go4QEHz9+DKAEotevX69U0LGwsKBQoUKsXr2a4OBg3N3d0dfX58SJE+TPn1/Z36xZs5Rgnvh7VCqVUs1l9erVDBgwgMqVK9OmTRumT59OUlISarWaChUqpAvwDBo0iISEBMLCwhg1ahSbN2/G0tIyi4/myxQREUGrVq2oWLEi5cqVA1CmeTQ3N2fQoEFcuXKF+vXrK8u0Uz+GhoYyZMgQXr9+zcyZM+natSthYWGUL1+eI0eOYGdnl+41FkIIIYQQQmRPepp3b5ERQgghhBDiC3L37l3atWvHlStXiImJoWbNmsoyLy8vAgICGD58OG3btuXs2bMsXryYnDlzEhUVRb58+bKw5UII8empVKp0U8tcunQJJycnxo0bxw8//MDr16/59ttvuXfvHjNmzKBbt27ExMTQrl07cufOTY8ePXB1dVUqYUD6AfqMlpSUxI4dO3B1dcXY2JgZM2bQvHlzcuTIAUBycjIDBw5k7dq1yjalS5dm79692Nvbo1arZXBTiEzWrFkzChcuTEBAANbW1kqVr7CwMDp16gTAuHHjCAwMxMLCAgsLC3755RdMTEyAtxVjNm7ciLu7O5UqVWLNmjVKlR7x53T7aGdnZ4KDgzE3NydPnjzcvXuX1NRUWrRogZubG7Vr18bQ0JDLly8zZswYDh48iKWlJUlJSeTPn5/o6GiKFy8ufenf9OTJEypWrMijR48ICAjAzc0NQHke3z2P6v6sXefNmzc8f/6cU6dOoVarKVWqFEWLFsXMzOy9c7sQQgghhBAie5LwjhBCCCGE+OItXrwYJycnfH19mTBhAqmpqRgZGXH9+nW8vb3ZvHkz8PYO1zJlyhAeHo6dnZ0MTAghsq2ffvqJatWqAXD27FmqVKlCWloabm5uzJ8/n4CAAIYOHYqJiQl37tyhevXqJCcn8/LlS/z8/PDy8srQwM4fBYL+LMADb6cmefz4MUZGRjRu3BgrKysZ3BQiCzx8+JCOHTty6tQpXF1duX//PqtWrWLcuHGMGDECW1tbABITE+nRowe7du2idevWrFixQpmSb86cOQQHB6NWq/nxxx+xsbGRa7R/YObMmbi6ujJs2DAGDRpEqVKlOH36NLNmzSI8PJyvv/6a2bNnU6NGDQBu3LjBmjVruHjxInny5MHf358iRYqQlpYm1cv+pjdv3rBp0yacnZ0pUKAAkyZNolWrVhgYGPylAOwfrZOZAVohhBBCCCFE1pLwjhBCCCGE+Ox9aEBWrVYDoK+vz8uXL2nWrBn379/nzJkz6SpGxMXFsWPHDq5evUrRokXp0qULBQoUkEFeIUS25eLiwpIlSzh27Bhly5ZVHk9KSqJhw4akpaVx+vRp5fGUlBSqVKnC8OHDiYqKYubMmdjZ2WVY+97tfxMTEzEzM0u3zrsBnpkzZ9K8eXOlUse7ZKBfiKyhVqt58OABLi4ubNq0CbVazaBBgwgICMDS0jJd8ODnn3/GxcWF3bt3Y25uToUKFXj06BH37t3DwcGB3bt3Y29vL9do/8CzZ89o0qQJRkZGbNq0CVtbW+W5v337NgsXLmTmzJl06tSJDRs2KNtpgzopKSkYGxvLc/8vpKSksHnzZoYNG0bhwoXx9/enXbt2ytRZEsARQgghhBBC/Bn5ZksIIYQQQnz2tIMIq1atYteuXcDb0I6+vj5qtZpcuXLRuHFj7t+/z7x581CpVGgz6nnz5qVv375Mnz6dESNGUKBAAdRqtQxMCCGyLTMzM168eMH169eBt4PrGo2G+/fvc/nyZSwsLEhOTlaWLVmyhKdPn1KvXj02bdqEnZ0dKpUqQ9qmOzC8fPlyunTpwldffUX37t1ZunSpsp6pqSlt27Zl+vTppKSk4OzszN69e5V2v3sfkgR3hMga+vr62NjYkDt3biVEp1arSUtLA1CCC/B2irudO3cyefJkvvnmGx4+fEipUqWYOHEiUVFREtz5F548ecK5c+eoVq0atra2pKWlKc+7vb09AwcOpFatWmzcuJHQ0FBlO+1zbWxsnO5n8fcZGxvTsWNH5s+fz4MHD5gwYQLbtm0D0r8PhBBCCCGEEOJjpPKOEEIIIYT4ImzdupWOHTsC0Lt3b9q2bUuLFi2UKgwvXrygatWqWFpaEh0djZmZmVRiEEL8J506dYrmzZtTsGBBoqKiKFiwoLKsRYsWxMbGMn36dJo3b87+/fsJDAwkb9687Nu3DwsLiwxrl26fPG7cOObNm4eVlRWlSpXi2rVrPH78mNGjRzN79mxlG90KPLly5cLHx4f27dsrA81CiKyl0WiIj4/Hz88PjUbD1atXiYqKYuTIkYwaNUqp4qXRaN4LT798+RJzc3OlKolct/1z9+7do0yZMrRo0YJNmzYB70+3tHnzZjp37kxAQABubm5Z1dRsT7cCT5EiRfDz85MKPEIIIYQQQoi/RD4RCyGEEEKIL0KTJk3YvHkzzZs3Z8uWLXTo0IEmTZpw8OBBfv31VywsLOjTpw8//fQTixcvBqQSgxDiv6l69er88MMPXL16lZiYGODtYCK8nVLL3t6eYcOGUalSJYYNG4ZKpSI0NBQLCwtlSsKMoO2Tp0yZQnBwMAMGDGDPnj3ExMSwadMmcuTIwZw5cxg6dKiyjbYCz8yZM7l9+zbBwcEZVhVICPHXaO8D1AYR8ubNi5eXF35+fqxcuZJWrVoRFBTE3LlzuXv3rrKugYGBEuIByJkzZ7r9yXXbP5cjRw4KFCjAli1b2LJlC/C/ai/aKkj29vYAPH36NKua+Z+gW4Hnt99+UyrwaN8vch+tEEIIIYQQ4mOk8o4QQgghhPiixMXFcfv2bWWKhZSUFCpUqICXlxcWFha0bduWqlWrEhoaSpEiRbK6uUIIkSHenVpGOyioffz69et8++23VK1alfDwcGW91NRUbty4QXBwMPHx8djZ2TFq1Cisra0zZbqa6OhoBg8eTO3atfH09KR06dIkJCRQr149Hj16RO7cublx4wYjRoxg7ty5ynavX7/m4MGDVKtWTfp2IbKQbj+RnJzMmzdvyJ07t7Jco9Fw8+ZNnJ2d2b17NyNHjmTEiBEUK1YMgO3bt/PTTz/h6emJqalplhzDl+rPKhOtX7+enj170qhRI3x8fKhXr1665TNmzGD8+PGsWrWKLl26ZHRz/1M+VFHnQxV42rdv/9H1hRBCCCGEEELCO0IIIYQQ4ovw7pfcqampnD59mtDQUJYsWUJycjKtW7fm7Nmz3L9/n/DwcL7//vssbLEQQmS88PBwSpYsScmSJZXH1Go1iYmJ9O3bl23btrF27Vq6d+/+h/vJjOAOwPTp0/Hw8ODo0aPUrFmTxMREatWqxdOnT1m0aBHW1ta0atWKuLg4hg0bxrx587KsrUKI9HTDI8HBwezZs4ebN29ibW1N06ZNadOmDRUrVnwvwOPk5MSgQYO4desW7u7uXL9+nUePHmFlZZXFR/Tl0O33Dhw4wJ07d8idOzdly5alQoUKADx58oQpU6YQFBREjRo1GDhwII6OjiQnJ7Nt2zZ8fHzIlSsXBw8elOf+E9L9jPLw4UOsra2VZX8U4NF9TSXMI4QQQgghhAAJ7wghhBBCiC/Qu3ceHz58mMjISIKCgoiPj8fExITr169TtGjRLGylEEJkrAULFjB8+HDy5s3L+PHj+eabb6hWrZqy/Pjx49SvX58uXbqwevVq5XHdQcKMHDD8UMjmwoULXLt2jc6dO5OcnEz79u05ceIEAQEB9OrVixw5cjB58mTGjx8PQJcuXQgNDc2Q9gkh/hlnZ2dmz55N/vz5KVCgAA8fPuTZs2cULVqUFStW0KhRIzQaDbdu3cLNzY2tW7eSJ08e1Go1lpaWREZGUrx48T+tJCPe0u2n3dzcmDFjhrLM3t4eFxcXnJycALhx4wbLli1j2rRpAFSqVImkpCR+//138ubNS3R0NPb29vLcfyK6r01ERASjR4/G3d2dXr16Ket8KMDTunVrjIyMProvIYQQQgghxH+ThHeEEEIIIUSW+6dfVr878HDr1i3CwsJwdHSkUKFCUp1BCJGt3bx5k2XLlrF161auX79OgQIF6NatG8OGDcPa2hozMzO6devGxo0biYyMpEGDBlnSTm9vb+rVq0ezZs1IS0tDo9FgZGREZGQkbdq0wdHRkVmzZpEjRw4AFi5ciL+/P3ny5OHXX3/lwYMHWFpaZknbhRDpr7f27dtHnz596N27N05OThQvXpy7d+8yc+ZMgoODyZEjB3v37qV+/foAJCQkMH36dI4fP469vT0TJ07ExsZGrtH+gdmzZ+Pu7k779u1p2LAhz58/x8PDA4ApU6bg7u4OwJs3b4iJiWHOnDncvXuXPHnyULNmTVxcXChcuLA895+I7ueXqKgovL29OX78OIcPH6ZOnTrp1k1JSWHLli0MHTqUQoUKMW3aNFq3bo2rqyu5c+fGy8srKw5BCCGEEEII8ZmR8I4QQgghhMgSV69eBaBs2bKfZH/agSXtF+kyMCGE+K+4ceMGsbGxBAQEcOPGDQoXLkzDhg3x8fHh8OHD9OvXj+7du7No0SLMzMwytW0nTpygdu3aNGvWjC1btpAzZ05lWVBQEKNHj+b8+fPKtC+AMs3L1KlTMTU1pUCBAlKRQIgsovveS0xMZOPGjfj6+nLw4EFKlSqVbl1/f398fHyoWLEiW7ZsoUSJEsqylJQUDAwMMDAwkGu0v+jd56lPnz7ExcURHByMnZ0dAIcOHaJHjx48ePCASZMm4enpqayfmpoKgJGRkXKdLM/9p/FucMfd3Z1Lly5x6NAhqlevzvPnz/n999/Tfc7RBniGDRuGnZ0dVlZWREZG4ufnx5gxY8iVK1dWHY4QQgghhBDiMyH1UYUQQgghRKa7ceMGFSpUwMvLi2vXrn2SfWrvCNd+kS4DE0KI7E6tVgPg4OBA3759iYiIYN26ddja2rJu3Tq+/vprTp06Rb58+di7dy+PHz/O9DbWrFmT7777jhMnTiihzbS0NAAlSBQWFqasv3nzZmJiYsifPz92dnYUKFAAtVotwR0hsoj2vTd+/HgKFixIeHg4TZo0UYI7Go0GlUqlrNO3b18uXLjA2bNngf+9342NjZVrM7lGe9+H7q3UPk8eHh64u7tz+vRpunbtip2dHWq1GrVaTf369Vm/fj2FCxfG29ubgICAdPvQTs0k18efzseCO9HR0VSvXp2EhARWrlxJuXLl2Lt3r7KdsbExHTp0YMGCBfzyyy9ER0fj6elJ//79JbgjhBBCCCGEAKTyjhBCCCGEyAK3b99m8uTJrFmzho4dO+Ll5fXJKvAIIcR/nUajYfXq1ezcuZNt27YBbwOOv/32GwULFsy0dmgrPBw4cICWLVvSu3dvli1bpiy/efMmderU4cmTJ3Tq1AmNRsOhQ4cwNTXlyJEjFClSJNPaKoT4OLVajZ+fHwsXLuTJkyc4ODgQGRlJ0aJFlXV03+/ff/89ffv2ZdmyZVI16y/QfY5+//13ChUqBLx93n/77Tfs7OzInTs3ZmZmLFiwgNatW5OamoqRkZGy7Y8//kj37t158OABU6dOxdXVNSsPKdv6o+BOzZo1SUhIYM2aNUyYMIFSpUpx/Pjx9/aRnJzM/v37uXLlCj169Ej3PhJCCCGEEEL8t0nlHSGEEEIIkens7e3x8vJi0KBBhIaGMmnSJKUigxBCiH9OpVKhp6dHnz592LJlC1u3bmXo0KHcvn2bggULKhUyPjXd+4K0/9dWeKhQoQKVK1dmw4YNHDp0SGlniRIlOHDgANWqVSM8PJyDBw9Svnx5fvzxR4oUKZJhbRVC/HXa6Za8vLwYO3Ys9vb2PHr0iKNHj35w/QoVKmBiYvJexRfxcdrnqGnTpowcOZJbt24Bb0OXRYsW5fDhw6SlpfHbb78RHh4O/G8qLD09PTQaDd9++y3r16/Hzs4Od3d35s+fn2XHk1391eCOh4cHFSpUUII72upTWiYmJrRs2ZJRo0ZJcEcIIYQQQgiRjoR3hBBCCCFElrC3t2fs2LGMHTuWDRs2MHXqVC5duvSP9iXFJIUQ2cHdu3d58uTJv9rHu1Oi/PDDD8yZMwcbGxulMsan9u60Vtr/azQaNBoNhQoVwsPDg6SkJKKjo5V1VCoVlSpVYu/evRw9epTDhw+zY8cObG1tM6ytQog/pp2OT0s7LamRkRFjxoxh8ODBGBoa4uzszLFjx5T1DQwMUKlUbN68meTkZOzt7QG5Rvsjus/N69ev+eqrr9i+fTvTpk3j119/VZbVqVOHqKgocuTIweLFi5k9ezbw9rV5N8CzdOlSqlatSuvWrTP9eLK7vxrcqVq1qhJUTUtLw9DQ8L19GRgYYGpqmqntF0IIIYQQQnz+JLwjhBBCCCEylW4lBQMDAypXrkyDBg1Ys2YN8+fP5/Lly39rf7p3wR44cIAdO3Z80vYKIURmOH/+PGXKlGH+/Pn/OsDzLu3AYUaFYbSD+0OHDqVBgwbs3r2bR48epQv01KpVi+rVqzNz5kwuXbqEvr4+BgYGaDQarKysqFChAl999RVmZmZoNBoJ7giRBVQqlfJ+vn37NidPnuTHH3/kxYsXqNVqjI2NGTNmDG5ubrx48YKOHTuyePFiLl68CMDKlStZvHgxxYoVo2/fvoBU3vkjL168UP6fM2dO/Pz88PDwYOXKlfj4+HDz5k3gbaCqevXqxMTEYGJigpeXF0FBQcD7AZ7GjRtz9OhRJQQpPq39+/fj7u7O1atX/3FwRwghhBBCCCE+RsI7QgghhBAi06jVamVA1t3dnRYtWjBw4EBl8GLx4sXMmDHjL0+hpRvcOXjwICNHjqR79+7Ex8dnzAEIIUQGefPmDRUrVmT27NksXbr0kwd4MtrTp085evQo586d44cffqBVq1bs2rWL3377DYDChQvToUMHXr9+zdatW1Gr1e9V7NGSwX4hMp/uNdqkSZNo0qQJtWrVokGDBnz11VdMnz6dGzduYGxszOjRo/H29iYlJYWRI0dSs2ZNqlatire3N+bm5kRGRmboNH3Zwe7duylatChXrlxRHrOwsMDZ2ZnRo0ezdu1a5fpYG9CpUaOGUr3M1dU1XYBHO2UioExZJiHIf+bd6lNar169Yu/evZw+fZqDBw9KcEcIIYQQQgjxyelppH6tEEIIIYTIZJMnT2b8+PGMHj2arl27Ur16dTZu3Mjq1avZu3cv3bt3x9PTk6+++uqj+9AN7kRERODm5sbt27eJjIykSpUqmXUoQgjxyZw8eRJPT0+OHDnChAkT6N+/PwUKFMjqZv0pbX/8/Plz7t27x8yZM9m0aRPJyclUrVqVrl27MnbsWF6+fEnbtm25d+8e586dU6rsSFhHiM+Hm5sbM2bMoHHjxnTq1ImkpCR27tzJiRMnaNSoETNnzqRUqVKkpqYqYcNff/2V6dOn07p1a/Lnz0+ePHkkvPAHoqKiaNOmDZUqVWLWrFnUqlUr3fKEhAQePHhA2bJl0z2uVqvR19fn+PHjNGzYEI1Gw8yZMxk+fHhmNj9b052y8cqVKxQuXJg8efIoyy9fvoyhoSGlS5cmISGBtWvX4u7uLsEdIYQQQgghxCch4R0hhBBCCJFpNBoN9+7do3Hjxpibm7Njxw6KFi2qLL969Spz584lJCSEPn36MG7cOMqXL69sqx3g/VBw58aNG8TGxlKpUqXMPzAhhPgXdPu048ePM3HiRKKjo5k4cSJ9+vTB2tr6X+0zM7z7+2JiYoiJiWHGjBkkJSVRrVo1vvvuOxISEggJCcHV1ZUpU6ZkWvuEEH9u69at9OrVi+7du+Pi4qKEdNasWcOAAQOoWrUqMTExmJmZAZCSksLs2bOZMWMGlpaWbNu2jQoVKqQLQIj/0Wg03L59mxIlSgCwfPlyHB0dlWUf6rPffVw3wNO0aVMSExNZsmQJ/fv3z5RjyM50/25nzJjBvHnzKFGiBDt27MDc3Dzd6/Dq1SsWLlzIxIkT+frrr5WKSBLcEUIIIYQQQvwbMm2WEEIIIYTINHp6erx8+ZJbt25RuXJlihYtikajUcrTly1blsGDB1OjRg1WrVpFcHAwly9fVrYFCe4IIbIf3Sk6HBwc6NmzJw4ODsyYMYO1a9fy6NGjv7U/3X4yNjb2L09F+G9of5/2WBo0aICvry9HjhzB19eX58+fM23aNNauXYtarebWrVskJydneLuEEH9dbGwsJiYmDB8+XAnubN26FT8/P4oVK8a+ffswMzMjNTWV5ORkjI2NGTt2LO7u7jx+/JhWrVpx9uxZCe58hJ6eHsWKFWPkyJEALFy4kDNnzijLPraNLu0UWrVq1WLPnj0UK1aMpk2bZmzD/wN0p41zcXHBx8eHypUr4+TkhIWFxXuvQ3JyMseOHaNkyZIS3BFCCCGEEEJ8MlJ5RwghhBBC/B979x1Y0/3/cfx5s2SQHSskMYKgZmPPWlVBUVF7r1gpGYKQEDv23jMIYsTeIUHsotSsGqV2IoIkd/z+8Lvne4O2tJKUvh//lHvOuf2cc53POfe+X+fzyVQ3b96kdOnS1KxZk82bNwNvP1U8ZcoUBg0ahImJCQ0aNGDy5Mm4u7tLcEcI8dnRj6IAMHz4cA4ePMjx48fJmzcvN27cwMbGhsGDB9O5c+f3mkLLsJ/cu3cv3bt3x8rKipMnT2Jubp6h+/Jnnj9/ztixY4mNjeXHH3/k/PnzuLq6Zll7hBD/o9FolECIWq3m7NmzynRZAQEBGBkZcfz4cZycnAA4d+4cSUlJVK5cGSMjI9LS0pg6dSpjxozB1taWNWvWULFixSzeq38fw5Fd9NOTlS9fntmzZ+Pp6fm33is1NRUzMzMZ7egjGT9+PEOHDqVPnz7069ePwoULv3M9jUbDzZs3KViwICDBHSGEEEIIIcTHISPvCCGEEEKIDKcfiUGj0WBlZUXx4sXZsmWLEt5RqVTodDrS0tIAqF27Nu7u7jRu3JizZ89ib2+vrAewZcsWhg4dyvXr1yW4I4T4pBkGd0aPHs0XX3zBxo0bOXDgAMOGDSN37tyEhoayePFiHjx48Kfv9WbAMSAggMTERJYtW/a3gjv6Z300Gs0Hb2tIo9GQPXt2wsLC2LhxI7dv38bV1RW1Wv2P3lcI8fe8+RyfsbExpqamFCpUiISEBJ4+fcr+/fvfGdwB6NChA2FhYaSlpaHT6TA1NcXX15ehQ4dy8+ZN1qxZk9m79EkwNjZW+tPx48fj7+/PqVOn6NOnDydOnPjg9wIwNTVN93fx9129epWlS5dSp04dfH190wV34uLiWLt2Lbt37+b+/fsYGxtToEAB4PX3HAnuCCGEEEIIIT4GCe8IIYQQQoiPznAKGPhfcdrY2BhHR0e6dOkCvB6WXj/UvFarVQoQmzZtwszMjLCwMM6fP4+Dg4PynlqtloiICE6ePCnBHSHEZ+HMmTPMmTOHmjVrEhAQwNdff42LiwsjR45kxowZfPnll4SFhbFkyZI/nELrj0YmO3DgAOXLl//gNu3du5c+ffqQnJyMsbHxW/36hzA2Nlba5+DggI2NDTqdToqdQmQBrVar9BUJCQkkJycDr/uQIkWKcPv2bTp06ECvXr0wNjbm6NGj6YI7kyZN4v79+9SuXRsTExNUKpVyD9e/f3+2bt3KlClTsmTfPgXvCvCcPHnybwV44I+n2xIf7vfff+fy5ctUqlRJCeZcuXKFfv36UaNGDb7//nu8vLwYMGAAd+/eVY69/nuOEEIIIYQQQvxTMm2WEEIIIYT4qAyH7d+7dy8XL17k1q1blC5dmipVqlCoUCEAAgICCA8PJ0+ePISHh9OoUSOsra2JiIhg3LhxlCxZkuXLl2NqavrWtFrJycn89ttvFClSJEv2UQghPqYDBw5Qp04dRo4cybBhw4D/9aU6nY4tW7bQq1cvUlNTGTx4MO3btydXrlzK9h9zSkGdTodOp6N8+fKcPXsWHx8fxo8fj5WVVbopvoQQnx7De7QFCxawbds2KlSoQK9evbC3t+fFixdUrFiRCxcuYG9vz4kTJ5QQA8DatWsZNmwYjo6ObN68OV2o583+QfqLP/euKbS+/PJLZs2a9cFTaImP4+TJk1SoUIFvvvmG3r17c/z4cdatW8etW7f47rvvKF++PNu3b2fv3r1ER0fTsGHDrG6yEEIIIYQQ4jMjj7kJIYQQQoiPRqvVKoWIwYMHM2vWLF6+fImRkRFqtZqSJUvSq1cvfHx8mDBhApaWlowcOZK2bdtSqFAhjI2NuXbtGnnz5mXcuHHKSDyGwR21Wo2VlZUEd4QQn43U1FQAkpKSgNf9nImJiRLKadKkCevWrSMiIoLx48eTmJiIr68vDg4OHzW4A6/7W5VKRWxsLLVr12b27NloNBrCw8MlwCPEJ8zwHs3Pz4/58+eTO3duOnXqhK2tLRqNBktLS2bPnk337t25du0aEydOpE2bNlhYWBAREUFkZCRGRkasWbMGJyendP3Bm/2C9BPvpu+z9SOaGRkZMX78eAAmTpxInz59JMCTwd58KEDPw8ODgQMHMmXKFLZv346JiQklS5Zk7969lCxZkuzZs+Pu7s6uXbu4cuWKhHeEEEIIIYQQH52MvCOEEEIIIT660NBQQkND6dChA71798bZ2ZnY2Fh69uzJ8+fPmT17Nr169QJgw4YNbN++nZiYGJycnChatCijR4/G2dk53VPJQgjxOTEsHl69epXatWuj1Wo5cuQIbm5uSlE3NTUVMzMzoqOjCQ4ORqfT8fz5c3788Uesra2V99u+fTshISFcunTpH08pqA8PPX/+nKpVq3L+/Hl69OjBpEmT/naA581i6R8VT4UQGWvs2LEEBwfTt29ffHx83gpDazQaTpw4Qe/evTl79qwyzVOOHDn48ssvWbJkCS4uLnKP9jcY9nsJCQnkyJEj3TH8oxF4pL/8eAz/3b548YJnz55hZGREzpw5AUhMTGTv3r2cPXuWUqVK0bBhQ6ysrJTtg4KCmD17Ntu2baNatWpZsg9CCCGEEEKIz5eEd4QQQgghxEd18uRJvv32W8qVK0d4eDhFihRBrVazZ88e2rVrh5OTE0ePHsXOzi7ddi9fvsTCwkIpGktRSAjxOfmrwEvPnj1ZsGABXl5ezJkzB2dnZyW4A9CjRw/OnTvHzJkzcXV1xcnJSSnoJiQkULduXU6fPs2PP/5IqVKlPmpbS5Uqxc8//0ynTp2YOnXqBwd4DAvP9+7dI2fOnNK/C5EFLl26xDfffEPBggVZsGCBMiXWu8IhaWlprF27lt9++w21Wk3lypUpV64cNjY2co/2Nxge4127drF06VJ69+5NjRo1PmgKLRn97O8zPM5Tp05l8+bNnDlzBgsLC2rXro2vry+lS5cmW7Zs79xeP22ci4sL69evx9bWNhNbL4QQQgghhPgvkGmzhBBCCCHER3X16lXu3r3L3LlzleDO+vXrCQwMxNramri4OOzs7EhLS+Pp06fKk67m5uYAyo/qUhQSQnwuDAuGJ06c4NatW/z+++9UrlwZV1dXHBwcmD17NpcvX2br1q0kJycze/ZsihYtCrwuGB45coSGDRtSrlw5jIyM0hVwbW1tmTZtGjly5PjHwR3Dtq5YsYJnz55RtGhRfvrpJ1atWoWxsfEHjcBjWLDeuXMnK1eupFGjRrRu3foftVMI8eF+/fVXfv31V0JDQylQoIByfr4Z3FGr1ZiamtK2bdu33sNw+i3xfgz7wf379zNixAiOHz/OkCFDAP5wCi0fHx8WLlxI6dKl6d+/P23atKFSpUpZth+fMp1Ol27auKlTp+Lh4UGbNm24desWO3bs4MKFC3Tt2pUePXoo30vgdZBtypQpzJkzB41Gw+LFi7G1tZUglRBCCCGEEOKjk/COEEIIIYT42971o/WFCxcAKFKkCBqNhrVr1xIUFISRkREnTpzA0dERgDt37jBnzhwGDBiAs7OzUtSQaQGEEJ8Tw0J3cHAw8+fP5+HDh8Dr/q5Zs2b07t2bOnXqsH79etq2bcuePXvw9PTE09MTtVqt9J39+/dX+lzDvler1VK1atWP0l59WwcNGsSSJUvImzcvjRo1onbt2ly+fJn58+ejUqkIDw//ywCPYcF6z549BAUF8dNPPzFixIiP0lYhxPvRn6fXrl0DXo92qH/dMIijD+89fvwYrVZLnjx5lGX681nCCh/mzeDO4MGDuXjxIvHx8XzxxRc8f/6cZ8+ekStXLmVdwwBPnz59yJs3L+vXr8fe3p4vv/wSExP5OfdD6T+DhQsXMn36dHr16kWfPn3w8PDg0aNHTJgwgfDwcPbu3UvPnj2B1+fDzz//TJcuXTh//jzly5dn1apVMm2cEEIIIYQQIsPIN24hhBBCCPFetFrtW6/pCzi//vorGo0GgLJlywJw4MAB9u/frwR3jh8/rgR3AIYMGcLy5ctJTU3NhNYLIUTW0PeTw4YNY/To0dSoUYMtW7Zw4MABfH192bBhA+3bt2f//v04Ojqya9cugoODKV++PLGxsdy7d4969epx+PBh8ufPr/S17/p/fCzLli1jypQpdOzYkejoaMaPH8/GjRuJjo6mRIkSzJs3Dz8/P5KTk5VRgN70ZnAnMDCQGzducPLkSdzd3T9qe4UQ6b15Tur7iJIlSwJw/vx54H8jvkD6kUn69evHpk2b0vU3Eq7+cO8K7vz000/s27ePChUqkJiYyNy5c2nevDnnz59HpVIpx3z8+PEEBARw5MgRdu/eTXBwMD169JDgzt+k0+nQaDRs2rQJNzc3+vbti4eHB2q1mpiYGCIjIylQoACLFy8mW7ZsSrBNo9FQp04dwsLC2LRpkwR3hBBCCCGEEBlKvvEJIYQQQoj3oi/8DBgwgPr169OoUSMAunTpwrlz54iKisLV1ZVChQphY2ODn58f1tbWmJubc+zYsXTBnQULFnD48GG+/fbbdE91CyHE52j//v3Mnj2b77//ntDQUNzd3dFoNDx//pzp06djZ2dHuXLllPVDQ0NRq9Xcvn2b3LlzA2BhYZEpBUOdTkdsbCwmJiZ07dqVggULAmBtbU25cuXYv38/NWvWZN68eWi1WiZNmkT27NnTte1dwZ1r164RGxtL6dKlM7T9QvzXGZ6Lp0+fJiEhga+++goAV1dXPDw8mDVrFpUrV6ZNmzYYGRml22bRokXs2LGDypUrS2DnH/ij4M6BAweoWLEiiYmJrFy5krCwMAoUKECZMmUAlMCIsbEx48aNI0eOHDx48IDu3buTN2/eLNyjT5tKpeLx48ccOnSIFi1aUKxYMV69esXmzZsJCAjAyMgo3feVixcvUrx4cUqXLk2RIkUwMzNTwm4S3BFCCCGEEEJkFBl5RwghhBBCvLfIyEhmzJjB+PHj+fHHH/Hz82Pp0qVUrFgRCwsLAMqUKcOIESNITk7m3r17hIWFpQvurFixgokTJ2Jra8uIESMwNzdHp9Nl1S4JIUSG+/HHH3n+/Dl9+/bF3d0dtVrN2rVr6dOnD/ny5ePgwYPY2tqSmppKYmIiACYmJri6umJhYYGFhUW6UTEykk6n4/fff8fc3BwXFxflNXgdCnBycmL69OnY2NiwZs0a/P39ef78uQR3hPgXMAwWjBkzhpYtW9KvXz/i4uIAKFCgAEOGDAGgc+fOLF68GPjfdHlr1qwhPDycQoUK0bZtW5ki6296n+DOihUrGDx4MGXKlOHMmTMAyqg7+gAPwNChQxk1ahT58uXLmp35jNjY2JAjRw5evHgBwLZt25TgjuEIoVqtlvr16xMSEgK8Ds/qzxE5J4QQQgghhBAZSUbeEUIIIYQQ761Vq1b89ttv+Pn50ahRI+7du8fQoUPp06cPOXPmRKvVYmRkhK+vL0+fPmXUqFF069aN2NhYnJ2dOXbsGLGxsdjY2LB7927y5MkjQ88LIT4rhkVb/Z9PnjyJpaUlpUuXJiUlhY0bNzJ48OC3CoY3b95k3759tG3blhw5cqQrEmbWCBhGRkbkzJmT58+fs3PnTlq2bKn8v/V9dfHixbG2tubx48fMmzcPW1tbxo4dm27fd+/ezZAhQyS4I0Qm0el0Sp/h5+fH9OnT+eabb/jhhx+oVq2acn62bduWxMRE+vbtS7du3Vi5ciVubm7cuXOHEydOYG1tzY4dO9Ld14n3977BnaCgIMqVK0dMTAwAarU63ZRYxsbGyntZW1tnxa58st713SItLY20tDRcXFzYuXMnQUFBREREYGJiwtGjR3FycgJef36jR4/m1atXFClSJCuaL4QQQgghhPgPU+nkMWchhBBCCPGBypcvz9mzZ8mdOzeTJ0/G29sb4K0iz7x585g+fTq3bt0iOTmZggULUr16dcLCwnB2dpbgjhDik/Zmn5eWloapqSkAT58+xc7ODoCwsDCGDx/OyZMnefToEd27d1eCO/qCIUC9evW4f/8+Bw4cwMHBIUPbri8K638SUKlUSp984MABvv32WypUqMCaNWuUthgWl2vUqEHr1q3ZunUrs2bNws3NTXnvY8eO0atXLy5fvkx8fDylSpXK0H0RQvzPkiVL6NOnD926dWPgwIHpzk3DPmvDhg0sW7aMI0eOkJCQQIECBahcuTJjxoyRe7SPYNeuXYSGhvLjjz8SExNDhQoV3gruHDx4EHg7uCM+jhEjRmBvb8+AAQOU17Zv307Tpk3RarXkzp2b8+fPY29vD7y+Lq5bt44hQ4ZQsGBBIiMjleu4EEIIIYQQQmQG+WYohBBCCCE+yKlTp0hOTqZ8+fKcOHGC6dOn4+TkRO3atTEyMkKr1aJSqVCpVPTs2ZNGjRrx/Plz7t27R4kSJbC2tsbc3FyKQkKIT56+CB4eHo6XlxfFihUDoEePHqSmpjJ58mTs7e0pUaIEAG3btiUpKYls2bJx+PDhdMGdmTNn8vPPP9O2bdsMH2Xhzf43ISEBOzs7Jcjj7u6Ol5cXq1evpnfv3oSHh5MnTx5MTU3RaDRERkZy6dIlqlatSu/evYH0xeeEhASsra05evSoBHeEyEQ6nY5t27ZhZ2dHjx490gV3AOU+zcjIiObNm1OvXj0SEhK4f/8+bm5u5MiRg2zZssk92nsyHGXH8LWkpCRCQ0OJj4/n2LFjeHp6SnAnE+l0Oq5fv86oUaOwtLTEysqKbt26AVCtWjWCgoIYP348efLkIT4+nipVqgAwZ84c5s+fj1arZdGiRdjZ2cnoU0IIIYQQQohMJSPvCCGEEEKID5KUlMT169dxdnYmIiKCgQMHUqlSJUaPHk3t2rWB9IWIPypsZNYUMEIIkZFCQ0MJDQ2la9euTJ48mVGjRhEeHk7fvn0JDQ1Vntpv1aoV69atw9LSkpiYGL788kvlPdasWcPw4cOxtrZm27Zt5MqVK8Paa1iUnzNnDtHR0cTHx1O4cGHKli1LWFgYOXPm5Oeff2bAgAHs3buXypUr88033/D111+zY8cOIiIiyJYtG/v27fvDEYISExOxsbHJsP0QQrzt4cOHFC9enKpVq7Jp06Z0I2u9L7lHez+GfenNmzexsLAgZ86cyvL4+HiMjIxkxJ0stG/fPho2bIi5uTnh4eH06NEDgOvXr7Ny5UpCQ0MBcHV15cWLFyQlJVG8eHGioqJwdXWVEJsQQgghhBAi00l4RwghhBBC/KH3edp03LhxDBky5K0Aj1ar5ejRo1hZWVGqVCl5alUI8Vl68uQJvXv3Zt26dRQrVoxLly4RHBxMt27dyJ8/v1L8S05OpmXLluzcuZMyZcrQs2dP7Ozs2LFjB9u3b8fMzIy4uDhcXV0z7El/w6L8oEGDmDZtGu7u7pQtW5abN29y9OhRChUqxIoVK6hUqRJXr15lypQpbNy4kfv37yvvU6RIEXbu3Imbm9tbbZXCvxBZ5/79+xQvXpy8efOyZ88ecufOnW65/nx9+PAhGzduVMIM4sMYhjqmTZvGsmXLqFatGsOGDSNnzpzp+sXExEQWLlzI6NGj+eKLLyS4k0n0n8G+ffuoX78+VlZWTJw4kZ49ewKvP8Njx46xZMkS7t69i4ODAzVq1KBZs2Y4ODhIcEcIIYQQQgiRJSS8I4QQQggh3snwR+sNGzZw6dIlLly4QN26dfHw8KBSpUrKuuPHjycoKIhKlSoxduxYatasybZt2+jZsyclSpRgy5YtmJmZZdWuCCFEhnNxceH333+naNGizJ8/n8qVK2P4dVulUpGWlkbPnj1Zv349z58/B8DJyYmqVasyffp08uXLlykFw1mzZjFw4EB69OhB//79cXd3JzU1lU6dOrFmzRpq1qzJ7t27MTU1JSkpiUePHrFp0ya0Wi25c+emQYMGODo6SnFTiH+hZs2aceDAASIiImjUqJHyumGgxMfHh9OnT7N27VpcXFyyqqmfJMPj6O/vz5w5cyhevDjBwcE0btz4rfXv37+Ph4cHbm5unD59GpDgzsf0ruuQ/jN6V4BnwoQJ9OrV60/fU6bKEkIIIYQQQmQVCe8IIYQQQoi3GI6cEBAQwKRJkzAxMSEtLQ2AHDlyEBYWRr9+/ZRtJkyYwODBg8mTJw+enp78+OOPpKamcujQIQoXLpwl+yGEEJlhz549fPvttzg5OXHr1i26du3KkCFDKFCggLKOYTHw9OnT3L17l2fPnlG2bFlcXFywsrLKlDBMSkoKDRs25MGDB6xdu5bixYvz6tUrdu/eTZ8+fbCysiI2NhYnJ6c/LWBKcVOIrPFH515aWhqmpqYsW7YMHx8fChQoQFRUFEWLFk233rp16/Dz86N69eosWLAACwuLzGr6Z2XUqFGMGjWK3r17069fvz+9171w4QIlSpQAJLjzT718+fKd/2bHjRuHvb29MprUmwGevXv30qBBA3LkyMG4ceOUAE9qaqrygIGMHCeEEEIIIYTIahLeEUIIIYQQf2jWrFn4+fnRqlUrunTpgpGREYcPH2bIkCHodDpCQ0MJDg5W1l+4cCGDBg3C0tKSQoUKsWrVKlxcXKRQIYT4rN2+fZtr167h7OxMcHAw69ato0OHDoSEhODm5gb8ddgls4qGt2/fpnDhwgwYMIAJEyaQlpbGhg0bCAgIwMjIiBMnTuDo6AjAyZMnKVq0KDly5FDaJ8VNIbKOYcDv119/5cmTJ6SmpqYbDTEpKYmAgADmzZtHkSJFCAgIoEqVKuTPn58FCxYwe/ZsNBoNhw4dwtnZWc7pv+H06dN8++23fPHFF0ybNi1dcOfYsWOkpaWRmprKV199Bfzvc5P74X8mJiaG5cuX4+/vj4eHB/D62nnp0iVKlCiBs7MzY8eOpV27dsDbAZ4VK1bQqVMn8ubNy9ChQ/9yBB4hhBBCCCGEyGzyjVEIIYQQQigMi8spKSkcOXKEr7/+mpEjRyrTKlSrVo3ixYvTt29fRowYgbOzM126dAGgW7du1K5dGxMTE+zs7LC2tkaj0UihQgjx2XhXCCd//vzkz58fgAULFqBWq1m+fDkqlYoRI0bg5uaGkZEROp2O8+fPY29vT758+dK9x8conhsW4bVaLTqd7q2RfNRqNTqdjmfPnvHq1Ss2b96sBHeOHz+uBHc0Gg0+Pj60bNkSf39/5X2lyC9E1tBqtcr5PG7cOJYsWcLVq1cBqFChAuPHj6dcuXLkyJGDsWPHYmFhwapVq+jWrRsmJiZky5aNly9f4uHhwZYtW3B2dpap7/6m33//nbt37zJ8+HAKFy5MWload+/eZdasWcyaNQuNRoNGoyEwMJCwsDDlGMv98N/36tUr1q5dy9KlSzE2Nsbf358iRYqgUqnw8PBg48aNtG/fnmHDhqHT6Wjfvn264I5Go8HT0xNHR0cePnyIj48POXLkoG3btlm9a0IIIYQQQgihkG+NQgghhBBCoS9IDxs2jGzZsnH16lW6deuGi4sLGo0GAGNjYxo3bkxaWhrfffcdkyZNolatWkpxulChQsr7vatwLIQQnyrDQvfevXu5e/cut2/fpnr16ri7u5MnTx6sra1ZunQpAMuWLQMgLCwMZ2dntm7dSr9+/WjatCmTJ0/+qP3jm6NnGAaM5s2bR8mSJalatSoFChSgePHiHD16lMWLFzNhwgQluOPk5KRsM3LkSC5fvvzWlDtCiMyn0+mUc9rPz48pU6ZQoUIFJkyYQEJCAkuXLqV3794MHTqURo0aYWtry6hRo2jWrBnR0dH88ssvZM+enSpVqtC8eXOcnJwkuPMP3L9/H61Wy549e6hatSrR0dGsWbOGy5cv89VXX1G2bFmmT5/OmDFjqF27NnXq1MnqJn/yzM3NGThwIMbGxsyePRu1Ws3gwYOVa1TTpk1ZuXIlrVu3VkYF1Qd49FNjFSlShGLFilG9enWWLVtGjRo1snKXhBBCCCGEEOItMm2WEEIIIYRI5/r16zRu3JhLly4Br5/uDggIUJYbFoh79uzJypUriYmJwdPTM0vaK4QQmcGw7wsKCmLatGmkpqai1WoxMzOjRIkSzJ07V+kLnz17RpcuXdiwYQM1atSgVKlS7Ny5k8ePH3P8+PF0QcePqVKlSuh0Oo4dOwZA7969WbhwIfPnz6d169aYm5szefJkBg8ejLGxMU5OTpw5cwYHBwflPVatWkVwcDDu7u5ERkZiY2OTIW0VQnyYmTNnMmTIELp06ULPnj3x8PDgwYMHVKhQgVu3bpE/f37CwsJo2rQp1tbWynZvBnX+aho/8dofTReo1WqpV68eBw4cwMzMjNTUVEqUKMGsWbMoXrw4jo6OrFy5kg4dOrB+/XqaN2+ehXvxefnll18IDw9n3rx5dOjQIV2AByA6OprWrVvj5OREaGgoHTt2BF5/ZlOnTmXq1KncuHEDlUqljMgjITYhhBBCCCHEv4V8UxdCCCGEEOkUKlSIqVOn4uXlBbweXeL69evKcpVKhVqtBqBYsWK8fPmSCxcuZElbhRAis+gLt+PGjSM8PJyvv/6aiIgIlixZgpeXF2fOnKFGjRrs3r0bAGtra1atWkWnTp04dOgQixcvJkeOHJw+fZpChQop/ejH9PjxY8zNzTlx4gQtWrTA19eXefPm0bdvXxo0aIC5uTkA3333HQ0bNkSr1VKkSBFevXrFkydP0Gg0TJ06leDgYHQ6HQsXLsTGxgatVvvR2yqE+DDXrl1j9erVVK1aVQnuJCYmUrNmTVJTUxkwYAA6nY7g4GA2b95MUlKSsu2b4QQJ7vw1jUaj9PupqakkJSWRnJwMvD5+u3fvZtCgQfTr148ZM2Zw9OhRatSogaOjIzqdjhMnTpAjRw4KFCiQlbvx2SlYsCB+fn707NmT5cuXM27cOC5fvqwsb9KkCatXr+bhw4cMHDiQ4cOHc/v2bWbOnMmCBQsoWLAgL168UM4BCe4IIYQQQggh/k1k5B0hhBBCiP+wP3qiGGDnzp2Eh4cTExNDaGgoffr0wdbWNt06/fv3Z/HixezYsYPq1atnYsuFECJzGI5Q8ezZM7y9vcmbNy8hISG4uLgo64WFhTF8+HDMzc05evQopUuXVpbFxMRgYWGBu7s79vb2GfKkv74ff/z4MQMGDGDVqlUABAQEMHToUHLkyJFufy5fvsywYcPYtGkT2bJlI2/evCQnJ/PkyROKFCnC5s2bcXNzk1EJhPiXOHLkCF5eXkRERNCwYUOSk5OpXr06v/32G1OnTqVhw4bMnj2bYcOG4eHhwZAhQ2jSpIly7ov3Z9jvzZs3j507d3Lp0iXMzc1p3bo1tWrVokKFCu/cVqfTERUVxZAhQyhUqBBr166VzyAD/NUIPHv37qVVq1Y8ffpUec3FxYWYmBjc3Nze+d1HCCGEEEIIIbKahHeEEEIIIf6jDAsTr169Ii0tDZVKRfbs2ZV1du3axejRo4mPj8fPz49WrVopBemNGzfSr18/8uTJw+7du7Gzs8uS/RBCiMwwefJkcufOzZAhQ5g8eTLNmzdHp9OhVqsxNTUFYOjQoYwdO5bGjRuzYsUKLC0tMTExSfc+GTldjf69vb29Wb9+PQC1a9dm3759AKSlpWFqaqoULe/du8fRo0dZuXIljx8/xtHRka+++gpvb2+cnJwkuCPEv8yRI0eoUqUKaWlp9OnTh9WrVzNmzBi6deuGhYUFFy9epEqVKgAkJycTGRkpUzZ9IMNQx6BBg5g6dSqurq64u7vz+PFjTp8+TcWKFfH19aVVq1ZvbR8eHs6cOXNIS0vj8OHD5M+fX4IiH5HhNfSvAjzXr19nzZo1PHr0CAcHB7p160bu3Lnl2iaEEEIIIYT415LwjhBCCCHEf9CbTxTv2LEj3RPFtWvXVp4o3r17N6NHjyYuLo78+fNTr149Ll68yOPHj0lNTeXAgQO4urpmaEFaCCGy0okTJ6hcuTJWVlZotVo2bdpEnTp1lIKsYZ9apUoV7t27x4kTJ3B0dMyS9oaGhnL9+nUePXrEzp07adiwIdu2bQNArVZjYmLyVp+dkpJCtmzZlL9Lny7Ev8eb52NCQgKVKlXC2dlZCecB3Llzh/Lly9O9e3eOHj3K8uXLcXZ2zoomf/JmzZqFr68vvXv3pl+/fri7u/Pq1St69+7NsmXLaNiwIZs2bcLU1BStVsvFixfx9vbm/v37FClShDVr1uDq6ipBkX/gfa5DfxXgeXOUUfk8hBBCCCGEEP9m8kucEEIIIcR/jE6nU360HjRoED4+Ppw/fx43NzfMzMwICgqiX79+rF69GoD69esTHBxMvXr1uHXrFvv27aNcuXIMHjyYo0ePKoUJKfIKIT5XJUuWZNasWRQqVIjk5GTWr19PQkKCMpKCsbExqampAJQqVYqbN29y/vz5TGnbu57HGTFiBMuXL2fVqlU0a9aMHTt20KhRIwBMTExISUlR+uwnT54AKMEd/ftJny5Exnvf5+nePB+vXbvGlStX0oUUACIjI7G2tqZLly7s2LEDZ2dnNBrNR2vvf4FOp+Pp06dERUVRpEgRfHx8cHd3R61Ws3nzZuLi4nBzc2P58uWYmpoq98DZsmXDw8ODPn36EB0dLcGdf8jwu8XmzZsJDQ2lbdu2zJ07l2vXrinrFSxYED8/P3r27Mny5csZN24cV65cUZa/eY7J5yGEEEIIIYT4NzP561WEEEIIIcTnRF9snj17NtOnT6dPnz7KE8UpKSn07t2bpUuXsnLlSpo1a4a5uTl169ZFrVaj0+k4cOAApUuXxtvbG0tLS7RarfwQLoT47Bg+rW9hYUH79u2B11OibNiwgQYNGtCoUSOleGtmZga8HhEjZ86cuLq6Zngb35z+MCkpCQsLC2X6Q1tbW6ZOnQq8nuqwUaNGbNu2jWzZsqHT6di5cydbtmyhc+fOeHp6AsjULkJkkn8ylVLx4sUpW7Ys27dv59ixYxQoUICdO3eyYMEC8ufPT+7cuZU+Se7RPoxKpSIhIYHTp0/Trl07ihUrRlpaGhs2bCAgIAAjIyOOHz+Og4MDADdu3MDa2hp3d3ciIiIwNjZWRuORY//3GB67wMBAZs2aRWpqKhYWFqxevZpChQoRERGhjBKqD/DA6xFFjY2NGThwIMWLF1cCQHJtE0IIIYQQQnwK5FE6IYQQQoj/GJ1OR0JCwjufKN64cSOxsbHKE8Xm5ubKaBJff/01fn5+eHp60qdPH+bNm0dycrKMziCE+GxotVrlzyqVCrVarRT8LC0tadeuHf7+/piamuLr60tkZCQPHjxQiowbN25kz549eHh4ZPiUWYbFzfDwcOrWrUvhwoUpV64cXbt25eLFi7x48YL8+fMzffp0mjdvzo4dO/jmm2+4c+cOq1at4ocffiA6OhoXF5cMbasQ4m36vqVOnToMGTLkg7Y1MzOjXbt2JCUl0aBBA8qWLUuPHj1IS0tjyZIlWFpavveoPuJtKSkpaDQarKysAFi3bl264I6TkxMAz549w9vbm127dgFgbm6OqakpIKOX/RP6YzdixAgmTpxIy5Yt2b17N4mJiQwdOpTr169Tq1Ytjh49qmyjD/D4+PiwePFiFi5cKKNOCSGEEEIIIT45MvKOEEIIIcR/jP6J4lOnTtG2bdt0TxQHBga+9UTxrVu3sLS0JG/evNSrVw8jIyNCQkIYMmQIxsbGdOnSRRnlQQghPlWGo9isWrWKQ4cOcfXqVXLlykXr1q0pX748efPmpW3btgCEhYXRr18/SpUqRcuWLdm7dy/Xrl0je/bsLFu2DGtr6380ssZf0Rc3AwICCA8Pp2DBgtSoUYNz586xZMkS4uPj8fPzo3nz5jg7OzN9+nRMTU2JjIykcOHC6HQ68ubNy6FDh8iVKxdarVaKzUJkshs3bqBSqRg3bhzW1tYMHjz4L7fR6XSYmJjQu3dv8ubNy8aNG7l37x5NmzZl6NChylRZMurLX3vzOKnVakxMTMiZMydFihRh2bJluLi4MHHixLeCOwCTJ0/m6tWr2NraZkHrP29RUVEsWbKELl26EBgYqDxosGnTJhwcHHj8+DG1a9fm4MGDVKxYEXgd4Onfvz85cuSgV69ecg4IIYQQQgghPjkqnTyKI4QQQgjxn3P58mU8PT3p1asXEyZMYNWqVQQFBWFkZMSJEyeUESOSkpKoWbMmvr6+tG/fXilC79u3j5CQEA4fPszOnTupX79+Vu6OEEL8I4YhGz8/P6ZNm4aNjQ1OTk7cv3+fpKQkvL29CQoKomTJkrx8+ZKVK1cyZcoULl26RIkSJXB3d8fT05MOHTpkaPHcMGRz+PBhmjZtSocOHfDx8aFw4cLcvHmTxYsXs2jRIlQqFRMmTKBFixaYmZnx8OFDIiIiOHfuHJaWlgwZMoS8efNKoV+ILHTu3DnGjBnD+vXrCQ4OZsSIEX+5zZthO/2oYUZGRnI+/w0jR46kRYsWlChRQjm2w4YNY8yYMeTIkQMbGxtu3LihHFedTkdkZCRBQUEUL16cVatWYWNjk8V78fl4+fIlPXv25ODBg0RHR1O6dGmeP3+Op6cnCQkJTJ06lZ9//pmRI0eSLVs2Dhw4QKVKlZTt9eeAnAtCCCGEEEKIT42Ed4QQQgghPmOGxR3908QAT548oX79+ty+fZvg4GDCw8NRqVRvPVEcEhLCpEmTiIiIoEmTJukK3Nu3b+fUqVMEBwdn/o4JIUQGCA8PJzAwkN69e9O3b1+KFSvGb7/9RqtWrThy5AidO3dm3rx5mJiY8PLlS5YvX87UqVN5/vw5a9asoWLFipiYmGRKwfDs2bNcuHCBUaNGsW3bNgoWLKj8f589e8aGDRsICAjAxcWFnTt3KqFMfT+uvyZIcVOIrGF4T3X+/HmGDh3K1q1buXTpEkWKFPng9xB/z/79+6lbty62trYcPXqUokWLKsuaNm3Kli1bqFKlCtHR0eTIkQNTU1MmT57M7NmzUavVxMXFkS9fPhm97B948zqk0+mYM2cOpqamdO/enZcvX9KwYUMuXLjAuHHj6Nq1KwB169Zl//79WFpasmXLFmrXrp1VuyCEEEIIIYQQH4WEd4QQQgghPlOGP4Rv2bKFa9eu4enpSbVq1QAYPnw4YWFhZM+eHVtb2/d+ovhdhSIpWAghPnW3bt2iadOmmJubs2TJEooVK4ZarWbLli0MGjRImTLF3t5e6V9fvHjBypUrGT58OFZWVsydO5evvvoqw8MwISEhjBs3Tpkq5ODBg6jVaoyNjZX++enTpwwdOpS5c+cSEhLC8OHDASn2C/Fv9eOPP5KamkqFChWyuin/OSNHjmTkyJHY2tpy+PBhJcCTkJBA586d2bx5MyYmJhQtWpSnT5/y4MEDihUrRnR0NG5ubhKC/EiWLFnCd999R44cOUhOTsbS0hKVSsXChQvp27cvAQEBDB06lGzZsgHQoUMH4uPjuXbtGm5ubly6dAlTU1O5xgkhhBBCCCE+WVJhEUIIIYT4DGm1WqWIMGzYMDp16sSUKVN48uQJqampwOtCRdOmTXn+/Dn58+fn6dOnyrLJkyczbNgwdDod8+bNw8bGRpmS4V0/iEtwRwjxqXvw4AEXL16kefPmSnAnKioKX19fdDodR48exd7eHp1Ox507d3j+/DmWlpa0b9+eUaNG8erVK3x8fIiJiUGj0WRoWz09PTEzMyM2NpYnT56QlpaGiYkJhs/m2NnZ0bNnT0xMTLh+/bryuhQ1hchabz5Dp/97mTJllOCOPGeXOfR99fDhwwkJCeHJkydUrVqVS5cuAWBra8vGjRuZNm0arVq1wtTUlEqVKhEeHs6+ffskuPMRrVmzhq5du7Jw4UIArKyslOvVsWPHUKlU/PDDD0pwB+Du3bt4eXmxcuVKYmJiMDMzk2ucEEIIIYQQ4pMmVRYhhBBCiM+QPkwzZMgQxowZw3fffUdUVBRNmjTBzMxMCeIsW7aMZs2acfToUfLkyUO5cuVwdnYmKCgIS0tLYmJiyJcvHxqNRgI6QojP2oMHD0hLSyNXrlwAREZGEhAQgJGRESdOnFCmFHz48CE9evTgzJkzAFhYWNC+fXtCQ0N5+fIlTZo04fLlyxna1kaNGrF582acnJy4cOECI0aMAF73/VqtVunjnZ2dMTc3Jzk5OUPbI4R4PxqNRgkXpKSkkJiYSEJCQrp1tFqtBBAywJuhSn3QXd9fDhs2jNDQUJ48eUK1atXS9eP9+vVjxYoVxMbGsn79evr374+Tk1O6sLz4Z4oWLYqpqSlbtmzh8ePHwOsQm0ajQafTkZKSooSqdDodq1at4vz583zxxRe0adMGFxeXDA/OCiGEEEIIIURGkwqMEEIIIcRnateuXcyaNYvOnTszdOhQPD09lWVGRkbodDqsra2JiopixowZtG7dGnNzc6pUqSJPFAshPlv6Qi2kH92iSJEiODg4sHz5ctatW8fQoUOVqbIcHR2V9SZOnEhcXJwSaNTpdJibm9OuXTv8/f3p0qULxYsX/+htfVPt2rWJjIzE3t6e2bNnM3PmTOB1/65v26ZNm3j+/DklS5b8KO0RQvx9hvdTc+bMwdvbm/Lly1OlShVGjRrFqVOngP/do4l/Rq1WA//r5/XHPjw8nJMnTyphRyMjIyX0ERwcrAR4atSowdWrV9O9l4WFRbr3lGD7x6HT6ShbtiyDBg0iJiaGY8eOAa9HijM2NlZGpOratSsREREEBQURHByMra0tDRs2VN5Hvq8IIYQQQgghPnUqnfwiIIQQQgjxWRo9ejTBwcEcOXKESpUqvXMdfdFCLzk5GSsrqz9cLoQQnzLD4vnBgwe5f/8+lStXJn/+/Lx69YqWLVuybds2HBwcsLKy4tKlS5ibmyvbr1y5kmHDhuHp6cmSJUvInj078LrwqFKpSEtLw9TUFPjn/adhWy9cuMCDBw949eoV+fLl44svvlDW27t3L97e3iQnJ9OtWzcGDhxItmzZ2LJlCzNnzuTFixccOXKEPHny/O22CCH+GX0fATBo0CCmTZuGm5sbxYsX58GDB5w6dYrKlSvTtWtXOnbsmMWt/fQdOHCA6OhogoKCyJkzp3L8d+zYQaNGjShUqBBRUVGUKlVK6asN++xevXoxf/58nJycOHToEEWLFpV74kywa9cuGjVqROXKlYmKiiJnzpzKsqCgICZOnKiEWkuXLs2mTZtwdXWVBw2EEEIIIYQQnw0J7wghhBBCfIY0Gg2tWrVi+/bt3Lt3Dxsbm3SFI/06xsbGJCQkYGtrm+61N9cVQohPnWHhNTQ0lDlz5mBtbc3MmTP56quvMDEx4fbt21SrVo3bt2/TpUsXFi5cqGw/Z84cJk+eDEBMTAzOzs5/2Ff+0z70zbYuWrSIO3fuAGBra0vTpk2ZP3++EhTat28f33//PY8fPyZfvnw8f/6cfPnyYWVlxZo1a6S4KcS/xOzZs+nfvz8+Pj74+PhQrFgxHj58SHBwMPPnz6dFixZERERgZmaW1U39ZKWlpVGnTh3i4uIYOHAggYGByrSHAAEBAYSHh+Pu7s7atWspXbp0uhF4jI2NuXLlCnXq1OHx48e8evWKq1evUqhQoSzcq8/DH12HDF9v374969atY+fOndSqVYuUlBSyZcsGvA7dPnjwAAsLC6pUqYK9vb1c24QQQgghhBCfFXlkRAghhBDiE/dmFlun02FsbIylpSWvXr3i9OnTb62nX0etVjNy5Eh+/PFH4H/DzUtwRwjxudGHYQICAhg1ahT169dn8eLF1K9fHxMTEzQaDfnz52fTpk24uLiwePFi3N3d8fLyonTp0gwcOBAjIyP27NmDs7MzGo3mD/vKf9qH6ts6ePBgQkNDKV++PIsWLSI6OpqyZcuybNkyqlWrxpMnTwCoU6cO69atw9HRkcTERL777jv27NlDbGysBHeE+BfQ6XQ8e/aMqKgoihYtSt++fSlWrBgajYb9+/eze/duXF1dmTt3LmZmZso0TuLDmZqasmbNGmrVqsXkyZMZM2YMDx8+VJZPmDABf39/rl69ire3N2fPnlVG3tHTh0UaNGhA7ty5lb+Lf0Z/HRo6dChz587ll19+UV7XT03Wu3dvTExMmDRpEvD6s9Avq1mzJi1btsTLywt7e3u0Wq1c24QQQgghhBCfFQnvCCGEEEJ8wrRarVIkvnfvHikpKcrfGzdujEqlUkaOMDIyQqfTpSs4h4SEsHr1ap49e5Y1OyCEEJlo3bp1zJo1ix49ehAWFka1atWUZfoCYNmyZYmPj6dnz544OTlx+vRp7Ozs8Pf35+DBg7i5uWVKGGbbtm3MmTOHbt26MXnyZDp37oyXlxfff/89AL/88gsmJibK+rVq1WL16tWoVCq2bt3K1q1bleUSyBQi8xmGQVQqFU+fPuX48ePUrFmTIkWKkJqayvr16wkICECn03HixAkcHBwAuHnzptyb/QN58+Zl1apVVKtWjWnTpr0V4Bk/fjwBAQFKgOfMmTMYGRkp/fqqVavIly8f69at4+rVq+TLl08CVR/JoUOHGDt2LD4+Pnh5eTFkyBASExOV4+vh4YGnpyfbtm1jx44dAH94vZVpzIQQQgghhBCfG/mWI4QQQgjxidJoNMqP1rNmzaJt27a0adOG1NRUAMqXL0+FChVYvXo1AwYMUII++h/AN2zYQFRUFCVKlKB06dJZth9CCJFZDh48iE6no3v37ri4uLxzHY1GQ+7cuZkxYwaHDh3i2LFjxMTEMGLECHLnzp1po9icPn2aly9f0rVrVwoUKIBarWbVqlWMGTOGggULcunSJaytrXn58qWyTZ06dVi/fj0vX75k2LBhb4U3hRCZw/Ae7dGjR8DrAI+RkZEyisuGDRsICAjAyMiI48eP4+joCMDz589p0KABUVFRWdP4z0Tu3LlZu3btHwZ4xo0bp4zA06BBA7Zv3861a9dYtGgRy5YtI0eOHKjVaqysrJQRK8U/V6NGDY4ePcrMmTNJSEhg3LhxVKhQAX9/f3766Sfs7OwYM2YM5ubmbN68GZAAqhBCCCGEEOK/Q6WTX/CEEEIIIT45Wq1WKQr5+fkxd+5cypQpQ58+fWjdurWy3vHjx2ndujU3btzAy8uL+vXr4+npydq1a4mKikKj0XD48GFcXFzSvacQQnxukpOTqVWrFklJSVy6dAl4PZWNYVFQ3w++fPkSCwuLdK+9uW5G0mq1eHt7c/jwYe7duwfAmjVrCAwMVAr9Tk5OAJw5c4bDhw/Tt29fZfu9e/fy/fffY2ZmRkBAAL6+vpnSbiFEer6+vmzatIkDBw5ga2tLjRo1ePDgAQMHDmT27NkYGRlx7NgxcubMqWwTEhLClClTWLJkCc2bN8/C1n8efv/9d1q1akVsbCz9+/dn6NChSv8JMHLkSMaNG0dKSgomJiakpaWRL18+ZdrBzOz7P3dvfte4efMm69evZ926dRw/fhxLS0t8fHz44osv2Lp1K9HR0ezYsYNatWplXaOFEEIIIYQQIhNJeEcIIYQQ4hM2ceJEBg8eTP/+/enVqxdFixZVlumLDadOnWL48OEcOnSI5ORkAExNTalYsSIrVqzA1dU100aSEEKIzPBmsVWn0/HixQtq167N5cuXOXbsGMWKFUu3nr6o+OLFC6ZOnUrXrl3JlStXprdVP7Vh+/bt2bRpE0eOHOHy5cv4+/u/FdwBqFu3LhqNhg0bNmBnZ6e8vm/fPurVq4ePjw8zZ87M8P0QQqQ/nxctWsQPP/xAs2bNCA4OpnDhwkyaNAl/f38sLCywt7fn2rVrykg8Wq2WdevWERQUhIeHBxEREdja2mbh3nz69J/HXwV4Nm/ezJEjR7hw4QLFihXjhx9+wNnZWe6PM9CbQZ7p06ezZcsW9u3bh6WlJVZWVjx8+JDhw4cTHBwsn4MQQgghhBDiP0HCO0IIIYQQn6hbt27RoEEDnJycWL58OW5ubm+toy9aPHz4kHv37nHkyBGMjY0pWbIkxYsXx8bGRgoTQojPimFBMDExERsbG2XZyJEjCQkJITw8nIEDByqvG/aD/fv3Z8+ePaxfv54SJUpkWlvv3btHnjx5lGWbN2+mRYsWNGjQgIsXLwK8FdyZPXs2oaGh9OvXj6CgoLf68gsXLij7IKNHCJGxDM/n5ORkAgMDuXLlCgsXLlSm6dNoNPTq1YtFixZRvXp15s2bR+HChQGYOnUqc+bMQaPREBcXR758+WRUxPf0Z8dJ37//VYBHv66RkREqlUrujzPJm+fN7t27mTVrFocPHyYtLY2bN2/i7Oycxa0UQgghhBBCiMxhktUNEEIIIYQQf8+dO3e4fPkyvXv3xs3N7Z2FC32h1snJCScnJ0qVKpVuuVarlcKEEOKzYVhsXbJkCdu3b6dkyZKMGDECgKpVq+Lo6Iifnx/58uXD29sbQNlmw4YN7Ny5Ew8PD1xdXTOtrbNmzWLlypW4uLgQGRkJQIkSJahduza7du3C0tKSCxcupCs0r1mzhilTpuDi4kKPHj3e2ZfrgzsSABAi4+nPsWHDhpGUlMShQ4do3769MjWpSqXC2NiY4cOHo9PpWLx4MSVKlKBkyZI8efKEhw8fUrRoUTZv3ky+fPkkPPKeDI/TgQMHuHjxIs+ePcPNzY3WrVsry3Lnzk1kZCStWrVi+vTpAEqAR99HGh5vOfYf7u9cawynpbSysqJZs2ZUrVqVGzdu4OrqSu7cueVcEEIIIYQQQvxnSHhHCCGEEOITlZSUBLz+oRx468dy/Q/dd+/excbGBisrq7dGXpBirhDic2EYRgwMDGTu3LkUKlSIRo0aKevUqVOHsLAwevXqxffff8+5c+eoWrUqZcqUYcGCBSxfvpy0tDRmzpxJ9uzZM2y0GsO2+vv7M3v2bDw9PalXr56yTuHChRk0aBAPHjzg/PnzTJo0ifLly1O6dGkWLlzIxo0bAYiKiiJnzpx/WjSVvl6IzHH37l32799PfHy8EkoA0v05f/78LFy4kKpVq7J3714uXbpExYoVqVGjBq1bt8bJyUnCCu/JsC8dOnQo06ZN48WLF8rydevWMWnSJNzc3FCpVG8FeIyNjQkICMiUKRI/V0eOHOH333+nefPmGBkZ/a0Az5vX2Zw5c5IzZ04AOReEEEIIIYQQ/ykybZYQQgghxCfq6NGjVK1alQYNGrBw4cJ0Q8obFpwbN25Mvnz5mDVrlhRwhRCfvbCwMIYPH07fvn3p1asXxYsXB9KPCLBkyRLGjx/PlStX0m1brlw5oqKicHV1zZSC4cSJEwkKCqJv3774+PhQpEgRIH0fvn//fqZPn8727dtRq9UAWFhYUL16debPn4+Li4sUN4XIIu8KKpw4cYKpU6eyevVqKleuzMKFC/Hw8ABQAjyGYQW1Wo2Jicmfvqd4m2E/OXToUMaOHUubNm3o1q0bxYsXJzQ0lDlz5tCwYUMmTJiQbhrE33//nTZt2hATE8Pw4cMZMWKETCv4gXQ6HQ8ePCBPnjxYWVmxfPlymjVrBsi/YSGEEEIIIYT4uyS8I4QQQgjxL6YvTBgWKAx/EPf29iY6Oppp06bRoUMHLCws0m2zaNEiRowYQe/evQkKCpLirhDis3b27FkaN25MqVKlmD17Ni4uLumWG/afFy5c4OLFi8THx2NlZUX58uWpXr069vb2mRKGuXHjBo0aNcLe3p6IiIi3puky7PcfP37MnTt3OHXqFACenp64ubmRI0cOCe4I8S9w+/Zt8ufPr/z9xIkTjBs3jk2bNuHn58cPP/xA7ty5leWG92o6ne5vj1giYNWqVQwaNIhmzZoxcOBAChcujE6nw8PDg3v37pGUlETt2rWZPn06xYsXV/rVu3fv0q9fP2X6QfH3LFq0iO7du5M3b16mT59O8+bNAQnwCCGEEEIIIcTfIeEdIYQQQoh/KcOCbGpqKmq1GmNjY7Jly6ass2HDBvz8/Hj06BHBwcE0adKEokWLotPpWLVqFaNHj8bMzIxdu3bJlABCiM/epk2baN68OREREbRu3fqd6/xVQTGzCo5xcXHUqFGDiRMnMmjQoHdO0fVX03Zl1LReQoj3FxISwpgxY4iNjaVixYrK66dOnSIkJIRdu3YRFBREr169yJMnTxa29POTkJBAu3btePjwIfPnz6d06dIkJSVRoUIFEhISGDp0KBcuXGDevHk0atSIsWPHUqJEibcC8RKC/HCG15/ly5fTqVMn8uTJw/Tp02nRogUgAR4hhBBCCCGE+FAmf72KEEIIIYTIbIZFhEWLFrF9+3auXbuGra0t/v7+eHp6kitXLpo3b86TJ0+YPHkyQUFBzJs3j6pVq3Ljxg3OnTuHra0t27dvJ1euXPIDuhDis3fnzh0AHBwcgLcLh/q+9dGjRzg6Or5znczqJxMTEwEwNzcH3g7i6Nv64MEDcubM+c51JLgjRNbS6XSYmppiYWGBt7c369ato0KFCgCUL1+e0NBQdDodY8eOBZAAzz/0ZsjG1tYWKysr6tatS+nSpXn58iXffPMNjx8/ZsKECXTq1Il79+4RFRXFtm3bUKvVjBs3jtKlSwP/6+8luPPhVCoVaWlpmJqa0qFDB2xsbGjWrBn+/v6o1WpatWr1wSNKGV7jnj9/Tvbs2TNyF4QQQgghhBDiX0eqN0IIIYQQ/zJarVYpIvj5+dG9e3diY2OxsrLi1q1beHt7M3nyZC5dugRAt27dmDlzJn369OH+/fusXr2ahw8f0qpVK44cOYKbmxsajUaCO0KIz56VlRUAW7Zs4cWLF+n6PZ1Op/Stbdu2ZcaMGUDGh3W0Wu07X7e0tARg3bp13L179w/b+u2339KvXz9AwjpC/NuoVCoGDx7MqFGjePbsGc2aNeP48ePK8nLlyjFy5Ejq16/PuHHjWLBgAb/99lsWtvjTpu8XN23axL179wCIjIzE19cXgGnTpnH69Gn69+9Pq1atAMiTJw9fffUVX375Jbt27WLUqFGo1eosaf/nRKPRYGpqCsDBgwfJmzcv1apV4+bNmwwbNoz169cDKAGev2IY3Nm3bx+TJ0/m/PnzGbcDQgghhBBCCPEvJBUcIYQQQoh/GX0BNywsjBkzZuDj48OBAwc4cuQIa9euRavVMnfuXGbMmMHly5cB+Oqrr5g2bRrXrl3j119/5ezZs8yaNYu8efPKVABCiM+S4QzQ+sLgt99+yxdffEF0dDTHjx9XXk9LS0OlUqHT6Zg5cybnzp3DzMyMjJ5F2jA4eeTIEWJiYpSice3atWncuDHx8fFs3LhRGYlH31atVsuiRYu4ffs2Tk5OaDSaDG2rEOLP6c9BnU6n9B36wHWfPn0YMWIEL168+MMAT8OGDQkJCWHNmjXvFWYQ77Z8+XKaN2/OzZs3gfTXgvj4eGxsbOjevTsWFhbK6+fPn6dkyZJMnjyZKVOmYGIiA5H/E4YBU39/f77//nuaNWuGhYUFefLk4fr16wwcOJCoqCjgrwM8hsGdPXv20L9/f2bMmIGdnV3G74wQQgghhBBC/ItIeEcIIYQQ4l8oNjaWpUuX8v333+Pr60uJEiV48eIFHTp0wM7OjiJFijBv3jymT5+uBHgAnJycyJs3L2ZmZkphQoI7QojPhWHxTx9wgf+FHi0sLOjQoQMPHz7E19eXrVu3kpiYqIwOsG7dOmbPnk3+/Plp3rx5ho5kYziKWmhoKC1btuSbb77hwoULpKWlAdC7d2/y58/PyJEjmTVrFjdv3lTaunr1aiZNmoS9vT09e/aUvlyILGQYVnj8+DEqlUoJ5+nP9X79+ikBnubNm3PixAll+3LlyhEUFET79u3x9vaW0RD/Af1USmPHjiUpKUnpx3U6HU+fPlWmMdNbtmwZz549o23btvj6+pI/f34JQ/5D+mM+efJkJk2aRMeOHdm7dy+7du1i3759hIWFcefOHQYMGPCXAZ43gzuBgYH89ttv7Nmzh3z58mXeTgkhhBBCCCHEv4BKl9GPGgohhBBCiA+i1WqZNm0a4eHhrF27lqpVq/L8+XMqVarEo0ePmDhxIh4eHvj6+nL27Fm6dOlC3759cXd3z+qmCyFEhjEcRWzt2rUcOnSIGzduUKhQIfr27Uv+/PmxsLDg4cOHhIeHM2/ePExMTPD09KRWrVocO3aMuLg4smXLRlxcHK6urmi12gwpohsWIwMCApgyZQpt27alXbt21K1bV1kvJSWFNWvWMHHiRC5evEjBggWpXr06169f58cff8Te3p6YmBjc3NwyrK1CiPcXGBionK/FihVT+iX9+alWqxk/fjzBwcG4ubmxdu1avvzyS2X7tLQ0TE1NZVTEf+jrr78mPj6e3bt3U6FCBeW4jhw5kpCQELy8vBg4cCBxcXEsX74cU1NTYmJicHJyyuqmfzaePHlC06ZN+fXXX9m/f/9b30MWLlxIjx49yJ8/P+Hh4bRs2RIg3bXsXcGda9euERsbS+nSpTN3h4QQQgghhBDiX0DCO0IIIYQQ/0IxMTH8+uuvdOrUiZSUFLy9vYmNjWX8+PF07doVIyMjBg0axJQpU7CxscHLy4uwsDBcXV2zuulCCPHRGRb79GEYU1NTHBwcuHfvHq6urgQEBODt7Y2dnR2PHz9my5YtLFmyhNjYWABy5cpF9erVmTx5Mvny5cuU4vmqVavo0qUL3bp1w8/PDzc3N2WZvmiZmprKuXPnmDt3Lhs3biQhIYGiRYtStWpVQkNDZfpDITKRYZgAQK1Wp5tiafDgwUyYMAEnJycOHjz4zgAPQN26ddm/fz8uLi6sWLGC6tWrZ/q+fI70x3r37t00btyY9u3bs3DhQmX5nTt3GDRoEJs3byY1NRUADw8Ptm/fnqGBzf+ix48f8+WXX1KgQAH2798P8NbxDQoKYvz48bi7uxMWFqYEeECCO0IIIYQQQgjxLhLeEUIIIYT4l9IXjHbv3s3333+Pt7c306dPx8zMDICdO3cydOhQsmfPzrVr1/jpp5+ws7PL4lYLIUTGGTFiBGPGjKFz58706dOH0qVLs3HjRlq0aEGhQoXo06cPnTp1wtbWVikinj9/npSUFAoUKICVlRXm5uYZHobRFyU7dOjAtm3bOHz4MMWKFfvD9fQePHjAq1evyJs3LzqdTkboECITGQYPnj17hrW1tbJs69ateHl5ATBmzBiGDRuGvb09sbGxeHh4KOfpq1evMDc3JywsjPXr13PhwgVKlSpFfHy8MiWe+Gvv6vcM+8t79+7RsGFDLl++zK5du6hRo4ayzcOHD/npp584ffo0+fPnp3bt2jg5OUlf+pE9ePCAypUrk5SUxIEDByhRokS65Tqdjh07dtCkSROMjY0xMTFh/fr1NGzYMN16e/bsYfDgwVy9elWCO0IIIYQQQoj/PHncRAghhBDiX0r/pPeFCxdISEjgm2++UYI7ABERERgZGREREaEEdySXLYT4XO3YsYMlS5bQuXNnAgICKF26NC9fvmTo0KE4ODiQlJTEmDFjWLp0KQkJCUoR/osvvuDLL7/EwcEBc3NzdDpdphRwExMT2b9/P25ubsroHG/S99kvX74EwMnJCRcXF0xMTJRCvxSbhcgc+j6jSZMmjB49mqSkJAB8fHxo0qQJ27dvB2DIkCGMHDmSJ0+eUL16dX7++WeMjY1JS0vD3NwcgOPHj1OqVCkWLFjA5s2bJbjzgfT93pw5c9i+fTu///47KpVK6TPz5MnD8OHDSUlJ4cCBAwCoVCq0Wi1OTk7Url2bQYMG4e3tjZOTE1qtVvrSj0Cr1Sp/zpkzJ23btuXJkyfs2LEj3XpqtRqVSkXNmjUpX748HTt2JFu2bJQqVSrdejExMQwcOJCrV68SFxcnwR0hhBBCCCHEf56Ed4QQQgghMoHhj90fytHREYCTJ08qhaS1a9dy7NgxatasSd68ebGzs0Or1aYbwUEIIT4XqampHDp0CCMjI7p160bhwoV5/vw55cuX58mTJ4SHhzNnzhxMTU2ZMWMGS5Ys4dmzZwBvhRozo59UqVTY2NiQO3duXrx4oQSGDK8F+lE+EhISiI6OfmsUHiFE5vvll1/46aefmDt3LkuXLqVHjx7MnTsXX19fvvjiC2W9YcOGMWrUKJ48eUK1atU4ceKEEtBZs2YN58+fp06dOnTq1EmZpk98mKioKPr06UOTJk345ptviI6O5rffflOWV6hQAU9PT6ZMmcLFixcxMjL6wymxZKqsv+evvr/UqFGDAgUKEBQUxOrVq1Gr1cDrBxDUajXz58/n/v37zJ8/n99//x1nZ2flXEhLS+PEiRMkJydz6NCht4I9QgghhBBCCPFfJNNmCSGEEEJkouDgYDp27EjhwoXfe5sbN27QtWtX4uPjadiwIWq1mqNHj2JpaUlcXBz58uXLwBYLIcS/w6xZs7CwsKBLly68evWKJk2acPr0aSZMmECXLl1Qq9U0bdqUHTt2UKhQIbp06UK/fv3Inj17prdVq9WSmppK69at2bx5M2PHjiUwMFBZplKplKBOjx492Lx5M/Hx8RQoUCDT2yqE+B+NRsPly5fx8fEhPj6e1NRU+vTpw8iRI5WpSQ2nXxozZgyjRo3C1NSUpk2bKiPBWFtbExcXR548ebJydz5pjx8/5urVqyxcuJAVK1ag0Wj48ssv8fb2pn///piYmDBv3jx69+5NSEgIwcHB6HQ6Cep8JIb/ziMjI4mPj+fEiRNUqlSJL7/8ku+//x6AefPmMXjwYJ4/f05AQAB169alSpUqLFu2jFmzZmFvb8+WLVveeS1++PAhaWlp5M2bN1P3TQghhBBCCCH+rSS8I4QQQgiRSTZu3EiLFi1o3Lgx06ZNw83N7b233b9/PwsXLmTNmjU4ODhQunRplixZQv78+dP9uC6EEJ+z1NRUzMzMWLFiBb1796Z3796MHDkSCwsLAKZPn86KFSv49ddfcXJy4vjx4xka3tGPnvNHTp48SfXq1cmZMyejR4+mXbt26bZdv349w4cPp2TJkixbtgwrK6sMa6sQ4v21b9+eiIgIzMzM8PX1ZcSIEVhYWCjnvOG914IFC1izZg0HDhzAwsKCEiVKsG7dOlxdXeUe7W96cySymJgYDhw4wJQpU3j+/DnlypWjRYsWtGrVivbt25OQkEB8fDw5cuTIwlZ/PgyvbX5+fsyePRtzc3Ny5crF7du3efHiBb6+vkyePBmApUuXMnPmTE6fPg2AlZUVycnJuLi4EBMTg5ub21ufqYw2J4QQQgghhBBvk/COEEIIIUQmefz4MREREQwZMoR69eoxadIkChYs+KfbvPnD9oULF7C3tydHjhxkz55dikJCiP8kf39/pk+fzsmTJ9NNZVOvXj0sLS0JCwvDycmJ3LlzZ1iB0LD/vXTpEg8ePEClUlGgQIF0I6LNmzePAQMGYGlpSa9evejYsSOWlpZERESwePFi0tLSiI2NJV++fFLMFCKLGJ57v//+O/7+/piZmXHmzBmuXr1KUFAQPXr0UKYyBVCr1ZiYmADw8uVLzp8/j62tLbly5cLGxkbu0T6CN4/h2bNn2bhxIxEREVy/fh1HR0dsbGy4fv06o0aNYujQoVnY2s/PuHHjGDp0KD169KBHjx6ULVuWQ4cO0a9fP86fP09wcDChoaEA/Pzzz8THx7N9+3YsLS0pUKAAPXv2JE+ePHIuCCGEEEIIIcR7kvCOEEIIIUQmevr0KStXrmTAgAHUq1ePjRs3Ymlp+bfeS4q8QohP2V+NWvNnfvjhB6ZNm8aqVauUqTsiIyMZOnQoPj4+DBw4EHi78PuxGLZ99OjRLFiwgFu3bgFQqlQpevTogY+PDwDPnz9nw4YN+Pj48OLFC2X0DrVaTcmSJdm4cSNubm5S3BQiixiee7du3cLR0REzMzMSExN59OgRHTt25KeffmLIkCH07NkTBwcHZZs/uhf7J/2b+GtJSUmEh4dz6NAhDh48SK5cuTh58iTOzs5Z3bTPxu3bt6lTpw4uLi7MmTMHd3d30tLS2Lt3L507dyZ79uwcPXoUJyendNvpR8h71yhVQgghhBBCCCH+nIR3hBBCCCEy2ZMnT1i6dCmFCxemSZMmWd0cIYTIUsuXL6dp06bY2Nj85br6Qvnhw4dp06YNpqamtGzZkidPnrBt2zayZctGbGwsefPmzbD2GhbrAwMDmThxIl999RWNGzcmZ86c+Pj48OrVK/z9/Rk5cqSy3eXLl1m7di1Xr17F0tKSChUq0LRp03RBACFE5jI89/SBQDMzM6Kjo7Gzs0On03H27Fl69+7N+fPnGTJkCN26dSNnzpzodDqio6O5ffs2ffr0kUD1ezIcsejvMAxGJSYmsmnTJmrXro2Li4v0pR/R0aNHqVq1KsuWLaN9+/akpaWxfv16Bg8ejJGRESdOnMDR0ZHU1FTu3LmjjCaq/3zkIQMhhBBCCCGE+HAS3hFCCCGEyAL/tHAhhBCfg8mTJ+Pn58eIESP44YcfsLa2fq/tXr58yerVq5kxYwZnz57F3NycMmXKsGbNmkwr4M6aNYvg4GDatm1L//79cXd3B6BQoUL89ttvpKamMnz4cEJCQv70fWSEDiGyhmG4YNCgQcyZM4dKlSrRp08fWrRokW7ds2fP0qtXL3766Sf8/f3p3Lkz586d44cffuD27dvcu3cPW1vbLNiLT8fx48cpXLgw9vb2//g++F3BELm3/vvedR3as2cPDRo0YM2aNXh7exMZGUlAQABGRkYcP35cGXEnMTGR5s2bM2zYMGrXrp0VzRdCCCGEEEKIz4Z8qxVCCCGEyAJSXBBCCGjUqBGXLl1i1KhRqFQqBgwY8Jcj8Oh0OiwsLGjfvj3fffcdhw4dIm/evBQoUAA7O7tMCe7cvHmT5cuXU6VKFXx8fHB3d+fZs2dUqFCBly9fMnjwYCZNmsTIkSNRqVSMGDEC+N90Ivr9UKlUEtwRIovowx9Tp05l2rRp9O/fn969eytBPEOlS5dm7ty59OnTh5CQEBYtWkRycjLZs2fnwoUL2Nraykgjf+LatWtUqlSJPHnycP78+X8c4HnXcZZ76/fzZlDH8HPYvn07NWvWxMrKily5cgEQFxeHVqslMDDwreAOwODBgzl37hw5cuTI3B0RQgghhBBCiM+Q/EoohBBCCCGEECJLFC1alMGDB9OtWzdCQkIYP348arX6T7fRF21NTU2xtrbGy8uLcuXKYWdnh1arzZQpU9RqNRqNhvbt2+Ph4cGLFy/46quvePr0KWPHjiUkJIT169cDEBoayvDhwwGU4I7hfgghss7Dhw9ZtmwZZcuWpV+/fu8M7uiVLl2ayMhI2rZtS65cuahbty6xsbEULFgQtVot5/SfyJ8/P927d+fevXtUqVKFJ0+eYGJi8pf9vfj49MGdWrVqsWjRIiW44+PjQ/fu3Tl8+DBarZbixYvz7bffMmvWLPr27YuxsfFbwZ1ly5axc+dO6tWrh4eHR5bsjxBCCCGEEEJ8TuSxFCGEEEIIIYQQWaZgwYL4+fnx6tUrPD09/9HoCZk1ik2+fPlYtmwZJUqUQK1WExQUxKVLlxg9ejStWrUCoECBAuTKlQuVSkVYWBjW1tb4+fllSvuEEO/n7t27nD17lpEjR1KgQIG/HLnL2dmZFStW8OLFC0xNTTE1NUWj0cioL39Co9GQLVs2pk+fjoWFBdOnT6dKlSocOXLko0yhJT7cwYMHOXTokDJyXVxcHHPnzsXX15dSpUphZGSEkZER7dq14/z58/zyyy8EBASkC+4sWrSIcePGkS1bNiZOnIiVlZWMPiWEEEIIIYQQ/5BKp9PpsroRQgghhBBCCCH+ewyn73jx4gWWlpZZ3KL3py9SPnr0iDp16mBlZcXBgwcxNTUFXk+RVa5cORo3bszBgwdZvXo1rq6uWdxqIYShkydPUqFCBfz8/JgwYcJby/Vhnt9++w21Wi3n8N+knzIwJSWF4OBgwsPDKVSoEPHx8Tg4OHxwgMcwJKK/jrw5HZT4c5s2baJv377cvXsXgJEjR9KpUyfy5cuX7ljOnz+fMWPGcOfOHTw9PfHw8ODy5ctcuHABJycn9uzZg5ubW6ZMWSmEEEIIIYQQnzv5ViuEEEII8ZFotdp3vi5ZaSGEeO3NflJfHNTpdFke3PnQvlpfOE5MTOTWrVvkyZNHCe6kpaUxffp0Hj58SGBgIIcPH8bV1VWmiBEii2g0mne+bmtri0ql4sCBA1y6dCndMp1Op4QRBgwYwKBBg3j16lWGt/Vzo9FolCkD7927x1dffUXFihW5fv06NWvW/OAptAyDO/v372fs2LG8fPlSgjvvSX8d/vbbb3F1dVWuW3Z2duTLl0+5FurX69GjB/PmzaNv375cvnyZqKgokpOT6dq1K4cOHZLgjhBCCCGEEEJ8RPLNVgghhBDiI9BoNErR4OrVq/z444+kpKSgVqtRqVR/WDQyJCEfIcTnzLCf3LdvH8uXL2ft2rXcvHkTlUqVpX2gYTH40aNHymvvw87Ojly5crF9+3bmzp1LWloay5YtY/HixRQtWhQjIyPlvWVqGCGyhj5YsHLlSo4dO6a8XrhwYXr37s3p06dZs2YNiYmJyjJ9v7R06VLi4+Nxd3eXc/gDGQaggoKCqFq1Kn379uXp06c4OTlx8eJFqlevzuPHj98rwGPYV+/evZuBAwcya9YsHj9+nOH78rkwMjJCrVbz6tUrHj9+TJUqVXBycqJfv36sXr0alUqlXLf0AZ4GDRowdepULl++zKVLlzh16hQTJ04kT548EtwRQgghhBBCiI9Ips0SQgghhPiHDAsJQ4cOZc6cOSQkJFCyZEm8vLwYMmQI2bNnf+eP27du3SIxMZEvvvgiK5ouhBCZwrCfDAwMZOLEicqy3Llzs3z5curWrZtVzVPUrFmTXLlyMWXKFJydndO1+130y2NiYmjUqBEvX77E2tqaZ8+e4ebmxoEDB3B1dZXpXIT4F9i+fTteXl58++23BAcHU7ZsWQAOHDiAv78/Z86cwdfXF29vbypWrIhGo2HlypWMGzcOY2Nj9u7dS+7cubN4Lz5NY8aMYdiwYfj6+tKzZ09cXFy4d+8evr6+bN26lSJFinDkyBHs7e3/cAotw/54z549DB48mCtXrhAXF0fp0qUze5c+afpjmZKSgkajYe/evfTs2ZP79++zatUqvv/+e4B0n4X+z391XRRCCCGEEEII8fdJeEcIIYQQ4iMZO3Ysw4YNo2rVqhQtWpTY2FiuXLlCkyZNWLp0Kba2tukCPHfu3KFFixY8fvyYyMhIypcvn8V7IIQQGWvy5MkMHjwYLy8vmjdvzokTJ1i4cCFqtZqVK1fSsmXLLG3f119/ze7du+nRowfBwcEfFOD56aefGD9+PAD58uWjf//+MiqBEP8ijx49YuzYsUybNo1vv/2WwYMH8+WXXwKwadMmwsPDOXr0KHZ2dnz55Zc8fPiQK1eu4OTkxP79+2V6oL/p8ePH1KxZE2NjYzZt2kSBAgWUQGNKSgq+vr7MmzeP4sWLc+jQoXcGeN4M7gQGBnLt2jViY2MluPMe3vx3qx8R1PC1NWvW8MMPP7wV4IHX05NduHCBrl27ZvkUl0IIIYQQQgjxOZPwjhBCCCHEP6TT6UhMTMTLy4tChQoxevRo8uXLR3JyMt999x27du2iXr16REZGpgvw/Pzzz7Rp04ZHjx5x+vRpnJycsnpXhBDio3qzYPjNN99gamrKtGnTcHNzA2DFihWEhoZy+/btLAvwGI6M07ZtW1avXk379u2V/vx9t09NTcXMzEzZbyn0C/Hv8uTJE8aOHcukSZNo3rw5gYGBeHp6AnDq1Cn27t3L7NmzefnyJS4uLlSvXp2AgAAJ4v0DN27coFChQnTp0oWFCxe+1T++evWKb775hpiYGDw8PDh06BAODg7Kcgnu/DOG17fFixcTHx/P1atXyZ8/P97e3tSsWZMcOXIA6QM8ERERtG7dmk2bNhEYGIiFhQUxMTHY2tpm4d4IIYQQQgghxOdNwjtCCCGEEH/Dm1OgPHr0iEqVKrFo0SJq1qypFHBfvnxJ27Zt2bRpE3Xr1iUyMhI7Oztlu3PnzuHs7IyDg4NMqyKE+GwFBARQokQJ1q5dS48ePWjatClpaWmYmpoCrwuGw4YNy9IAj2FhvlmzZmzevJnTp09TpkyZ934PfZFZphURIuu8K2RjeI/1ZwEegKSkJLRarRJoMDIykuDOP/D7779TrFgxChYsyN69e7G3t1eW6Y/rtm3baNeuHUlJSVhbW3P9+vV098sgwZ1/ys/Pj8mTJ2NhYYGNjQ2///47AN27d6dr165UqFABgLVr1+Ln58edO3f44osvuH37Nubm5sTFxVGwYEG5vgkhhBBCCCFEBpLqkBBCCCHEB9JoNEoBSP+U9uXLlzExMVEKPfqRFywsLFi9ejXffvste/fupVWrViQkJCjvVapUKQnuCCE+a6dOnSI8PJzOnTtz5MgRtFotACYmJsqfv//+e8LCwsifPz/t2rUjKioq09upHwkCYOPGjRw7duyDgjuAUtCUwqYQWUcfslm/fj0///wz8DqAo+9v7O3tCQoKYuDAgWzYsIEJEyZw4sQJZXtLS0tsbGwwMjJS7s0kuPP35c6dm0qVKvHjjz8SGRlJSkoKkH4qrDx58mBkZMRXX32FSqXi1atX6d5j9+7d+Pr6cuPGDQnuvCf9v3d4PcLdvHnz6NevH7Gxsfz666+sWLGCunXrsmjRIsaNG8ePP/4IgLe3N/PmzaNly5YYGRlRs2ZNjh07RsGCBVGr1XJ9E0IIIYQQQogMZPLXqwghhBBCCD2dTqcUcIKCgpg6dSopKSmYm5uTmprK1atXKVeuXLopAbJly8bq1auVoefr1avH3r17sbGxUd5XgjtCiM9V+fLlmT9/PmPHjuXGjRtcunRJWaZSqZTw4vfffw9ASEgILVu2JDo6Gi8vrwxtm2HxWN+/v3r1CnNzc2UkDhllQIhPg2EQOioqCm9vbzp27MiQIUNwd3dXAjxGRkbY29vj6+vLvXv3WLt2LRYWFvTv358vv/xSgjp/w1+F0AcPHsy5c+eYNWsWLi4ufPXVV1hYWCh96+7du/Hw8CAqKgqtVouNjY1yL/3ixQtOnTrFzZs3iYuLk+DOe9DpdMrnkZiYyIMHDyhbtiwDBw7E1dUVeD1FZKlSpZg2bRpLlizB3d1dCaw2bNiQ6tWro9VqMTExwdLSEo1Gg4mJ/IwshBBCCCGEEBlJps0SQgghhPgbpk2bRkBAAPXq1aN27drExsYSHR2Ng4MDBw8epHjx4ukCPMbGxqSkpPDNN99w5swZLl26RM6cObN6N4QQIkOp1Wql2Ldo0SKCgoJ49OgRGzdupGnTpui/jhoWGpcuXcqCBQtYs2YN+fPnz7C2GU6Dk5SURGpqKg4ODhn2/xNCZBzD8/nVq1e8ePECf39/VqxYQfv27Rk8eDDu7u5A+qDJkiVL6Nq1K+bm5lStWpUpU6ZQsmTJLNuPT5Hhsd+6dSsXLlzg119/JW/evLRr144CBQrw8uVLZsyYwejRo8mZMyft27enS5cuODg4EBUVxfjx43F2dmbz5s1ky5btrdDk5cuXyZ49O87Ozlm1m5+kQYMGceLECZ48eUK1atWYO3cuWq023cMIJ0+epHv37pw9e5Zjx469M7gqIVYhhBBCCCGEyBwS3hFCCCGEeA9vPlHcpEkTzMzMmDx5Mi4uLgAMGzaMMWPG4OjoyOHDh3F3d38rwJOamsqzZ89wdHSUqbKEEJ+V9+nTFi9ejJ+fHwkJCWzZsoVGjRq9M8Dz4sUL5Un/jBgFw7CtEydOZP369dy5c4dKlSrRr18/PD09sbKykoKlEJ+YAQMGcPToUQ4cOMCjR48YO3YsCxYsoFOnTukCPKmpqZiZmXHr1i1atGiBq6srR48e5ezZszg6OmbxXnw6DPvSgIAAZs2aRUpKCtmyZePly5c4OjoyduxYWrZsiU6nY9myZUybNo0bN25gZ2eHpaUld+/exdnZmbi4OFxcXKTf/Uh0Oh3t2rVj9erVWFtb07p1a+bMmfPO4ztp0iT8/f1ZsGABXbt2zaIWCyGEEEIIIYSQapEQQgghxHvQFyaGDRvGnDlzePbsGW3btsXFxYXU1FQAwsLCCAkJ4dGjR1StWpWrV6+mC+5oNBrMzMwkuCOE+OxoNBqlT9uyZQtjxoyhTZs2DB8+nOPHjyvrdenShfDwcGxtbWncuDHbtm1TiogqlUoJ8lhaWgJk2PQ1+rYGBgYSGBjI3bt3sbKyYtOmTbRv354lS5bw7NmzdG0SQvy7zZ49m0WLFlG4cGEePnyIq6sr/v7+dO/enaVLlzJu3Dhl2j4zMzM0Gg1z5sxRRoW5dOmSco8m3o++Lx01ahTh4eG0b9+ew4cPk5yczLJly7C2tqZ79+6sW7cOGxsbunfvzvbt2+nevTtlypTB3d2dfv36cfToUVxcXNBoNBLc+UhUKhXLli3D19eXZ8+esXDhQg4dOpRuNJ20tDQAatWqBcCDBw+yqrlCCCGEEEIIIQCZrFgIIYQQ4j2dO3eOmTNn8uzZMywtLVGr1cD/CkDGxsYMHz4cgJCQEKpWrfrWCDx6EtwRQnwutFqt0r8FBgYqxXAjIyPS0tIYM2YMo0aN4vvvv6dAgQJ06dIFlUrFoEGDlABPw4YNM2W0BcPg5Llz51i1ahV9+/Zl0KBB5MyZk+3btzNixAhGjBiBWq2mS5cuWFtby0gQQvwLGZ7PL1684PTp09SuXZtx48YpoyIWKlSIgIAAVCoV8+fPJzk5mc6dO9OgQQNWrlxJdHQ05cuXx9HREVNT03QjgIn389NPP7Fo0SK8vLzw9/enUKFCwOv748TERPLkyUOzZs0AsLCwoGjRosybNw+NRqMcbyMjowwbae2/QH+NMrxWabVaTExMGDt2LCqViilTphASEkJ4eDjlypVDq9ViamoKwM6dOwGUkamEEEIIIYQQQmQN+UVCCCGEEOI9lSpVilmzZuHp6cmLFy84c+aMEuDRj6wDMHz4cEJCQnj69ClFixblxo0bUowQQny29IXukSNHMnHiRFq1asXu3bv56aefGD16NG5ubgwfPpzp06dz584dADp37szkyZNxcnKiUaNG7Nu3L1PCMfq2XrhwgZMnT5KamkrPnj1xdXXFwsKCpk2bsnDhQvLly0doaCiLFi2SEXiE+JfSn88hISGMHz+emJgYGjRooEy9pB9Bp2DBgvj7+9O/f3/Wrl1Lw4YNcXFxoXPnziQlJREWFqaEGCSk9+Fu377NrVu3aNu2LYUKFUKtVrN69WoCAwPJnj07Z8+exd7enlevXimjVeqDOiYmJsrnKPfKf4/haEWJiYncuHGDp0+f8uzZMwCyZcvG2LFj8fHxISYmhl69erF7927leK9atYrly5dTtGhRatasmWX7IYQQQgghhBBCwjtCCCGEEO/0ZpFWXwBq27Yt/fv3p2jRooSHh7N27VplnTcDPAMHDiRPnjxKQUgIIT5XP//8M4sWLaJhw4YMHz6c2rVr4+7uTlBQEHPnzqV27drMmDGDrVu3Ktt06tSJkJAQPDw8MvVp/0mTJlG2bFl2795NrVq1KFGiBFqtVhmloGLFisyfPx8XFxdGjhyZLsAjhMh6hvdoN2/eJCIigrFjx/Lbb78pU+5B+lEOCxYsyKhRo1i/fj2VKlWiaNGitG3bNt10TeKvves4/f7778D/Rm1Zt24dgwcPRqVScfz4cRwdHYHXIZ969erx9OlTCep8JIYj302aNIm6detSqFAhChYsSOPGjdmxYwfwehSkqVOn0rdvX06ePMnXX3/NV199RdGiRQkODsbExIQdO3bg5OQk08YJIYQQQgghRBaS8I4QQgghxBsMn2DVarW8evWKlJQUZXnbtm0JDg7G1dWVzp07/2GAZ/z48fz888/ky5dPikJCiE/elStXuHnz5juX3b17l9u3b1OnTh3y58+PVqtVRiarW7cu/fv3x8rKisDAQK5evaps17t3b44fP46Li4uyfkbSarUUKFCAIkWKsHbtWuLi4rh165YybQu8HnmjQoUKSoBn7NixzJgxg6SkpAxvnxAivTeDBCkpKco92oMHD3B1dWXRokXUr1+flJQU1q9fz61bt94ZtsuePTvNmzdn9+7d7Nmzh0WLFuHs7CzTNf0Bw2OvD0zpj9PJkyeVZTY2NgBERUURERHB4MGDMTIy4vjx4zg5OSnrTZgwgTNnzvDbb79lRvM/e4ZTvPn5+eHv74+ZmRmBgYG0bt2a8+fP4+XlxbRp0wAwMTFh8uTJDBo0CHh93a5ZsyYbN27k4MGDuLm5odFoZNo4IYQQQgghhMhC8o1MCCGEEMKAYQFn8eLFdOrUiRo1avD111+zbNkyLl26BECbNm0YOXIkLi4utG/f/g8DPNbW1uh0OikKCSE+aRcvXqRYsWL4+Pjw8OHDt5anpaW99ZqJiYlS8PXy8qJjx44kJSVx48YN4H+FYSsrK2X9jKQvdHp5eTFu3DgqV67MgwcPWL9+vTK9iJ4+wLNgwQLMzMxYtWpVhrZNCPFu+iDB+PHjuXfvHtmyZQOgX79+BAUF8eTJE6pXr46fnx+1a9dm3759rFq1iidPnvzhe+r7HP3IiHKP9m76Y9+tWzf279+vvN63b19q1qzJmTNnAPjmm28oXbo0M2fOxM/PD5VKxdmzZ5Xgjk6nY9myZezatQtvb28KFSqU+TvzGdIH1JYuXcrs2bPp06cPK1euZOzYscyePZuePXui0+mYOXMmr169Al5fZ0ePHo2Pjw9XrlzhwYMHmJiY4OjoKMEdIYQQQgghhPgXkG9lQgghhBD/zzBk4+fnR48ePdi3bx9GRkbcvn2bLl26MGTIELZt2wZA69atCQsLUwI869evV97LsBAkU60IIT51+fPnx9PTE1NTU6XwDf8bjUE/8sL8+fO5evVqulFsUlNTAahSpQoAd+7cAcjwIuGbI3bo+2IzMzPq1q1LUFAQxYoVY/z48WzevJnk5OS31vf09GTLli3s27ePHDlyvDWlohAi4w0cOJCgoCCGDBkCQGBgILNmzcLe3h6VSoVKpaJGjRqMGDECT09PxowZw4oVK/4wwKPvC+T+7K9t2rSJxYsXM3DgQC5dukS/fv2YPXs2vXv3Jnfu3ACYm5sTEBCAvb099+/fZ8iQIVhbWyvvsWTJEkaPHk327NkZNWoUFhYW0pd+RHv27MHOzo5u3bpRsGBBZQSqyMhI3N3dOXr0KObm5srodmZmZkyZMoVevXoRHR1NUFAQ586dw9jYWM4JIYQQQgghhMhiKp18YxZCCCGESGfq1Kn4+fnh4+ND79698fDw4ObNmwwaNIgNGzbQsWNH5s6dqzz9HRkZSUhICJcvX2bz5s00btw4i/dACCE+vpcvX6LVarGysmL9+vWUK1eOggULKsu7du3KkiVLGDhwIL6+vuTLlw+1Wq2MqDNo0CDmzZvHgQMH8PT0zNC2Go6idubMGe7fv8+9e/eoWrUqOXPmxNbWlpcvX7Jv3z4CAgJ4+vQp48ePp3nz5mTPnv0v31MIkXlSU1Np2LAhBw4coGjRoly+fJnhw4fTuXNnXF1d0el0qFQqdDodcXFxDB48mPPnzzNq1Cjat2+Pvb19Vu/CJ+vJkyfKfa5Wq+Xx48cMGjSIQYMGKeEdgMePH7No0SImT56MVqulYsWKVKpUicOHD3P06FHs7OzYv3+/MjWT9KUfR2JiIqVKlaJMmTJs3rwZtVpNVFQUAQEBb01ddvToUSwsLChTpgzw+prWv39/5syZQ5MmTQgODqZ8+fJZuDdCCCGEEEIIIWTkHSGEEEL8Z705KoNOp+PBgwdERkZSpkwZ+vbti4eHB1qtlvj4eE6dOoWLiwvh4eFky5ZNmSamVatWBAYGUqVKFeUHcSGE+NxYWFhgZWXFsmXL8Pb2ZtSoUdy8eVNZPnDgQKpUqcLs2bMJCwvjwoULSnBn/fr1bNiwgTJlyuDu7p6h7dRqtUpheMSIETRp0oRGjRrRtWtXKlWqRN++fbly5QoWFhbUrVuXiRMnYmdnR2BgIBs2bHhrBB49KTYLkfnS0tIwMzNj3759ODo68ssvv+Dh4UGLFi1wdXVFo9EowR2VSkW1atUYN24cX3zxBcHBwURERPD48eOs3o1Pkk6nw97eXgmyP378GEdHR6pVq6YEd/TPAzo4ONCzZ08WL15MyZIl2blzJ8HBwVy7dg1vb2/i4uIkuPMPvfnspf7vxsbGPHnyhMePH7Nhw4Z3BncAfvjhB+bNm6eMhmdsbMz06dPp3bs30dHRHDp0KPN2RgghhBBCCCHEO8nIO0IIIYT4z/npp5+wsbEhf/78aLXadFO3/Pzzz5QtW5ZBgwYxevRo0tLSiIqKIjAwECMjI06cOIGjoyM6nY47d+7g4OCApaUl8HpUCgsLCylMCCE+afoiOLwOwhhOKQjw/Plz2rRpw9atW+nUqRPDhw/Hzc0NtVpNbGwso0aNIiYmBgcHB7755hvu3r3L2bNnMTMz4/Dhw7i6ur7V92aEIUOGMG7cOJo0aULLli1xcnJi9uzZREdHkydPHg4cOECRIkVIS0tj9+7d+Pv7k5SURHBwMO3atVP6diFE1tJoNMTFxVG7dm1y5szJgwcP6NixI+Hh4Tg4OCj9yZsj8AwdOpS4uDgWLFhAly5dZEqgv0Gr1XLhwgW+/fZbChQowOnTp3F2dmbu3LlUrlz5D/vxX3/9leTkZAoXLoxKpcLMzEzuj/8Bw2vmixcv0l2funXrxrp16+jfvz8rV64EeCu4M2HCBMaMGcOUKVPo3LlzuvdWq9VERETQsWPHTNgTIYQQQgghhBB/RkbeEUIIIcR/yk8//USpUqXo3Lkzt2/fxsjIKN0IPCkpKWg0GmxtbQFYt26dEtw5fvw4jo6OwOtpBBo1asTBgweVbS0sLAAZnUEI8ekyDO6kpqZiZGSk9GnLli3j6NGjZM+enYiICJo2bcrSpUsZOXIkN27cwMTEhBo1arBy5UoGDhyIhYUFK1as4Pr169SpU4f4+HhlpIyMDu7s3r2bmTNn0qFDB8LDw2nbti3169ena9euwOv+OmfOnACYmppSr149wsPDSU1NZc6cORnaNiHEXzO8NzM2NqZixYrExMRw4sQJatasybJly/D19eXx48dv3cupVCqqV69OaGgoXl5e1K9fX4I7f5ORkRFffPEFmzdvZt68eYwZM4Z79+7Ro0cP4uPjldFf9Mdfo9EA4ObmRrFixciWLRtmZmZvhUDF+zO8Zq5cuZLOnTuzceNGZXmTJk2wsLBg7NixvHr1ilOnTqUL7qxZs4b58+dTunRpmjZt+tb7m5iYKMGdN0clFUIIIYQQQgiRuUyyugFCCCGEEJmpZMmS1K1bl71799K3b19mzpypjMCjUqmws7MjV65cLFu2jOzZszNu3Lh3Dj0/adIkfvnllwwvQAshRGbSF7irV68OwMGDBzEyMqJXr17Mnz+fiIgIypcvT44cOVi+fDkdOnRg6dKlAAQHB1OgQAHy5s1LeHg4gwcPJikpiVy5cmFsbEy2bNkybeSFU6dO8fLlS/r27UvhwoVRq9VERkYSHByMq6srx44dw9bWllevXqHT6bCwsKBOnTqsWrWK4sWLy6g7QmQhw35ix44dXLt2jU6dOlGjRg0A9u3bR61atYiIiABg6tSpODg4KNufO3cOW1tbateuTdWqVWXUlw/w5nHSBzpLliwJvJ4eKyUlhVGjRtG9e3cWLFhAhQoVMDExQafTsW/fPlJTU/Hy8kr3PhKe+nsMp4EMCgpi7ty52NvbU7NmTWWdb775ho4dO7JgwQJUKhWnT58md+7cuLq6MnXqVBYvXoxWq2XlypXY29v/6ch38r1GCCGEEEIIIbKWTJslhBBCiP8MtVqNicnr7HKzZs3YvHkzjRs3VgI8en369GHOnDnY2NiQI0cOrl69SrZs2YDXP6JHRkYSFBREmTJlWL58OdbW1lmyP0IIkRGSk5OpXr06P/74I61atcLOzo65c+fSv39/AgICyJs3r7JuUlISHTp0YPPmzXTq1IkRI0bg6uoK8FaB0HBUn4yi0+nQ6XS0adOGPXv28OjRI9LS0ti4cSMBAQFvhTGvXbvGvn37aNu2LdmzZ1feRwr9QmQNw34jNDSUOXPmYGZmxsKFC6lfv75yL6fT6ahVqxaxsbG0bduWefPmYWlpyZYtWwgICKBhw4ZMmDBBue8Tf82w31u3bh2nT5/m0aNH5M6dm9atW+Ps7IyNjQ0JCQksX76ckSNHkitXLubNm0fZsmWJjY3lhx9+ICUlhfPnz2NpaSmhnY8kJCSEUaNG0aNHD/r374+Hhwfwv/MlLS2NsLAwFi1axN27d9P9uy9XrhyRkZHKyHdybRNCCCGEEEKIfy8J7wghhBDiP+V9AjwJCQm0bt2aXbt20bx5c5YuXaoUdadNm8aMGTPQarXExsbi7Oz8p0+wCiHEp0Tfn7169Yrvv/+e6OhoAAYMGEBYWBhWVlZvbZOUlETHjh3ZtGnTWwGejPZHhUhfX1/mzJnD9evX+fHHH+nTpw9GRkacOHFCmf4QoEaNGqhUKqKjo7GxscmUNgsh3s0w4Ofv78+UKVPw9vbG19eXChUqKOsZBnhq1qxJXFwcVatWpUKFCmzevJlHjx5x+vRpChYsmFW78knz8/Nj8uTJ6V5zdnbG29ubgQMH4uzsTEJCAitWrGD06NGYmJjg7u7OlStX0Ol0xMXFybH/iA4fPsx3331H5cqVmTJlylvXV/11UKPRcPbsWXbv3s0vv/yCpaUlNWrUoFatWtjb20twRwghhBBCCCE+AfIIkhBCCCH+U0xMTJSiz8aNG5UAD6AEeKytrZk4cSJarZYNGzawf/9+Spcuzd27d7l58yaFChVi27ZtODs7yw/hQojPipGRERqNBnNzc+zs7JTXL168qAR3DEOQADly5GDZsmV07NiRpUuXkpiYyLRp08iXL1+Gt1ff/wYGBlK6dGnatGkDQLVq1Zg+fTotWrTg999/x8TEhLi4uHTBnZkzZ3L9+nW6dev2zlCSECJz6YM7CxYsYO7cufTp0wdfX18KFCiQbj19YFqlUnHw4EG+++47Nm/ezJkzZyhatCj79u2TUUY+gGFoau7cucybN4/evXvTuXNnHBwc2Lp1K4sWLWLKlCk8fPiQ8ePHkydPHjp16oSjoyNTp07l559/pnTp0ixYsAAXFxc59h/RtWvXuH//Pu3atXtnMNbY2FiZXqtcuXKUK1furXUMp98SQgghhBBCCPHvJSPvCCGEEOI/KS0tDVNTUyD9CDwzZszAxcUFnU6HVqtl1KhRHDlyhKtXr1K0aFFq1KhBt27dyJkzpxQmhBCflTentQoLC+O3337j4sWLxMbG0rhxY1avXo2lpaXS/xlu8/z5cxo3bszFixf5+eefsbe3z5R2nz17lrJly1KxYkW2bt2Kg4MDT548oUmTJhw5cgQbGxsuX75Mzpw5lf1ct24dQ4cOxc7Oji1btpArV65MaasQ4s+lpqbSqlUrTp8+zZYtWyhVqpSybMmSJcTHx/Pbb78xYMAAvvzySyVkGBMTg5GRESVKlMDBwUHu0d7Tm6NHBgQEcPToUVasWIGbmxvwOrD5yy+/0K1bN+Li4ggJCSEgIABzc3MAUlJS+O2338iVKxdWVlZy7D8yf39/Jk2axKlTpyhbtuxbAVr9Z5icnCxBVCGEEEIIIYT4xEl4RwghhBCfvT+a1sqw6PyuAI+hxMREbGxslG1kqiwhxOfEsE+7ceMGbm5uSv+oVqvx8vJi9+7dNGnShNWrV2NhYUFqaipmZmbA6+kGbW1tefnyJcnJyTg6OmZqP9mmTRs2bdrEjh07qFmzJgB37tyhRo0a/Prrr3h5edG+fXscHR2JjIxky5YtqFQqDh8+jKurq/TpQvxLJCQkUK5cOXLmzEl8fDxqtZrY2Fjmzp3LunXrsLKyIjk5GQcHBxYsWMC333771nvI+fzhBgwYwPXr10lMTKRp06b4+fmh/7lQfy04ffo0bdq0wcjIiPj4eKytrd8Kfb75d/H+3vx3qz+WY8eOZejQoQQHBxMaGppuG/06KSkp1KhRg1GjRlG/fv3MbroQQgghhBBCiI9Efs0QQgghxGdNo9EoP4QfOnSIqKgoZs+ezalTp3j27Jmy3saNG2natClbtmyhX79+3L59G3j9Q7pOpyNHjhwASiFDikJCiM+FYT85ffp0mjdvTosWLYD/TZG1Zs0a6tevT3R0NK1bt+b58+eYmZmh0WjYsWMHo0eP5tSpU1hYWGRqcEej0QDQr18/jIyMmDRpkrIsX758xMbG8vXXX7N3715atWpFnTp1WL16NWXKlOHo0aPK1DrSpwvx72BmZkalSpU4fvw4PXv2xNvbm5YtW7J//37GjRvHnj17WLRoEYmJiUyePBmdTsebz6TJ+fxhEhMT2b59O7t27eLMmTPKPbBarU4XxPHw8KBevXpcunSJnTt3ArwV1JHgzt9jeB26cuUKDx8+TPeAgZ2dHVFRURw+fFjZJiUlBZVKhU6nY+HChVy9epW7d+9mSfuFEEIIIYQQQnwc8ouGEEIIIT4rJ0+e5Pjx40ohRz9s/9ChQ/Hy8sLb25u+fftSu3ZtmjVrxvXr15Vt3xXg0f+Q/uZ/hRDic6DVapV+0t/fnyFDhmBra4u3tzcAJiYmaDQabG1tWbt2rRLg8fb25tGjR6xbtw5fX19WrlyZbsSyjOgrtVqt8uc3+3gPDw8qVarEtm3b2LZtG/B6ekRnZ2fWrl3L4cOHWbp0KUuXLiUuLo7IyEjy588v07sI8S9jaWlJz549qVWrFgsXLuTIkSNUq1aNU6dO4e/vT6VKlWjdujWOjo44ODigUqkkMPIP6HQ6bGxs2LdvH56enrx48YL4+Hhlell9QFKj0WBhYUGjRo2A18ER8XEYXocmTpyIl5cX3377Lffv30ej0VCoUCEGDBjAlStXGDNmDLt27QIgW7ZsAERFRTFjxgyKFi1K48aNs2w/hBBCCCGEEEL8cyZ/vYoQQgghxKfhzp071K9fHzs7O9asWYOnpycA/8fefcZFcb1tHP/RixQRRAEFFAULduy99yj+7S2xNxQVQVBREexiwYYNu6go9hJb7AbsXWOJvSuKKFJ293nhZ+cBNYlJFJTc3zcmO4Uzs+yZZc419wkJCWHixIk0bNgQT09P3rx5Q3R0NPv376dSpUps376dMmXKAO8DPNoptF6+fMmqVauwt7fPzMMSQoivRhuyGTt2LNOmTaNv377069cPNzc3ZR3toKKFhQVr1qyhY8eObNu2TZluys7OjiNHjpAzZ84vWnFHW/UH0k8n8uG0LNpwUWBgIAcOHODnn3+mcePGysCzmZkZpUqVolSpUun2r9FoJLgjRAZ5+/Ytpqamn7Vu9erVWbFiBU+fPiVbtmw4OjoqU/SpVCqWLl1KQkICFStWBGSqpr/jw3OlnQrW0dGRtWvX0qpVK2JiYmjfvj1RUVHo6ekpQR61Ws2BAwcAyJkzZ2YdQpaS9jo0ZMgQZs2aRf369Wnfvj25cuUC3l+D27dvz8OHD1m8eDEnTpygYcOGlC9fnsOHD7Nnzx4MDQ35+eefsba2lmnjhBBCCCGEEOI7pqP5sL6wEEIIIcR36tWrV8yZM4dp06Zhb29PeHg4rq6utGzZkoIFCzJ8+PB0lSEGDx7M9OnTyZUrFzExMemW1axZk9jYWG7fvo2NjU1mHI4QQmSIU6dO8cMPP1ChQgWmTJmCs7OzsuzSpUs8f/4cZ2dnrKysMDMz4/Xr10ydOpXff/8dQ0NDRo8ejb29fbqwzb915MgR9u/fT/fu3ZUBTIA+ffpw6NAhhg0bRsWKFcmXL5+y7OnTp7Rv3569e/dy6NAhKleu/EXaIoT4dw4ePEi3bt1Yt24dJUqU+NN1/yyIo9FoWLlyJePGjUNfX589e/Zga2v7NZqcJaWt8JKSkkJcXBympqaYmZkp69y9e5fWrVsTExND48aNiYiIwMrKCn19faKioggICMDU1JT9+/eTI0eOzDqULGfOnDl4e3vj5eXFgAEDlGtb2s/D7du32bZtG+PHj+f+/fvA+xBV+fLlmT17tlSTE0IIIYQQQogsQMI7QgghhMgStDe34+PjWbRoEcHBweTPn59Ro0bRq1cv5s+fT5MmTdBoNKSmpmJgYAC8HwieN28eHTt2ZN68eejr6yvLnj17ho2NjTzBKoTI0qKjo2nZsiURERH89NNPANy/f58ZM2Ywf/584uPjyZ07N0OGDKFLly5YWVkp/aI2sPMlBwzfvn1LvXr1OHr0KFFRUfzvf/9Do9Hw5s0bmjZtypUrV3j8+DEFChQgICCAatWq4eLiAkBERATdu3dn0KBBTJw4EV1dXem/hchEGo2GCRMmMHz4cNzc3IiKisLd3f1v7UOlUpGQkMCIESPYvHkzenp6/PLLL0r1L/mM/7W0fXRYWBibNm0iNjaWnDlzUqFCBYKCgsibNy/GxsbcuXOHtm3b8uuvv+Li4oK9vT36+vpcvnwZCwsLdu7cibOzs5z7L0Cj0RAXF0eLFi24f/8+O3fuVK5nf+TZs2dcv36dFy9e4O7ujrW1NdmyZZPgjhBCCCGEEEJkARLeEUIIIUSWoQ3wvHr1ioiICMaMGYOxsTHJycns2rWLMmXKKAMN2hvcKSkplCpVCo1GQ0xMDGZmZulufsvAhBAiq9u8eTPNmzdn5MiRdOzYkS1btrBixQouXLhAw4YNyZMnD3v37uX169fs37+fAgUKfPU2HT9+nI0bN+Lr60v27Nl5/fo15ubmvH37lgcPHjB37lyWLVvG8+fPcXV1pUWLFvj7+2NgYECHDh04cuQIZ86cwc7OTqbUESKTvXv3jhkzZjBmzBjs7OzYsGEDxYoV++zt7969S7t27Th79ix169YlLCyMPHnySFjhM6XtA318fJgxYwaFChWiSpUqXL9+nWPHjuHq6sqQIUNo1qwZZmZm3L9/n5YtWxITE4OtrS1eXl6ULl2aEiVK4ODgIOf+C/rtt98oVaoUbdq0ISIi4pPXLO3fI9opzD4k1zkhhBBCCCGEyBpkJEoIIYQQWYaOjg4ajQZLS0u6du1KYGAg5ubmxMXFsXr1at68eaMEcfT09EhKSsLAwIASJUpw+fJlLl68iEajSTcYIcEdIURW8UfPbbi5uVG9enXGjBmDq6srPj4+vHnzhh07drB06VJmzZpFx44defDgAb/++muGtLVs2bIEBweTPXt2AgMDGTt2LI8fP8bU1JQCBQoQGhrK9u3bmTFjBnFxcUyYMIGyZcvi5+dH7ty5efnyJUFBQaSkpMiAphCZSK1WY2xszMCBAxk+fDgPHz6kefPmnD9//rP3kTdvXsaOHUtkZCRLliyR4M7fpO0D582bx8yZM+nduzfr1q0jPDyc1atX06VLF86ePcvatWsxNDRErVbj4ODA2rVr8fDw4MmTJ9y7d49GjRrh4OBAUlKSnPsvSKVSKZVB4eNrtTa4ExcXx61btz65D7nOCSGEEEIIIUTWIJV3hBBCCJHlaJ8+ffnyJREREUydOhV9fX0WLlxIzZo10dPTU6Z6AWjevDknT54kNjYWOzu7TG69EEJ8eR8OdGsr2WjFxsZy6NAhrl27RunSpWnbti0WFhbK8r59+7Jhwwb27t1LkSJFMqzdjx49okaNGty+fZvAwEB69uyJjY1NunVu3rzJli1bWL58OadOncLU1JS3b9/StGlTVq9ejYmJSYa1VwiRXtq+JzU1lcDAQMLDw8mdOzfr1q2jaNGif7r9n1UhEZ9Ho9GgVqtp1KgRd+7cITo6msKFC5OcnMzWrVsZOHAgRkZGHD16lJw5c6Y753fv3qVVq1bExsbSuXNnlixZApDue7T45zQaDXfv3qVKlSq8fPmSAwcOUKpUqXTLte9FvXr1MDQ0ZN26dRgbG2dWk4UQQgghhBBCfEVyt0MIIYQQ3zW1Wv3Ra9qb3NmzZ6dLly4MHjyY169fM3DgQLZs2cKrV6+UAYfo6Gj2799P4cKF0w1kCyFEVpF28HzBggW0bNmSQoUKUbNmTfz8/NBoNJQrVw4fHx/Cw8Pp2bNnuuDO2rVr2b59Ox4eHuTNm/ertvXDPj1XrlysXbuWsmXLMnbsWMLDw3n27Bnw/wPS+fPnx9vbmxMnTjBx4kRq1qyJtbU1M2bMkOCOEJlIrVYrfc/kyZPp3bs3S5YswcTEhKtXr9KmTRsuXLjwp/v4VEURCe78PTo6Ojx9+pQDBw5Qp04dChcuTFJSEhs2bGDQoEHo6elx7NgxJbhz+vRpZdu8efMSFRVFuXLlWLZsGV27dgWQ4M7fpH1uMu3zk2q1Gh0dHRwdHenQoQMJCQmEhITw22+/ASiV41QqFZGRkVy/fh0nJyf5/RdCCCGEEEKILEwq7wghhBDiu5V2QHr//v1cvnyZhw8fYmNjQ9u2bTE3N8fExIS4uDgWL17M+PHj0dPTw8PDgzZt2rB9+3bOnz9PfHw8hw8fxtHR8ZNPeAshxPcqbZ/m4+PDzJkzyZMnD8WKFePatWtcuXKF2rVrM378eEqXLv3RoGBYWBgzZsxArVZz8OBB8ubN+9X6ybT7ffv2Laampsqyc+fO0adPH86cOUNAQAC9e/fGxsZG2Sbt9eD169cAmJubS3UIIb4Bfn5+zJw5k0aNGlGrVi3Mzc1ZtGgRhw4dokCBAkRHR+Pu7p7ZzczSXr9+jZOTE40aNWLZsmVs2LCBwYMHo6ury/Hjx9NVNLOzs6Nbt26EhIQofeu9e/do1aoVMTExzJs3jx49emTi0Xxf0l6f4uPjiYuLI1euXOjo6GBkZKS83rlzZzZv3kzt2rUJDg7Gw8MDfX195s+fz7Rp01Cr1fzyyy/Y29tn5uEIIYQQQgghhPiKJLwjhBBCiO9S2ikTAgICmD17NgkJCcryokWL8uOPP9KlSxesra2VAM+sWbO4desWxYsXJ3fu3JQrV46ePXuSJ0+ej6aVEUKIrCIsLAwfHx969+5Nnz59KFKkCLdv38bHx4fo6Gg6depERESEMq3gyZMn8ff35+TJk7i6urJ+/XqcnJwypJ/08/Nj165d7N69m5w5cyqvnz9/nj59+nD69GkCAgLo1atXuuUfhookjClE5tu5cydNmzalffv2BAcH4+joCMC7d+8YM2YMoaGhODk5SYDnC0n7/Thtf/3u3TsqVqzIs2fP6NatGxEREejr6/Prr79ia2sLvO8zx4wZQ1hYGHPmzKFNmzbp9nPr1i2WLFnC6NGjM+XYvkdp34MZM2awatUqTpw4gZOTE/Xr12fAgAEULlwYjUbDjRs3GDZsGOvWrQOgcOHCJCYmcufOHfLnz8+uXbtwdnaWv1eEEEIIIYQQIguT8I4QQgghvmujRo0iJCSEH3/8kXbt2mFhYcGaNWtYv349z549Y8CAAfj6+pIjRw7i4uKIiIhg7ty5xMfHs2/fPooWLfpR1QYhhMhKEhISqFu3LikpKaxcuRI3NzeSk5PZuXMnffv2xdTUlCNHjqQLwmzZsoUFCxZQpkwZ+vTpg62t7VfrJ9PuNyUlhc6dO7N27VoaNmxIRESEMrAMfx3gEUJ8W8LDw+nbty/r16/H09MTeP85NzAw4N27dwwdOpSZM2dStGhRIiMjJcDzL6TtS9evX8+JEyfo3Lkzrq6u6OnpsX79ejp16kRycjK5c+fm1KlT6YI7a9asYcSIEbi4uLBmzRqyZ8/+yX1D+pCQ+LS052jIkCFMnToVd3d3qlSpwuPHj9mwYQPFihVj0aJFeHh4KNtNmzaN/fv3c+HCBQoWLEi5cuXo168fuXLlkr9XhBBCCCGEECKLk/COEEIIIb5bMTExeHp64uHhwaxZs5Snud+8eUNMTAwDBw7kzp07TJkyhS5duqCnp8erV6+YOXMmK1as4MCBA+TKlSuTj0IIIb6uGzduULBgQcaMGcOIESNITk4mOjqaoUOHppsyRaVScfHiRYoXLw7As2fPsLS0xMDA4KsN1KYdiJw9ezb37t1j9erVJCYm8uTJExo3bkxERMQfVuAZPnw43bt3TxfwEUJ8OyZMmMCwYcPYvXs3tWvXVqay0/YpiYmJeHh4cPnyZdzc3Fi5ciWlS5fO7GZ/dz6sSLlw4UJev37N6tWradq0KXp6ejx69IiJEyeyYMECSpUqRXBwMCVKlEBfX5+5c+cyZ84cdHR0lCkSJaDzZUyePJmgoCC6du1Kv379cHNzAyBnzpw8f/6c/Pnzs3bt2o9+7xMSEjAzM1PeBwnuCCGEEEIIIUTWJ3+FCyGEEOK78WHm+N69ezx58oQWLVrg6OiIRqNBo9GQLVs2atSoQUhICEZGRixevFi52W1paYm3tzfHjx8nV65cqNXqzDgUIYTIMNrB18TERDQaDZs2bVKCO7GxsdjY2ADv+9i2bdsyZ84cAGxsbDAwMEi3jy9N2zf7+vri5+fH+fPn6dKlCz4+Pjg7O7Nt2zbat2/P06dPlW2KFSvG3LlzKVu2LCNGjCAyMvKj64MQ4tuQN29eAObMmcPbt2/R19dHo9Ggq6tLUlISJiYm1KhRA3d3d65evYqXlxepqamZ3Orvj7aPDgwMZPLkybRq1YrY2FiaN2+u9LO5c+emV69e9O7dm5MnT1KrVi3Kli1LoUKFCAoKIkeOHOzfv5+8efOiUqkkuPMFnDlzhiVLltCsWTP69u2Lm5sbcXFxFC1aFD09PRo3bszNmzdp06YNp0+fBt4HsdRqNaampgDK9I8S3BFCCCGEEEKIrE/+EhdCCCHEd0GlUik3r7Vu3LiBWq0mOTkZgNTUVGUdXV1dqlatSsWKFTl27BgHDhwA3g9Om5ubY25urgweCSFEVqBSqT75upWVFba2thw4cIBZs2YxZMgQdHV1iYmJSVfRJjAwkEePHpE/f/6MajIAUVFRhIaG0qFDB+bMmcPIkSPx9fXll19+oVOnTuzdu5eOHTt+FOCZNm0azZo1o0WLFh9dH4QQGefPgtAtWrSgVKlSbN++nSVLlpCYmIiOjg7JyckYGRkB77/Pubu7M3v2bNasWYO+vn5GNT1L2bNnD7Nnz6ZVq1b4+fkpVdTSKlSoEIGBgezcuZOWLVuSL18+qlatSmhoKLt27cLJyUkqvHxBjx8/5s6dO3Tq1IlChQrx5s0batSowfPnzwkLC2PLli106dKFGzdu0KpVK06cOIGuri46OjrK3yhyfRNCCCGEEEKI/w4ZrRJCCCHEN0+j0SiDCL169cLPzw+A8uXLY2BgwObNmwGUqV3gfZDHysqKZs2aAfDu3Tsg/Q1wuRkuhMhKtP1keHg4O3bsUF7Pnj073t7e/PrrrwwdOhSA2NjYdFNNrVq1iqioKKpUqUKlSpUytN2XLl0CoE2bNsr0h6mpqTg5OTF+/Hg8PT3ZvXs33bp148mTJ8p2pUqVIioqSqkSIYTIeGkrtNy9e5cLFy7w6tUrpXqOiYkJ48ePx9ramokTJzJr1izevn2LoaEhAOvWreO3336jQYMG9OnTRz7P/8LZs2dJSEigb9++ODs7/+F6lpaWVKtWjbVr17J7925Wr15N7969sbGxQa1WS3DnCypatChbtmyhQYMGpKSk0KNHD+7cucPo0aNp0qQJAE2aNMHY2JinT59Sp04dzp8/L3+jCCGEEEIIIcR/lIR3hBBCCPHN097AnjJlCgsXLuTMmTO8ePGCwoUL4+7uzvbt2xk9ejSAMg2D9qntmJgYTExMlGkbhBAiKzty5Ah9+/YlJCSEffv2Ka83atSIxo0bk5qaSuXKlUlMTCQ5OZmUlBRmzpxJYGAgGo2GOXPmYGFhkaFTCt6/fx8Ae3t74H1wRzu1jr29PZMnT8bS0pKtW7fSoUMH4uPjgfehAW1fL4PNQmS8tEGP0aNHU7FiRYoXL06VKlUICAggISEBgHLlyjFu3Dh0dHQYOnQodevWZdKkSXTt2hVvb290dHSoU6eOsl/5PP892rDTL7/8go6OjjIt7If9uPb/37x5o0w1q6X9b6lI+WXlyZNHCcReuXKFffv2UbduXX788UdlWqwCBQpgZ2dH1apVMTAwwNraOjObLIQQQgghhBAiE8lf5UIIIYT4ZqV98jo5OZkjR47Qvn175s2bR44cObC1tWXRokVYWFgQEhKiVOTRTsMQHR3Nzz//TPny5SW8I4T4TyhRogShoaGcPn2aUaNGsWfPHuX1QYMG0bBhQ9auXUvp0qWpXr067u7uBAQEYGJiwt69e8mTJ0+6ShoZwcXFBYBFixYBKMEd7dQ6+fPnp0mTJhQrVox9+/bRsWNH4P0Af9rBZyFExtL2E4GBgYwZMwY7OztatmxJYmIioaGhtG/fnlevXpE9e3Y8PT1Zt24dNWrU4Pjx4/j7+xMZGUnevHnZt28f9vb2UnHnM33Y72nDTiVLliQlJYVr166hq6ubrh9Xq9Xo6ury5s0bvLy8uHXrllSj/AI+5xqkrTL16NEjnjx5Qu3atTExMQHe/62zZMkSrKys2Lp1K7///rt8FoQQQgghhBDiP0zCO0IIIYT4ZmkHI6ZPn05YWBi7d++mcePG5MuXD3hfnaFkyZKsX78eCwsLpkyZgoeHB127dsXT05NevXqRkpLCkiVLMDc3l0FeIUSWZ2ZmRq9evRg3bhwxMTEEBQWxe/duAGrUqMHkyZOJiIjAxcWFhIQEnJ2dCQwMZN++fTg7O6NSqTK86kXr1q2xsbFh1apVrF+/Hng/kJyUlKQMet68eZMSJUrQsmVLtm7dyrhx45T1hBAZK22w4MmTJ6xZs4YePXoQHR3N2rVrOX36NHXr1mXr1q20adOGV69eYW5ujoeHB3v37uXs2bPs2bOH06dPs2vXLpycnDKl7/keqVQqpd+7f/8+jx8/VpYVKlQIgFGjRnH58mXl9dTUVCXIM3/+fJYvX87FixczsNVZx4fVjHR0dD67Up2xsTEAK1as4MqVKwBERUWxfft2nJycSEpKIlu2bOmmCxZCCCGEEEII8d+io5FRLCGEEEJ8w86ePUupUqVwdHQE3lfTKV26tDKtitaVK1fw9fXl4sWL3Lp1C2dnZ0qVKsX06dPJmzevDAoJIbKUT/Vp2soK8H5alAULFuDn50f58uUZOXIkdevWVdZNTU1Fo9FgYGDwye0zivZnLlq0CG9vbwoVKsTAgQOV6jrwfnBz2LBhzJ8/n/z581OmTBnKly/Ptm3bMrStQoj0oqKiMDc3Z8CAAaxZs4ZSpUqRkpKCgYEBb968oX379mzZsoX69euzZs0aLCwsPrmfzOh7vkdp+/2wsDBWrVqFu7s7gYGBODk5AdC5c2dWrFhB27Zt8ff3p3jx4sr269atY8SIEdja2rJp0yasrKwy5Tiygnbt2lGjRg169eoFfP7vcN++fQkPD8fW1hZ7e3suXbqEra0thw8fxtHRUak6J4QQQgghhBDiv0nCO0IIIYT4piUnJxMdHY2vry/379/Hy8uLsLAw4P9vlGv/ff36NcnJyVy+fJmCBQtibm6OqampBHeEEFnW4sWLKVy4MBUqVADSDyC+ffuWefPmMXToUCpVqsSwYcOoV68ekH4Q+FsYLHz69CkLFixg/PjxJCcn07p1axo1asTx48eJjo7GwMCAvXv34ujoSNmyZfntt9+4efMmOXLkyPS2C/FftGDBAnr16kW5cuV48+YNx44dw9jYGH19faV/efv2Le3atVMCPGvXrsXc3Fy+l/0DaftpHx8f5s6dS/HixQkICKBZs2ZK33/r1i369+/Ptm3byJs3L/3798fW1pYjR46wdetW9PT0lKCIhKb+mRs3blCwYEHy5s3L2LFjlbDpn53PtMtGjRrF2rVrMTU1pXDhwkyYMEGZslI+F0IIIYQQQgjx3ybhHSGEEEJ8k9LewE5MTGTr1q30798flUrF9OnTad++PTo6OspgRtpBjT/6byGEyEr27NlDvXr1qF+/PmPHjqV06dJA+kHC58+fM27cOKZNm0ajRo0YOHAgderUycxm/6G4uDj279/P4MGDuX37tvJ6sWLF2LhxI/ny5eP333+natWqlChRQirvCJHJqlatypEjRzA3N+fUqVO4uLgo/c+nAjzly5dn9+7dmJmZZXbTv1vTpk3Dz8+Pfv36MWDAAPLnz//ROo8fP2bMmDHMnTtX+Y5sZWVFmTJlWLhwIY6OjhIU+Ye0v99nzpyhevXq5MiRg8DAQLp27fqX26b9m+Thw4eYm5ujr6+PsbGxvB9CCCGEEEIIIQAJ7wghhBDiG/FXN63fvHnD1q1b6dOnD3Z2dowZM4YWLVp8FNwRQois6sO+7v79+yxYsIDJkydTvXp1xowZg4eHB5A+wLNjxw4aN26Mvr4+bm5uzJ07lypVqnzVtqb9+UlJSRgZGX32ts+ePePChQvcvXsXZ2dnChcujI2NDffu3SM0NJQZM2YwadIkhgwZ8rWaL4T4E9qpsQAaNGjArl27qFOnDitWrMDW1vaTAZ6GDRty/vx5rl69Ss6cOTP5CL5PDx48oEmTJujo6LBmzRoKFCjwp+sfOnSIly9fcv/+fcqUKYObmxsWFhYSFPmXtOdPO7WvtbU1p06dIm/evH+57aeq88jfMUIIIYQQQgghtCS8I4QQQohMl3YQITIykpMnT3L16lXKly9P2bJlqV+/PvA+wLNlyxb69u1L7ty5CQ4OlgCPEOI/IW0f9+bNG7JlywbAo0ePmD9/PmPHjqVWrVoEBwcrAZ7k5GQMDQ2Ji4ujdu3aVKhQgZ07d3Ls2DFy5cr11dqatk9ft24dx44do3Xr1pQvX/4vt/2jaUd+++03Fi5cSFhYGC1atGDVqlWADHoK8bX90WdM278A1KlTh3379tGuXTtmzJiBjY3NRwGexMREEhISyJkzp0zX9A+dOnWKcuXKMWrUKAIDA//wvUlNTUVfX/+T+5Bz/2Vof69Pnz7NvXv3aNq0aWY3SQghhBBCCCFEFvDpv+aFEEIIITKQdpB3yJAhTJ06FQMDA1QqFdu2bcPY2JjOnTsTHh5OtmzZ+OGHHwDo27cvI0eORFdXl+bNm8vgrRAiS9P2cf7+/jx69IiJEyeSK1cucufOTY8ePQAYO3YsAEFBQXh4eGBoaIhKpWLOnDkkJCQwYsQIpkyZgqmp6VcbwFWr1UqfHhgYyOzZs7GysqJSpUqfFd75VEWCkydPUrFiRczMzOjYsSMLFy5UfpYMQgvx9aQN4t26dYuEhARev35NxYoVleAOvJ/Cr1atWkRGRgKkC/Do6emhUqkwMTHBxMREPrf/wqNHj1Cr1Tx//hx4//6kDelo36+4uDgePHhAiRIlPtqHnPt/T/t7nZqaSqlSpShVqhQgYVIhhBBCCCGEEP+ehHeEEEIIkWnS3uSOiIhg3rx59OrVi969e2NoaMj58+cZOHAg8+fP59mzZ6xbtw5TU1OaNWsGwIABA+jTpw9GRkY0atQoMw9FCCG+irSD58+fP+fcuXPs3LkTOzs7vL29yZ07N3Z2dkqAZ9y4cbx7944uXbrQsmVLVq1axapVqyhatCjW1tYYGRmh0Wi+2gCudr/+/v5MmjSJn376if79+yuDm3+Xjo4OHh4eBAYGYm9vT/fu3QEJ7gjxtaXteyZNmsTSpUu5efMmycnJNGrUCG9vb6pVq6aEePbt2/enAR4t+dz+c0WKFCFXrlycPHkSAH19feV90mg0ynnu2bMnLi4uuLm5YWxsnJlNzhI+vN6kpqZiaGioBKe074EEd4QQQgghhBBC/FsybZYQQgghMsWHN8J79erFpUuXWL58Oc7Ozsrrt2/f5ocffuD8+fP4+PgwefJk4P10DatWrWLy5Mns2bMHOzu7jD4EIYT4qtIOnq9YsYKbN2+yceNGLl68SEpKCr6+vgwePFiZAuvRo0csW7aMMWPG8PbtW6ysrIiLiyNv3rwcPHgQJyenDKkMEBkZSa9evejcuTNDhgxJ16cnJyeTlJSEubn5Z+3rU+2V4I4QX1faz52vry+hoaGULl2aJk2akJiYSEREBE5OTnh5edG+fft0VXhq1arF/v37adSoEcuWLSNHjhyZdRjfpT/qozUaDQkJCXTu3JlNmzbRt29fZs2aBaSfJmvlypUMHDiQ7t27Exwc/IfTZ4nPk/Y6vHz5cg4cOMCvv/6Kh4cHZcuWpV+/fpncQiGEEEIIIYQQWYmEd4QQQgiRqQYPHszz5895+fIltWrVwtvbW7lRrv33xo0bVKxYEQsLC7Zu3UqhQoUASElJISUlBVNT03Q314UQIivx9fVl7ty5lC9fniJFipCcnMzy5ct59+4dAwYMYNiwYdja2gLw7t07zp07x/jx49HX18fa2pqRI0dib2+fYf1kv379WLlyJXv27MHDw0MZjA4LC2P79u1cv36dnj170qFDBxwcHL56e4QQ/0xYWBjDhw+nW7du9OzZkyJFivDkyRM8PDy4d+8eBQoUYNiwYR8FeEqVKsX169e5desW1tbWmXgE35e0fXRKSgqvXr3C3NwcIyMjZZ3Lly9TvXp1nj17xk8//cTUqVMxNzdHT0+PpUuXMn78eHR1ddm3bx+5c+fOrEPJEtIGqXx8fJRpIJ2cnPj99995+vQpbdu2JTw8HAsLi0xurRBCCCGEEEKIrEAewRFCCCFEpnn48CExMTEcO3YMAFtb23TTK2gDPC4uLvTq1YuxY8dy/vx5JbxjYGCAgYGBsq4QQmQ1S5cuJTQ0lN69exMQEEDevHkB+N///kdYWBhhYWHo6uoydOhQcuXKhbGxMeXKlWPt2rUYGBgoFRkyKriTmprKjRs30NfXp0yZMqjVarZt28a8efPYvn07efLkISkpCX9/f969e8fIkSO/epuEEH/fb7/9xooVK6hatSq9e/emUKFCvH79mmrVqqFSqRg4cCArV65k7NixaDQa2rdvr4RMTp8+zZMnT7C2tpZKWZ8pbR89Z84cNm3aRGxsLEWLFqV27doEBQUBULhwYXbt2kWzZs1YsmQJe/bswd7ensTERH777Tfs7e3Zu3cvuXPnlmD7v6QN7kyZMoUZM2bQq1cv+vTpg7u7O5cuXaJXr16sXr0aa2trZs6cmcmtFUIIIYQQQgiRFcgdFCGEEEJkGjs7O8LCwmjRogXGxsZcvHiRGzdupFtHe+O8WLFiwPtpYYQQ4r/i5MmTmJqa0qVLF/LmzYtKpQKgXr16jB49mmrVqjF9+nSmT5/O48ePle3ShiDT/vs1aTQadHV1qVy5Mi9evKBs2bJUrVqVtm3bcvLkScLDw9m3bx9bt27F0dGR2bNn8+TJE6QYrBDfngcPHvDs2TO8vb0pVKgQb968oVq1asTFxTFp0iRGjBiBj48PN27cYM6cOURGRpKUlKRsrw1kS3Dnr6UNrvv4+ODt7c21a9eoW7cub968ITg4mA4dOhAfHw9AyZIl2bNnDz4+Pjg4OHD79m3MzMzo27cvhw8fxtnZWYI7/9CH16OnT5+ybNkyKlasiLe3N+7u7iQlJXHr1i1u3LhBwYIFPwqhqtXqjGyyEEIIIYQQQogsRCrvCCGEECJTaEvRlylThoCAAFJSUtiyZQuTJk1ixowZmJqaAiiDPmfOnEFXV1epOiGEEFmdWq3m/Pnz6Ovrp5v+RNt/enh44O3tzcGDB5k4cSLJycn4+/uTM2dOpe/UBiC/Rts+HJTX0dFBR0eH7t278+jRI3bs2IGxsTFt27Zl7Nix6Y4he/bs5M2bV5nuSwjxbalRowYLFiygdu3apKSkMHDgQK5du8aECRNo1aoVhoaGVKlSBYALFy7Qv39/smXLRqtWrZR9SHDn82jP08SJE5k9eza9e/emR48eFC9enHPnzlG9enUiIyN59+4dERERWFpaUrBgQcaPH49arebhw4fY2dmhq6uboZXWsorLly8D76safXjNvH//PhcuXGD27Nm4urqSkpLChg0bGDp0KEZGRhw5cgQbGxtSUlK4ceMGhQoVkt97IYQQQgghhBD/mPxFKYQQQohMkfbmeJkyZQgKCqJx48YsWrSI3r17Exsbqyxfv349q1evpkiRIlStWjUzmiuEEBlOV1eXSpUqER8fz759+4D/r6CjrQ7g6elJ3bp1cXFxYdq0aQQHB/Ps2bOv2i6VSqUMTm7evJlFixYxbdo0nj59SlJSEnZ2dkyaNInDhw9z8OBB5s2bpwR3VCoVS5cu5datW1SoUIHU1FSpvCPEN0ZbOaR27doAPH/+nIMHD1K5cmW8vLwwNDQEIH/+/OTLl49hw4ZRtGhRKlWqlGlt/t4dO3aMZcuW0aJFC7y8vChevDivXr1SpiOrXLkyGzZsoGfPnrx8+RJ4f40wNDTEyckJQ0ND9PXfP58nwZ3Pd/36dYoVK8awYcO4cuWK8rr2uqStJqXRaFCr1URHRzN06FB0dXWJjY3FxsYGgJSUFHr06MGGDRsy/iCEEEIIIYQQQmQZUnlHCCGEEN+EkiVLEhwcjI6ODitWrGDfvn0UKFCApKQkHjx4gLGxMZs3b8ba2lqmYRBC/GdUrFgRgJCQEFxcXKhSpQo6OjqkpKRgYGBAamoq169fp06dOpQvX55Zs2aho6NDYGCgMqj4JWk0GmVg2N/fn0mTJqGjo4NGo2Hp0qV4e3vj6elJ9uzZyZYt20fbL1++nIkTJ2JnZ8fgwYOVwWYhxLfjw+9Y169f59q1a9SqVUt5TfuZT0lJoWPHjvj7+2NgYCBVX/6h8+fPc/nyZebNm4ebmxtv3ryhSpUqPHv2jNmzZ1OoUCE6duxIVFQUurq6zJs3DwsLi8xu9nfPwMCArl27smzZMrJly8bw4cPTVeCxt7fH0NCQQ4cOYWlpybBhw5TgTs6cOZX9DBs2jIsXL0o1OSGEEEIIIYQQ/4rcKRVCCCHEN6NkyZIEBQWhq6vL3r17OX/+PMOGDcPJyYlq1apha2srg0JCiP+UJk2a4O/vz4QJExgzZgwBAQHUrFkTAwMD4H1lMu3gY7Zs2UhJSVECPAEBAeTKleuLtkc7oDlt2jSmTp1Ks2bN8PT05MyZM2zatAlfX19evXpF165dlYHl5ORkHjx4wOjRo9m9ezfZsmVj9+7d2NnZSZ8uxHegRIkSFCpUiIMHD3L48GGKFSvG5s2bWbJkCYULFyZ37txKnySf53+mQ4cOWFtbU6VKFZKTk+nUqRP3799n/PjxNG7cGGNjYwYMGECPHj1Ys2YN9+/fZ/v27ZiZmWV2079rTk5ODB8+HBMTE2bNmoVGo2HEiBEULlwYAAcHBzp16sSiRYvYsWMHVlZWHwV3li9fzubNm6lVqxYlSpTIrEMRQgghhBBCCJEFSHhHCCGEEN+UUqVKERgYiI6ODps2beL58+f4+PgAyCCvEOI/RaPRoKOjw+DBg3nx4gXz58/n+PHjDBkyhCJFinDmzBmWL1+OoaEhBQsWJEeOHEpVgLCwMDw8POjYseMXaUvaimdJSUmcOXMGT09PJkyYQL58+WjVqhUtWrSgX79+jBkzBo1GQ7du3bCwsCApKQlfX19++eUXGjVqxIQJE7C3t5c+XYjvgEajwcjIiF69ehEUFESzZs2wsLDg0aNH2NnZMX/+fExMTJT+Svx9Go2GbNmy4enpCbyvwvPLL7/QvHlzunTpokxTlj17dnLkyEGZMmU4deoU7969k/DOF+Dk5MSgQYMwMjIiNDQUfX19fH19cXd3R1dXl7Zt23L8+HHOnz9Pjx490lW1mz9/PpMmTUJfX58ZM2ZgZmYmnwUhhBBCCCGEEP+YjkY7kbMQQgghxBfyZ9Nafe6UV6dPnyYwMJDt27fTt29fZsyYIYO8Qoj/rNevXzNr1iwCAwNRq9XA+yo4hQsXZuvWrTg7OyvrnjhxgpiYGPr16/fF2zFu3DiMjY2ZMWMGISEhdOrUSenXNRoNsbGx9OzZk7t37xIYGEiXLl3Inj079+/f59atW5QsWZJs2bJJcEeIb8DfCRnExcWxd+9ewsPDeffuHW5ubgQHB0sQ7ytYsWIFnTt3ZseOHdSvX195fcCAAVy6dImlS5diamqKlZWVTCX7L6T9vb179y4HDx5k8eLF7Nu3j549e9KvXz+KFSsGwLp16xgzZgwXLlygUKFCFChQgHv37nHt2jXs7e35+eefcXZ2ls+CEEIIIYQQQoh/RcI7QgghhPii0t603rFjB5cvX+bu3bvY2dnRqVMn7OzsPntfZ86cITAwkG3btuHl5cW0adPkhrgQ4j8tJiaGhw8fcvXqVQoVKkTlypWxsbFBpVKhq6v70UD8lxzYvXr1KlWrViUpKQl9fX0iIyOpV69eup+h0Wg4fvw4PXv25Pbt24waNYqOHTumq1QgVQmEyBwf9gd/9f9/Rvt9T8IKn/Zvzu2+ffuoU6cO7dq1Y+XKlQBERUXh7+9P1apVWbx4MTo6OhLc+RfSnjt/f3+2bdvGjRs3KFq0KCdPngSgU6dOSgUegKNHj7Jv3z4iIiJITk7G0dGR2rVr069fP3Lnzi2fBSGEEEIIIYQQ/5qEd4QQQgjxxaS9ET506FBmz57N27dv0dfXJzU1FXt7eyZNmkS9evXSDeT+mbNnzxIYGMjWrVtZunQpnTp1+pqHIIQQX9U/rUz2Z4GXjBrATU5OZteuXUycOJEjR47Qo0cPJkyYgJWV1UdtPX78OH379uXUqVOEh4fTo0cPCewIkYnSBgvWrFlDbGwsx48fp2LFipQtW5aWLVv+5T4kLPL3/fTTT/Tq1YuKFSt+9vl79OgRLVu25OjRo1SuXBljY2NOnjyJhYUFhw8fJk+ePBnQ8v+GsWPHEhgYyMCBA2nbti1ly5Zl7dq1LFu2jB07dtC+fXuGDh2qVOABSEhIICUlBSsrK+XaLMEdIYQQQgghhBBfgoR3hBBCCPHFjRkzhtGjR9O9e3e6du1KmTJlWLZsGWPGjOHevXssW7aMDh06fPb+jh8/zqFDhxg8ePBXbLUQQnxdaQf3du3axdWrV3nw4AG5c+emY8eOWFtbZ3IL/5h2gFIb4Bk5ciS///47oaGhtG7dGjMzs4/WP3r0KEFBQURERMhgsxCZKG1oZMiQIcydOxdDQ0NsbW25d+8eiYmJ+Pn5MW7cOAnnfEEbNmzgf//7H66urqxcuZIyZcr8ZYBH29fevHmT4cOHc+jQITQaDSVLliQ8PJy8efNKUOQL0Gg03L17l9q1a2Nubs6mTZvImzevsvzy5cvMmDGD+fPn8+OPPzJ48GAlwCPV44QQQgghhBBCfC0S3hFCCCHEF3X27FmaNm1K6dKlCQ0NxcXFBYDVq1fj5eWFkZERFy5c+KhSw+eSp76FEN+jDyuTzZkzhzdv3ijL8+XLx6RJk6hVq9Y/7h+/lA/72Q8HKpOSktizZw9+fn48f/6cSZMm0aJFi08GeFJTUzEwMJDBZiG+ARMmTGD48OH07NmTHj16ULp0afbv38+AAQO4cOECwcHBDB8+PLObmaWEhYUxduxYrK2tWbJkCeXKlfvLbbR98Nu3b4mPj0elUpEjRw5MTEykL/2CLl68SPHixfnxxx+JiIhAo9Gg0WiU69/p06fp06cPsbGx9OzZk/79+1O0aNFMbrUQQgghhBBCiKxMRr6EEEII8UXdvn2be/fu0alTJ1xcXEhNTSUyMhJ/f38sLCw4e/YsVlZWvHv3jtTUVOB9NYrPJcEdIcT3SNt3BQUFMXnyZNq2bcvhw4d5/fo1s2fP5t27d3To0IHdu3dnajtVKpXS1i1btjB69Gil6kBMTAzPnj3DyMiI2rVrM2nSJKytrfHz8yM6OpqEhIR0+9LR0cHAwABABpuFyGR37txh0aJF1KpVi8GDB1O6dGlSUlJITEzkyZMn5M+fn549e6bbRp71+ue0320HDBjAiBEjuHXrFi1btuTVq1d/eV61fbCpqSm5c+fGwcEBExMTNBqN9KVfkJmZGebm5jx//hx4f81KG1QtVaoUbdq0ASAiIgJ/f3+uXbuWKW0VQgghhBBCCPHfoJ/ZDRBCCCFE1nL//n0AChcuDEBUVBT+/v7o6uoSExODjY0NALdu3cLb2zutvEoAAQAASURBVJvo6GiyZcuWae0VQoiMcurUKRYuXEjz5s3x9/dXKpNlz56dd+/eYW1tTd26dTOtfWq1WhkY9vPzY+bMmSQlJSnLly9fTrNmzRgxYgTOzs5KgMfPzw8/Pz90dHRo3rw55ubmmXUIQog/cO/ePW7cuMHo0aMpWLAgKSkprFu3Dn9/f0xMTPj111+xsbEhKSmJhw8f4uzsLFMD/Qt6enpKBZ3+/fuTkpKCm5sblpaW/3if8n58OWq1GjMzM4oUKcKWLVvYtGkTzZo1Q0dHJ13VuJo1a1KwYEHc3d05fvw4OXLkyOymCyGEEEIIIYTIwuTRdSGEEEJ8UdmzZwdg3bp1SsUdXV1dYmNjyZkzp7Le5MmT+fXXX7lz504mtVQIITLWrVu3uH//vlKZTKVSERkZSUBAAJaWlukqk2krM/ydymT/lrbaw9ixYwkNDaVr167ExMRw+/ZtwsPDcXFxISIigkGDBnH79m2MjY2pU6cOkydPJleuXPz000/8/PPPGdZeIcTn01bGMjY2BiA6OjrddzRtuPrdu3d0796dAwcOZFpbswpdXV2lyuTgwYNp3LgxIBWNMpJarf7k67q6ulhbW9OtWzcAfH19+eWXX5RttFXjNm7ciKGhISEhIZw/fx5ra+s/3KcQQgghhBBCCPFvSeUdIYQQQvxt2ieJ09JoNOjo6NCkSRPc3NyYMWMGRkZGGBoacubMGeVJY41Gw9KlS9m1axdt27YlX758mXEIQgiR4bRhxWLFigGwdu3aT1Ymu3HjBsOGDWPdunXKAOLXkJqair5++j8JL168SEREBFWrVsXX1xdnZ2cAunfvTuPGjenWrRubNm3CxcWFoKAgsmXLRt26dUlKSmLmzJlUrFjxq7VXCPHXPvUdDcDW1haAQ4cOkZKSwtChQz8Zrh46dCjnzp3DzMwsw9qcVXzq3Gv7WO0y7fdl8fWpVCqlmtyFCxe4c+cOCQkJODo6UrZsWfT09OjWrRu//fYbkydPpmPHjkyZMoXGjRtjYWHBypUrWb9+Pe7u7hQoUAADAwM0Go1M4SuEEEIIIYQQ4qvR0cgjP0IIIYT4DNrBhrQ3wp89e8br16/Jly+fslytVrNs2TJGjBjBgwcPiIiI4KefflL2s3jxYsaPH4+BgQF79uzBzs5OBjKEEP8JS5cupUuXLowbNw5nZ+c/HDz/6aef2LhxIzExMbi5uX3xdly4cAF3d3fg48HmgwcPUqdOHUJCQvDz8wNI10dfvXqV1q1bEx8fz7Fjx8idOzfwPgiUmpqKsbFxuuuEECLjpP3snT9/HpVKRcmSJYH3n9GWLVuydetWsmfPjoWFBTExMen6nqVLlzJ69GgqVarE/PnzZVrTvyHtuV+/fj2nT5/m6tWr5MmTh/bt21O2bNlMbuF/S9pr2+jRowkPD+fJkyfK8nbt2tGuXTuaNGkCQGBgIGPHjgXAxcUFPT09rl+/jr29PYcOHcLJySnjD0IIIYQQQgghxH+OPC4ihBBCiD918+ZNXr58iY6ODikpKcrAREhICFWqVMHFxYVatWoRFhbG27dv0dXVpV69evTu3ZscOXIwfPhwWrZsyZQpU2jatCk+Pj6kpqayfft27OzsUKlUEtwRQmQZn5pOQ/u8RJMmTXB2dmbatGn4+Pigo6PDmTNnlMFzjUbD4sWL2bt3L+3atfsqg4WXLl2iePHilCpVCuCjCgKPHz8mNTWVR48eAZCSkpKuj3Z2dqZevXrcvn2bXbt2Ke3W19dXpuOR4I4QGS9teGTy5Mm0bt0ab29vrl27BryvANOhQwecnZ2Ji4vDy8srXXAnIiKCkJAQjIyMmDRpEtmyZZPpnT6TWq1Wzr2vry+dOnVi6tSpHD58mLCwMKpVq8bkyZO5detW5jb0P0R7bQsICCA4OJgKFSqwdOlSpk2bRu3atYmKisLPz49Vq1YBEBwcTFRUFF27dkWj0WBlZUXHjh05duwYTk5OGTqFpRBCCCGEEEKI/y6pvCOEEEKIP3Ty5EnKly9Pv379CAoKInv27AAMHz6c8ePHU6RIEWxsbLh69SqPHz+mb9++TJgwATMzMx4/fszRo0eZNGkSMTExAOTPn58aNWowZswY7O3tpTqDECJL0D7hn7ZPi4uLIyEhgbx58yrrqVQqFi1axMiRI3ny5AnLly+nQ4cOyvKMqEz24MEDWrZsiYWFBevXr1cqa6SkpGBgYMCNGzeoWbMmOXLkIDY2FkNDQ+W4tP/u2bOHevXqsWjRIrp06fLF2iaE+GfSVhnx8fFhzpw5VK5cGX9/f+rUqZOuH5k9ezYTJ07k0aNHlCtXjsKFC3PlyhXOnz9Pzpw52b17N87OzvId7R8YP348w4cPp3fv3nTr1o0yZcqwZcsWAgMDOXfuHNOmTWPAgAESWs8gO3fupHXr1rRo0YIxY8bg6OgIwK1bt1i/fj3Dhw+ncOHCTJ06lZo1ayrbJSYmYmJiokwtKZ8FIYQQQgghhBAZRT+zGyCEEEKIb1fOnDkpWLAg8+fPx8TEhKFDh5KYmMjKlSvp1asXAQEB5M2bl99++43WrVszZ84cUlJSCA0NJVeuXHh6euLp6cmNGzd4/fo1rq6u6OnpYWRkJDfChRDfvVu3bmFjY4OZmZkSfgEYO3YskZGRXL58mXr16vHDDz/QrVs3DA0NadCgAffu3SMsLIwRI0awfft2KlSowN69ezl48CCWlpbs3r1bqUz2JftJtVqNvb0927Ztw9DQkGzZsrFkyRJ++uknDAwMUKvV2NnZUa1aNVatWsWPP/7IqlWr0NPTU45PrVazZ88e9PT0yJcv3xdrmxDin9MGdyZPnsycOXPo3bs3Xl5euLi4AKQLi/Tr1w9nZ2e2b9/OihUrOHv2LPnz56dr1674+vp+lb7nv+D69etERERQr149fHx8lHP/7t07Hj16hIODAx07dpTgTga6ePEiSUlJdOnSBUdHR+X32tnZmV69evHmzRuCg4PZtm2bEt5Rq9UfVZGTz4IQQgghhBBCiIwilXeEEEII8afu3r1LixYtOHXqFAEBAZQpU4ZBgwaxdetW3N3dlae9ExISqFq1KmfPnqVnz55MmTIFMzOzT+7zS1eSEEKIjHb8+HGqVKmCt7c3gYGBmJubAzBs2DAmTJiAm5sbOXLk4LfffuPly5f079+fcePGYWxszIMHDzh8+DATJkzgzJkzwPvpqGrUqEFwcDAODg5fbfA8bf+7fPlyfvzxRxo0aMD27duVdR49ekT16tW5du0aTZo0YeHChdjY2KCrq8u6desYNmwY1tbW7NixQ6nIJoTIXL///juNGjUiZ86cLFq0iIIFCyrLdu/ezZMnT3j37h3dunVTXn/y5AmpqanY2tqio6OTrsKW+HsOHDhAzZo1Wbx4MT/++COpqalERUUREBCAjo4Ox48fx8bGhqSkJJKTkzE3N09XMUl8Odrz2r59e1avXs3hw4epVKnSR7/bFy5coFmzZty/f5/ffvtNqcwjhBBCCCGEEEJkFrlLIIQQQog/pNFoyJs3L+vXr6dUqVJMnjyZ8PBwHBwccHd3JzU1FV1dXVJTUzEzM+Pw4cOUKFGC+fPnM2TIEN68eQO8v4melgR3hBDfuxw5cmBvb8+cOXOYPHky8fHx3LlzhxUrVtC7d29+/vlnDh48yL59+3BxcWH69On4+fnx7t077O3tad26NbGxsVy5coXY2FjOnTvH3Llzv2hw58PnNFJTU9P1v1WqVKFZs2bs3LmTxo0bK6/nzp2bvXv34u7uztatWylVqhQ1a9akWrVqdO3alcTERFavXk327Nk/6t+FEJnjxYsXXL16lfr161OwYEGSk5O5ePEi/fr1o379+nTq1IkePXrQoUMHkpKSALC1tcXe3h59fX2pMvIvxcfHA2Bvbw9AVFQU/v7+6OjoEBsbi42NDfA+MFW5cmVu3LghwZ0v5MPrkPa8VqtWDYCjR48C73+3014X3d3dqVu3LsnJySQmJmZQa4UQQgghhBBCiD8mdwqEEEIIAaQf5FWr1ahUKmWQ19HRkejoaNzd3dm9ezfXrl3j4cOH6Ovro9Fo0NfXJzU1lWzZsqUL8PTs2ZO3b9/K4IQQIstxcXFhz5495M+fnwkTJjBjxgxOnTqFnp4e/fr1w9HRER0dHYoVK8aRI0dwc3Nj1qxZ+Pn5KQPn+vr6uLq64uHhgZmZGUZGRmg0mi8W3NH24c+ePUOlUqGv/37W5AkTJnD58mXy5cvH7NmzadGiBTt27EgX4MmTJw979uzBz8+PfPnyceLECeLj4/nf//7Hr7/+ipOTEyqVSvp3ITKBSqX66DVtkHrFihUcPnyYUaNG0aJFC5YvX063bt2YOXMm1apVIzIykoiIiExoddbwR4HFbNmyAbB+/XqWLFmCv78/urq6xMbGkjNnTmW9iRMncuvWLeLi4jKkvVld2uvQpUuXOH36tLLM3d0dAwMDRo4cyY4dO4D3DxCkpKQo6zx48AB7e3usrKwytuFCCCGEEEIIIcQnyLRZQgghhPjTaazmz59PsWLFqFixInfu3KFz584cPHiQLl26MGnSJKytrZXtU1NT0dfX582bNxQpUoT4+Hhu3rwpN8SFEFmOtt+7ceMGzZs358aNG1StWpXExEQOHjxISkoKBgYGSr8YFxdHpUqVuHr1Kv3792fSpEkYGRl99WlT6tati4GBAUuWLMHW1paBAwcSFhZGaGgo/fr1w9DQkAcPHtC/f382bNhAw4YN2bZtm7K9tlrP9evXyZs3L7q6uhgbG8vUOkJ8A86ePUuhQoUwMjICoE+fPsybNw94H1IoXbo08+fPp0CBApibm7Nv3z7q1KnD2LFjCQgIyMymf5fS9nu//vorKpWKChUqKK/Vq1ePAwcOYGlpiampKefOncPCwgJ4f81YsWIFI0aMoHr16oSHh2Nqapppx5IVpH0/Jk2aREREBLly5WLatGmULl0agGnTpuHj44OrqysTJ06kWbNmyvbR0dH07duXKlWqsGLFCoyNjTPlOIQQQgghhBBCCC39zG6AEEIIITKfNrhToUIF9PX1OXz4MPB+EGjhwoUsWLCAMmXK4OjoyLJly2jdujWLFy8mZ86c+Pv7kz179o8q8Fy5coVXr15hZWX1p+EgIYT41qXtw9RqdbrqOC4uLmzYsIHmzZuze/du7OzseP78uRJs1PaLVlZWHD16lEqVKjFz5kxev35NeHg4hoaGX63d9+/fx8rKiujoaIYPH45KpWLJkiX4+fnRsmVLDA0NUavV2NvbM3PmTAA2bNhA48aNlQCPWq3G0NAQV1dX5Rx8qepAQoh/burUqQwZMoSVK1fSvHlzTExMmDt3LlWrVuXVq1fY2dlRt25dpSKMRqPh4MGDmJmZUaJECeU1+X72edRqtdLvjRkzhvnz5/Pq1SuOHTuGm5sbBgYGDBw4kEePHnHhwgUmTpyoBHcAFi5cyKRJkzAxMWHChAmYmprK+f8X0l6HfHx8mD17NjVq1MDHx4fSpUsr53bQoEHExcUREhKCp6cnvXv3pmDBgty4cYPNmzejr6/PtGnTMDY2lvdDCCGEEEIIIUSmk8o7QgghhADg6dOneHp6cvToUVq3bo2dnR0zZszA29sbX19f7O3tlXXv3LnD//73P06dOoWvr2+6AI+Ojk66J2GlOoMQ4nv2Z4N5CxcupGTJknh4eHDjxg06dOhAbGwsvXr1YsKECVhaWiqVddJW4HFzc0OlUnH9+vWvXpns9u3bzJgxg+nTpwPQq1cvRowYgYODg3Js2jY+ePCAAQMGEB0dTaNGjdi6dSsg/bgQ36KVK1cSEhLCs2fPmDlzJj/88MMfVnLRaDSsW7eOwMBAbG1t2bRpk1RF/BvSXgd8fX2ZPn067du3p0OHDtSrV09Z7+3bt6xYsYIpU6Zw7949SpcuTbly5Th9+jSnT5/GxsaGPXv24OzsLP3qFzJv3jy8vb3p3bs33t7e5MuXT1mW9n2bP38+48aN4/HjxyQlJZEjRw6KFy/OkiVLcHR0lPdDCCGEEEIIIcQ3QcI7QgghhFBubj958gRvb2/WrFkDgL+/P/7+/umeHNb6qwCPEEJkJeXLl8fS0pJdu3YB7yuTRUREsGDBAtq1a4eBgQE3btygZcuWXLhwgYCAAPz8/DAzM/sowPPq1Svevn2LnZ1dhvSZvXv3Zv78+QB4enqyYMECcuTIkW6dTwV4KlasyJEjR75q24QQ/4xGo2HDhg0EBgby8OFD5syZg6enpzKFVlohISFERESgUqk4dOgQjo6OX33KvqxoxYoVdOvWjZ49e+Lj44Ozs7OyTNuXv3v3jlOnTjFz5ky2b9/O69evcXd3p2bNmvj7+2NnZydBkS/k7du3eHp6cu3aNbZt20bhwoU/WiftNfbq1avExcVx9epV3N3dKVCgAJaWlvJ+CCGEEEIIIYT4Zsi0WUIIIYRQKi/Y2tqSkpKivH7y5EkluJOSkoKBgYGyzNHRkfXr19OyZUumT59OQkIC48aN+2TQRwghvmcPHz5ErVazZ88eunbtipWVFfPmzWPAgAHUqVNH6RtdXFxYt24dnp6eTJgwASBdgEdfXx+VSoWlpeVXHTDU7lf7r6mpKV5eXrx48YJVq1ZhYmLC+PHjyZs3r7KNjo4OGo0Ge3t7ZsyYQVxcHDExMTx9+pScOXN+8TYKIT7Pp/oJbfDG09MTjUbDyJEj6du3LwAtWrRQpuOLjY2lXbt2PH36lFKlSrF8+XKpMvIPaAMgO3bswNzcHC8vr3TBHfj/PtTY2JhKlSpRqVIlpcqLo6OjEt6Uc//lPHv2jGPHjtGiRQsKFy78yTBs2upybm5uwPtpgrXSTocmhBBCCCGEEEJkNqm8I4QQQoh0AgMD+f3333n27Bm7du2iadOmbNq0CUAZeEh7c/zu3bvUqFGDlJQUzp07R/bs2TOx9UII8WVp+7v79+/Tv39/Nm7cCEBAQAC+vr6f7PNu3LiBp6cnV65cwd/fXwnwZHRlspiYGMqXLw9AUlIST58+JTg4mAULFtChQwfGjx9Pnjx50m3z+vVrzM3NuX//PkZGRtjY2EiFDiG+Ab/99huurq7K/2s/lxqNhujoaAIDA3n8+DFz5syhefPmSgWefv36UaBAATp37oy1tbWER/4BjUbDy5cvKVq0KI6Ojvz6669/Gqp69+4dxsbGaG+3aYM9Upnyy7py5QrFixenatWqbNy4EXNz83TLte/Hs2fPOHToEJ6enpnUUiGEEEIIIYQQ4vPIHVghhBBCACgDDMHBwaxYsYLIyEiaNWvGli1baNasGQD6+vokJycrgw8vX74kb968HDp0iNjYWGXaLCGEyCq0T+07ODik698uXLigBHfSViyD9xV4NmzYQKFChQgNDWXkyJG8efMmQwduJ0yYQMWKFZXpsoyMjMiTJw8+Pj707NmTlStX4u/vz507d5RttmzZQufOnbl27RoODg4S3BHiGzFu3DgKFSrEjh07lNd0dXVRq9Xo6Ojg6emJv78/arWaQYMGsWXLFt68eQPA7NmzGTBgANbW1lJl5B/S0dHB0tKSnDlz8vbtWwD09PRQq9XKOtq+Mi4uTnmfdHR0lH5fgjtfXoECBShXrhxXr17l7t27wPsHDYB0165BgwYRERHBs2fPMq2tQgghhBBCCCHE55C7sEIIIYQA/v+pYHg/RYOVlRVhYWEfBXgMDQ3RaDTs2LGD4cOHc+LECezt7cmdO7cyiCSEEFmJdgAwX758tGnThho1arBlyxbatWsHgIGBgTJgqO1HtQEeS0tLNmzYoCzPKPnz58fBwYHevXuzaNEi5XVXV1clwLNq1SoCAgI4duwYq1atYtiwYfzyyy+Ympoq60twR4jMZ2lpSe7cuenQoQM7d+5UXtcGeHR1dencuTMNGzbk0aNHDBgwgKioKJKSkgCUwI58nv8ZlUrFu3fvcHBw4MKFC4SFhQH/f/41Go1ybn18fPDy8lLCJOLv0V5DP+dhAF1dXerXr8+DBw/o1q0b7969Q19fX1kGsHbtWn755Rfs7Ow+qswjhBBCCCGEEEJ8a2TaLCGEEOI/7o/K+GsHg+7du0f//v3ZtGkTTZs2JTw8nH379hEcHMybN284ceIEuXLlyoSWCyFExviwn3z06BFdu3Zl586dtGnThsjISACSk5MxNDQE4NWrV1haWnLr1i2MjIyws7PL8GlTtmzZogwiL1iwgG7duinLrl+/zvTp01mwYAEpKSkYGRlha2vL/v37yZcvn0ytI8Q3ZsmSJYwYMYL4+HjWrFlDw4YNlWVJSUkYGRmxYsUKpk6dypMnT7CwsODEiRPpwnjiz/1Vv3f48GFq1KiBs7Mz48ePp1WrVsoytVrNunXrGDlyJMWLF2fJkiVy7v+BZ8+eYWNjA/zx3yhpvXz5ko4dO7J9+3ZKly7N5MmTKVSoEHZ2dsydO5fp06ejVqs5cOAA9vb2GXEIQgghhBBCCCHEPybhHSGEECKL+3DKk78zIJs2wDN48GDWrVuHsbExarWa3Llzs2/fPvLnzy/TqgghsjztIKK2v7tx4wb9+/f/KMADsHPnTnbs2EHXrl0pUaIE8Pf63r/jw/1qK6BpBzw3b95M//79PxngefDgAYcPHyY6OhonJye8vb2xt7eX4I4Q35C037EWL15MYGBgugBP2uVt27bl9evX9O/fnxIlSmBnZ5eZTf+upO33Nm7cyJUrVzA1NcXd3Z1atWop682YMYMhQ4aQI0cOvLy86NChA6ampixbtoyFCxeSmprKwYMHyZMnT4YHNr93R44coWrVqqxcuVKpbPdn51D7u//8+XN69+5NdHQ0Ojo6ZM+eHT09PZ4/f06BAgXYuXMnzs7Ocm0TQgghhBBCCPHNk/COEEIIkYWlveF9/fp1ChQooCwbNWoUdnZ29O7d+0/3ob0x/vjxY1asWMH58+cxNTVlxIgR2Nvbk5qaqpSoF0KI793nDLZq17l58yZeXl5KgGfmzJns3r2boKAgEhMTOX78OLa2thnSzhMnTuDh4QF8OsDj5eXFvXv3WLx4MT/++GO6fWmnfdHT05PBTSG+QX8U4ImIiKBu3bpYWlqyevVqRo4cSZ8+fRg0aBDw9UKDWZmvry+hoaHK/xsYGODn50dwcDDwvqramjVr8PLyIjU1FTMzM1QqFUlJSRQtWpRNmzZJUOQfioyMpEuXLiQnJ7N27VpatmwJ/Pl1WXue4+Pj2bZtG7t27eLatWvY2tpSqVIlOnXqRK5cueT9EEIIIYQQQgjxXZDwjhBCCPEf0KxZM3bu3MnJkydxd3enf//+zJ49m5CQEAYOHPiXZf0/rDiRkpKCgYGB3AgXQmQpafu0v+rf0gZ4Bg0axJYtW8iWLRupqanY2tryyy+/fPHKZOfOnSM5OVkJ6WjbMHr0aMaMGcO8efPo0aMH8HGAJyoqijZt2gAQERHBTz/99FnHKYT4NqTtS5YuXUpISAh37tyhXLlymJqacvToUWxsbDh8+DAODg6Z3NrvR9pgyNy5c/Hx8aF169Y0b96chIQEhg0bxr179/Dy8iIsLEzZ7ty5c0RFRfHbb79hbm5OuXLlaNGiBTY2NtKv/gtr1qxhwIABPH369LMDPB9eZxMSEjAzM1P+X94PIYQQQgghhBDfCwnvCCGEEFmcSqVi9OjRzJo1C2tra8qWLcuaNWvw8fFh4MCBf2uAR8r/CyGyqrT9W69evUhMTCQ8PPxPw43abe7du8fy5cs5d+4cFhYWjBw5EgcHhy9amezGjRsULFiQSpUqMWPGDMqUKaMsi4yMpFevXqhUKmbMmEH37t2BjwM8Q4YMYerUqQBMmzYNb2/vL9I2IUTGSBtS2LFjB6tXr2b58uXkypWLggULsmLFChwdHSWs8Jk+DH14e3tz9uxZFi9eTL58+QA4ffo0Xl5eHDt27KMAz+fsU3yetOdt5cqVDBw4kOfPn7N69Wpat24N/PXfIfJ3ihBCCCGEEEKI752Ed4QQQogsLO1N7PDwcAYOHEhKSgodO3Zk6tSpWFtbZ3ILhRAi86XtK8PCwvD19aVhw4bMmTMHe3v7z9pWO1j+NSuTeXl5MWfOHOrVq0dISIhSgQdg48aN/PTTTyQmJjJ79ux0AZ7U1FQMDQ2ZNWsW06dP58WLF2g0Gu7du4epqakMdgqRSf4obPBnAZAPl125coXs2bNjZmamTOEkwZ2/Z9CgQcTFxXHnzh3atWtHjx49UKvVAOjq6nLmzBn69evHsWPH6N+/PzNmzAAgKSkJIyMjQIIj/9aHv7fTpk1j2rRp3Lt3jw0bNtCsWTNAzrMQQgghhBBCiKxNHgcSQgghsjDtVFcAv//+O8nJyRgYGLBv3z6ePHkCvL8JLoQQ/1Xa6jRaZ8+epUGDBkydOvUvgzuAsq12MN3AwADgqwyez5o1Cx8fH3bt2sWIESM4ceKEsqx58+YsXrwYExMT+vXrx8KFC5V2GRoaAnDy5Elq1arF5s2buXDhAtmyZZNBUCEyiUqlUj5/SUlJPH36lOfPnwP/35986jvah6GeQoUKkTt3bszMzNBoNBLc+ZsePXrE8uXLWb16NWfOnCEpKQl4//5oz3XJkiWZPXs2FStWZObMmQwePBhACe4A0pf+C2q1Wvm9HTt2LA0aNGDWrFnKe+Hp6cnGjRuB9+dZ/nYRQgghhBBCCJFVSXhHCCGEyOK0Aw/VqlUjMDCQIUOGEB8fT8OGDTl16lS6wYYPb4bLzXEhRFan7SNHjRqFj48PW7dupWHDhuTPn/9v7SejBm4nT578hwEeT09PJcDTp08fZs2aBbwfGF27di379+/H3t6eKlWq4ODggEqlypA2CyHSS1tlZObMmfzwww8UKVKEMmXK0Lt3b06fPq2Ee/7OdzEJkPx9uXPnZs+ePbi4uPDy5Ut++eUXAKWCmlbJkiWZM2cOVatWZfr06YwcOTKzmpzlaK/DAQEBjBkzhhw5cjBt2jTmz5+vBKVatGjB+vXrAQnwCCGEEEIIIYTIumTaLCGEECIL+rPpFgAmTZpESEgIOXLkYMOGDZQqVSrdQNLdu3fJmzdvRjVXCCEy1f3798mfPz+mpqZky5aNmTNn4unpqUyB9S3y9fUlNDT0D6fQ6t27N0+ePKF27dqoVCpOnz6NpaUlR44cwcHBIRNbLsR/W9ppf4YMGcL06dMpWLAgFSpU4Pbt28TGxlK0aFG6d+/OTz/99M32Qd877Xdl7fffCxcu0KpVK65evYq3tzfTpk0DPp7O6cSJE4SEhDBt2jTy5cuXWc3Pco4dO0bNmjVp2rQpoaGhODo6KssWLVrE4MGDef36tUyhJYQQQgghhBAiS5PKO0IIIUQWk7bM/61bt4iNjWX//v28evVKeYLY29ubESNG8OLFCzw9PTl58qQyMLF9+3a6dOnCsmXLMu0YhBAiIzk4OHD48GFsbGx48OABS5cuRa1WY2BgoEw9+C1I+9zFpEmT/nQKrcjISFq1akVMTAxnzpyhePHiHDx4UCruCJHJtGGDefPmERYWRu/evYmOjmbx4sVERkbSvXt3jh8/zs6dO6W6yBf0YV+u/a6s/f7r7u5OVFQUbm5uzJgxAz8/P2V52j7Tw8ODqKgo8uXLR2pqaga1Puu7d+8eycnJtGjRAkdHR9RqtXJ+u3Xrxvjx44H3FeY2bNgASAUeIYQQQgghhBBZj1TeEUIIIbKQtBV3QkJCWLp0KTdu3ADA3t4eLy8vWrZsSYECBUhOTmbGjBmEhIRgZWVFaGgoDx48IDw8nGfPnnHixAmpviOEyNK0FRW0feepU6do3bo1N2/eJDg4mKFDh6Kvr/+X1cy+lr/6uWq1mqFDh/5hBZ7ExETi4uJQq9XkyJEDU1PTj6pICCEylkajISUlhR9++IHbt2+zceNG3NzcSE1NZePGjfj4+KCnp0dsbCw2NjaZ1v9kJWn7vW3btnHu3DmuXLlCo0aNlGkEtc6fP0+rVq347bff8PHxYfLkyR/tQ3x5a9eupW3btsybN48ePXoor6f9/W/Tpg1RUVEALF26lE6dOmVKW4UQQgghhBBCiK9FwjtCCCFEFjR06FAmT55M7dq1adWqFYmJiWzevJmYmBhq1arFlClTcHV1JSUlhdmzZzNt2jTu3r2Ljo4O+fPnZ9euXeTLl08GKoQQWcqHg+CfGhQ/efIknp6exMfHM3LkSAYMGJApAZ60/e/Zs2d5+PAh9+7do2LFiuTOnRtra2tlPX9//08GeD5ss0wxIsS34eHDh7i4uNC5c2fCw8NJSkpiw4YNDB06FF1dXY4fP46NjQ0ajYarV69SqFChzG7ydyttPxgQEMDMmTNJTk5GX1+fd+/eUa9ePfr370/jxo2VbdIGePz8/JgwYUJmNf8/49ChQ1SvXp3KlSuzePFiChQooCzTTmEZEBBAZGQkT548wczMjNu3b2NsbCzXNSGEEEIIIYQQWYZ+ZjdACCGEEF9WdHQ0s2bNolu3bvj6+iohHXNzc3755RcePHiAvb09AAYGBnh5eeHh4cGxY8cA6Ny5M7ly5ZLgjhAiS0nbp23cuJHjx49z8uRJypQpQ5kyZWjRogUAZcqUITo6Gk9PT4KCggAyPMCjVquVtgYFBTF//nwePnwIgJmZGbVq1cLHx4eqVauip6fHxIkTAQgNDQVg7NixlClT5qO2ygCnEN8Gc3NzsmXLRlJSEgBbtmxRgjvaijvwvt+qWbMmgwYNUqZxEn+Pth8cMWIEkyZNok2bNvTt25dixYoxZ84cAgMDef36NSkpKTRv3hyAYsWKERUVRfv27Zk0aRJmZmaMGDEiE48ia/iza2jVqlXp2LEjkZGRrF69mp49e2Jra6tMYQlw//596tSpQ4sWLShevDgmJiYZ2XwhhBBCCCGEEOKrk/COEEIIkcUcOnQIIyMjvLy8lOBOdHQ0Y8aMIV++fOzcuRMzMzNSUlLQaDQYGhpSpUoVqlSpolRlkOCOECIr0PZpacMwvr6+zJw5E41GQ2pqKrt27QKgXbt2rFy5EgAPDw82bNiAp6cnY8aMQVdXFy8vL/T1M+bPJ+3gpr+/P1OmTFEq6ty6dYvjx4+zefNmjh8/zuLFi6lXrx66urrpAjxxcXHMnz+fEiVKZEh7hRCf9qnvUykpKaSmpuLg4MDWrVsZOXIkS5YsQU9Pj19//ZWcOXMC7/uvsWPH8u7dOxwdHTOj+VlGVFQUixYtonfv3nh7e+Pq6srr169ZuXIlJiYmHDt2jFGjRqGjo0OzZs2A9wGepUuXMmjQIJme6QtI+1m4fv06Dx8+RFdXF2tra6WyVK9evbhw4QITJkzgzZs3tG/fnmLFigGwYcMGjhw5QufOnWnUqNFH+xRCCCGEEEIIIbICmTZLCCGE+I59eNP67du3VK1aldTUVM6ePatMl+Xn56c8za0dFDp37hyvX7+mcuXKMpWKECJLiY+Px8LC4qPXx44dy6hRo+jRowe9evXC1NSUy5cvM3jwYH7//Xfq1KnDzz//rPSHJ0+epHXr1vz+++/MnTuXXr16Zdgx/Pzzz7Ro0YJ27doxYsQInJ2dUavVpKSkEBwczLhx43B2diYyMpLy5csr2/Xp04d169Zx6dIlpb8XQmSuoKAgHBwc6N69u/La2rVradu2LXp6euTKlYuLFy9iaWkJvK9Qsm7dOoYNG0aBAgVYvXo12bNnz6TWf9/evHnDwIED2bdvH+vWraNUqVK8fv2acuXK8fLlS0JDQ7lz5w7Dhg3Dw8MDf39/pRIbQGpqKvr6+sq/4u9LW3EnKCiIhQsXcv/+feB9FVBfX1/69euHnZ0dW7ZsYfz48fz66684OjrSrFkz7t27x6FDhzA1NeXIkSM4ODhk5uEIIYQQQgghhBBfzdev+S6EEEKIryJtJYmNGzcCYGpqSv78+Xn58iVxcXHs27fvk8EdeD89VkhICElJSRLcEUJkGT///DN9+vTh8uXL6V6/ceMGERERVK9enaFDh1KyZElcXV1p1qwZR44coXLlyuzZs4c+ffoo25QpU4aVK1fi4eFB48aNM/Q4tAHM7t27K8EdHR0djIyMCAkJYfDgwdy6dYvIyEhSUlJQq9UAzJ07l2vXrpEzZ07lNSFExkr72bt37x5BQUGMGjWKZcuWKa/XrVsXHx8f1Go1zs7OnD59mjdv3pCQkMDkyZMZOnQoqampLFiwgOzZs8vn+R/S0dHBwsKCkJAQSpUqRWJiIo0bN+b58+eMHz+eVq1a0aNHD0qWLMnJkycJDQ1l7dq1yvbawI4Ed/65tNXkgoKCKF26NJGRkSxfvpwGDRowYcIEevXqxc2bN2natCnh4eEMHjyYp0+fMnPmTPbt24e7uzsHDx7EwcEBlUqVyUckhBBCCCGEEEJ8HRLeEUIIIb5TaW+Et2jRgunTpwPg5ubG3bt36dy5M71790ZPT49jx46lC+6Ehoby+PFjatasKYMRQogs4+DBgzRs2JAHDx4ofaTW48eP+f333/nhhx9wdnZWBv9UKhW5c+dm9erVODg4sGnTJk6dOgW8H4CvUKECR44cIU+ePBkyYKgdoL969Srw/wPGurq6yhRgAD4+Pri5ubFlyxZev36d7nizZ8+ORqP56BwIIb4+lUqlfPb27t3L/fv3qVGjBs+ePWPkyJEsX74cACsrK7p3746Pjw9Hjx6lVq1alClTBjc3N4KCgrCysuLAgQPkzZs33T7FH/tUH21qasqoUaNo0aIFGo2G+fPnExMTQ58+fWjdujUGBgZYW1vj7u5O/vz5OXbsGHPnzuXdu3eZcARZ1+bNmwkPD6dLly5MnTqVNm3a0KFDB+rWrYtarebixYvY2NgAULx4caZMmcLly5e5ePEi586dY8uWLTg6OspUWUIIIYQQQgghsjS5+yOEEEJ8Z9I+eX306FGWLl1K//79qVevHgABAQEUKVKEbdu2kZiYyN69e8mdO7eyzdq1a5k3bx758uWjS5cucgNcCJElPH78mKFDh1KsWDFGjRqFm5tbuuUvX74E4Nq1a+kG//T09FCpVDg4ONCnTx8eP37MhQsXgP8PSRoYGCjrfmkfzmKs/ZlFixYFIDY2Fvj/QWldXV00Gg12dna4ublx79497ty589F+paKaEBlPo9Eo/YSvry8dOnSgVatWmJubkydPHu7cuYOvr68S4HFzc2PcuHHs3LmT9u3bkzt3bqpWrUpoaCi7du3CyclJwgp/g/Y8jRgxgtDQUOV1c3NzjIyM0NHR4cyZM5iZmeHr64upqamyzuXLl+nQoQNbtmxh6dKlGBsbZ3j7v3dv3rz5w2UxMTGkpqbSvXt3XFxcSE5OZu3atUyZMgUXFxdiYmKwsLAgJSVF2SZPnjwULlyYvHnzki1btnSfLyGEEEIIIYQQIiuS8I4QQgjxndEO7P7+++8cP34cAwMD+vbtS5EiRUhNTSVbtmzMmTOHggUL8vLlS6ZMmcKRI0c4efIkPj4+DBo0iMTERFavXi3TqgghsoyEhASuX79OqVKlqFGjBgB9+vRhy5YtAJQsWRIbGxtiY2M/Crtogy6FCxcG4MWLFxnSZpVKpfzsu3fv8uTJE2VZlSpVyJUrFyEhIVy/fh09PT00Gg0ajUbZJikpCScnJ+zt7TOkvUKIP6f9bE6fPp3Q0FB+/PFHdu/ezaZNm9izZw/Tpk3jyZMn+Pj4KAEefX196tWrx4oVK9i/fz+rV6+mT58+2NjYpJsiVfyxtN9lX7x4wbhx4xg/fjyzZs0C3r8vycnJpKSkcOvWLd6+fcuZM2eUbSIjI3n8+DH58uWjcePGSoUX8fn27t1Lu3btlMp1aanVao4ePUqePHmoWLEiKpWK6OhofH190dHR4ejRo0rVnSNHjijTAX9YbUpCqUIIIYQQQgghsjoJ7wghhBDfoZCQECpUqMD+/fupWrUqbm5uqFQqZXqVypUrs2TJEtzd3Zk7dy41atSgbNmyLFy4kEKFCnHkyBFlYEKmYRBCZAUmJibkyZOHXbt2kZiYyLBhw5g3bx779+/n7du32NjY0L59e06cOMHEiRPTPd2v7QePHz+OiYkJhQoV+urtTVtNIywsjHbt2hEcHMzTp08BKFeuHB06dODRo0fUqVOHc+fOoVarlcHLDRs2EBsbS5kyZTA3N//q7RVC/DWNRsPLly/ZuHEjuXLlonv37koVsHz58uHt7c2qVat49uwZfn5+LFu2TNk2bZ+kDaPId7S/lva77M6dOzl79iz16tUjPj6ecePGMWfOHAAMDQ0xMDCge/fuJCcnM27cOBYsWEBgYCABAQGYmJjQsGFDZb8Smvp7Nm7cyNatWwkJCeHs2bPK62q1Gl1dXezs7Hjy5AnHjx9n69at+Pv7o6urS2xsbLqpfUeMGMHw4cOJj4/PjMMQQgghhBBCCCEylX5mN0AIIYQQf09KSgq5c+fGwMCATZs24erqyosXL8iRI4dSkUFPT4+KFSty/Phx1qxZw4MHD0hJSaFSpUqULl0aS0tLmYZBCJGl2Nra0qVLFwYOHIitrS1v3rxhzJgxdOjQQZka5aeffiImJob58+fz7t07vLy88PDwAGDdunWsXbuWYsWKUb58+a/a1rTVNHx9fZkzZw6urq40atRIqYimq6vL5MmTiYuLY/HixdStW5f//e9/lCtXjjNnzrB582ZMTU2ZPHkyJiYm6SryCCEyh46ODhqNhnv37pE/f34KFiwI/H+AAaBt27Zcv36dkSNHMnr0aAA6d+6MgYGB8jmW0M7nSTuN0pAhQ1i+fDkmJiZUqFCBAgUKcPXqVQICAtDR0aFPnz4A1KpVi2HDhjFlyhR27tyJvr4+7u7ubNiwAVtb23Tvlfh806dPR09Pj7CwMFJTUwkODqZEiRLKuaxevTqrV69m9OjRXLhwAV1dXY4fP65U3AGYOXMm169fp3///mTLli2zDkUIIYQQQgghhMg0OhqNRpPZjRBCCCHE35OQkMCmTZsYN24c169fJzQ0lG7duqUbwP2zcI4MTAghspK0fVqpUqW4ePEixsbG7N69m/Lly5OcnIyBgQE6OjocO3aMgIAADh06RI4cOShSpAgqlYqLFy9ibm7OoUOHcHJyypB+Mjg4mJCQEHr37k2/fv1wdXX9aB2NRsPo0aPZvHmzUs0gW7ZslC5dmmXLluHk5CRhTCG+Ic+ePaNy5co8fvyYAwcOUKJEiXTLNRoNBw4coGHDhiQlJZE3b17CwsJo1qxZJrX4+zdz5ky8vb3x9fWlW7duuLq6cu/ePfbu3at8P544cSJ9+/YF4O3bt5w/f55ff/2VPHnyUL16dWxsbKQv/ZdUKhUDBgxg7ty5NGnShKCgIEqVKgXA8+fP6dKlC1u3bsXc3JwDBw5QsmRJZdvIyEhGjRpF9uzZ2bZtW7pqPEIIIYQQQgghxH+FhHeEEEKIb9ifDR7Hx8ezefNmhg0bhqGhIdOmTaN+/foYGhqmWy9tNQapzCCEyMo2btxIixYtKFOmDCdPnsTBwYFDhw7h7OxMSkoKBgYGAFy6dIldu3axaNEiHj16hJ2dHeXKlSMoKAgHB4cMGcA9fvw4P/zwA+XKlWPq1Km4uLgoy06dOkVqaiqpqalUqlQJgPv373P69Glev36Ns7MzRYsWxcLCQgabhfiGaL9njR07lpEjRxIUFMSIESOU5ampqejr65OamkqNGjXw8PAgLCyM6tWrs3TpUhwdHTOx9d8fjUZDQkICLVu25NSpUxw5cuSjEOTPP/9M48aNMTMzIzg4mP79+39yXxJs/+c+vA7169eP8PBwmjVrxogRIyhdujQAW7ZsYerUqRw9epRu3bpRrVo1ChYsyNKlS4mKisLAwCBDA7RCCCGEEEIIIcS3RqbNEkIIIb5RaW+EX716lcTERBISEvDw8EBfXx8LCwuaNm2Kjo4Ofn5++Pr6otFoaNCgQboAT9qwjgR3hBBZmaurK1u2bKFEiRIsWrSIoKAgqlSpwpEjR3ByclICPEWKFKFIkSL07NmTp0+fYmVlhbGxMYaGhhkWhrl//z6PHz+mQ4cOuLi4kJyczOPHj5k9ezZz5swhJSWF5ORkZs6cSd++fXFwcMDBwSHdPtJOvyWEyDh/FCzQhneqV6+Om5sbI0eOxM7Ojk6dOmFoaKgEd8LDw7lz5w6bN28mZ86cBAYGcvLkSQnv/E3a77X379/HwcFBCe5o3x+NRkP9+vWZOnUqgwYNYuLEiQBKgCft+yhBkX8m7TVz0aJFxMTE8PjxYzQaDTt37kStVjNy5EhKly5N06ZNMTY2Zv78+YSHhxMeHg6AhYUFFSpUYMGCBeTNm1dCqUIIIYQQQggh/rMkvCOEEEJ8g9IOyI4ZM4aIiAgePXpEcnIyFSpUoHHjxvTt2xcrKyuaNGkCgJ+fH35+fujo6HyyAo8QQmR1RYoUwcXFBSMjI0aOHElycjLjx4+ncuXKSoBHW/lCo9FgamqKk5NTun1k1IDhs2fPANi1axfVqlVj5cqVrFy5kkuXLlGzZk0KFizIvHnz8PLyomTJkkoFnrRksFmIjJc2WHDy5Elu3bpFQkICZcuWpUiRIgBUqVKFoUOH4ufnR48ePbh8+TJ169alWrVqLFu2jAULFpAvXz709PQoX748ADt27MDT01MqjvxNGo0GQ0NDLl26RExMDOXLl1fOn46ODhqNhgoVKpAtWzYePnxISEgIOXLkoEOHDnKevwDtZ8HX15fw8HBKly5NixYtsLa25tKlS2zevBm1Ws3o0aMpXbo0devWpVatWvz88888evSIhIQEKlWqhKurq1STE0IIIYQQQgjxnyfhHSGEEOIbpB1MGD58OOPHj8fDw4P27dtz6dIlTp06pTyhPX/+fGxsbGjatCnwPsATEBBAUlISzZo1U6aIEUKI/wojIyNl8G/s2LHo6Ogwbty4dAGeb2FwsGXLlkyfPp2IiAiWL19OSkoKhQoVYufOnbi7u2NjY0O+fPnw8fHh8ePHmdpWIcR7acPVo0aNYvbs2bx48QJ4H2IIDg6mbdu2ODs78+OPP2JgYMDMmTOZOnUqU6dOxcTEhMTERBwdHYmOjsbS0hITExMAZeo8CZT8NW3ASa1WY2FhQZcuXRg4cCDr1q2jXLlySkUebVizXLlylChRgqpVqzJ58mRmzJhBuXLlKFiwYCYfSdYQFRVFaGgoPXr0ICAgAGdnZ1QqFTdu3GDSpElEREQAKAEePT09GjVq9NF+pJqcEEIIIYQQQoj/OgnvCCGEEN8Q7YCyRqPh1q1bREZGMnDgQPr370++fPl48+YNv//+O927d2fjxo0YGRkRHh6OpaUlzZo1Q1dXlx9//JGZM2fSpEkTCe8IIbIc7bQ0f0ZPT0/pT0NCQgCUAM/Ro0dxdHT86gGeP2unSqUie/bsHDhwgODgYHR1dXF2dqZLly6Ym5sr6127do0cOXKQP3/+r9ZOIcTn0Wg0SrBm6NChTJkyhcaNG9O9e3eMjY0JCwtj5MiRPH/+nB49euDq6kr79u0pU6YMJ06cYOPGjZiZmeHk5ESPHj1wcHDg3r17zJo1C319fdzc3DL5CL9dH1YjSk1NxdDQUHmtXLlylCpVitDQUHLnzk2fPn0wNTVVpilbtGgRN2/eJCoqCgcHB/r378+xY8ckvPM3JCQkYGZm9sllp0+fBqBnz55KcEdPTw9XV1dmzJjBu3fvWLVqFXp6egQGBlK6dOlP7keCa0IIIYQQQggh/ut0NBqNJrMbIYQQQoj0Nm3ahLu7O/Xq1SMqKorSpUunGwh+/fo1NWrU4Ny5cyxevJiOHTsC72+s7969m3LlyuHg4JCZhyCEEF9c2gFcbZ/4VyEZbUBnxIgRjBs3DgMDA27evPlV+sgjR46QkJBA/fr107Xxz9qmPaa062o0GqKjoxk6dChubm6sXr06XahHCJF5Fi9ezJAhQ+jQoQNeXl64urqSmJhIyZIluX79Ojo6OvTt25f+/funC4ekpKRgYGCgVIO5ffs2ixcvZuzYsfz4448sXLgwE4/q25W2H1+2bBkHDhzg119/xcPDAw8PD/r37w9AdHQ0gwcP5s6dO/To0YP69etTq1YtVq5cybx58zA3N2fbtm1cuXKFSpUq0a5dO1asWJEulCU+7ejRo7Ru3ZqVK1dSvXp15XXt9atDhw5ERkYSGxuLh4fHR9vHxMTQokUL4uPjadCgAX5+fpQtWzYjD0EIIYQQQgghhPguyB0KIYQQ4huQNku7ePFiPD09qVevHnp6ehQoUCDduiqVCnNzc6ZOnYqenh5bt25VlpmZmeHp6YmDgwMqlSrD2i+EEBlBO8A6YMAA/P39Af60Co+2Ag9ASEgIAwYMwMrK6qu07f79+1SvXp0ffviBXbt2KW37o2cltIPR2vanPY6pU6cydOhQUlNTlUFneeZCiMz3+PFjoqKiKFy4MN27d8fV1ZX4+HhKly5NfHw848aNo0GDBsyZM4d58+Zx7do1ZVtt/6Wrq8uxY8f44YcfCA0NpXXr1kpwR61WZ8pxfas0Go3SV/r4+NCzZ0+2b9+Oubk5O3fuxNvbmzZt2pCQkECLFi2YNWsWtWrVYsGCBbRs2RJbW1v69+/P69evWbZsGdmzZ1f2V7BgQXR0dCS48xliY2N58OABK1euTPc7qj132sBObGwswEd/g5QvX54CBQpgbGzM+vXrmT17NqmpqRnUeiGEEEIIIYQQ4vshlXeEEEKITJb2iWKVSsW9e/fo2bMnR44cAWDDhg3UrVv3oykDXrx4gYeHBzo6Ohw5coRcuXL95VQyQgjxvbtz5w6FCxfGwcGB3bt34+Tk9JfbpO0/X716haWl5VeZNmvKlCkEBgZiZmbG8uXLadCgAfB5U30BHDt2jB49evDo0SNcXFxYu3YtTk5OX32KLyHE53ny5Al169Zl6NChtG/fnrdv31KrVi1+//13pkyZQocOHdi7dy/169dHX1+f/v3707t373QVeBITE1m8eDHR0dFUq1aNkSNHAh9PDSX+35QpU/D396dXr1706dMHd3d3Ll++rHxf7tWrF3PnzgXg9u3bnDt3jujoaExMTMiTJw8//fQT9vb23L9/H39/fyIjI1m9ejUtW7bM5CP7PqhUKnbu3EmVKlWwtLTk7t275M2bV/mdPX78OM2bNyclJYWDBw9SqFAhJXCqvfZVrlwZT09PUlNTadu2Lc7Ozpl4REIIIYQQQgghxLdJwjtCCCFEJko7oBsQEICuri4jR47k3r179OvXj127dtGgQQO2bdumVHBQq9XKIG7x4sUxNTXlwIEDGBkZZeahCCFEhhkzZgxBQUFs3LiRpk2bftag96em3PpS0u47LCyMwYMHY2VlxbJly2jYsOFn/8ybN2/y448/Uq1aNQYOHEjOnDkluCNEJvmjz97jx4+xtbVFo9EwfPhwwsLCGD16NP369cPU1BS1Wk2VKlV4+vQpN27cwN/fn+Dg4HT7evv2LfHx8eTOnRuQ4M6fefr0KbVr18bS0pJFixbh6upKUlISe/fupXv37pibm3P48GFy5syZbjvt+6f999atWyxZsoRx48bRqVMnFi1alElH9H358Hdz9OjRjB07lsOHD1O+fHnl9YCAACZOnIi9vT2bN2+mZMmSynbR0dEMGjSIyZMn07p1awBl+jghhBBCCCGEEEL8P7k7JIQQQmQi7UDutGnTmDhxInFxcbx48QIXFxdmz55N/fr12blzJ23btiUhIQH4/2lgVq9ezaVLlyhUqFBmHoIQQmQY7XMHDRs2JFu2bISEhBAfH/9Zg95p1/nSVcp0dXVJTk4G3k/ptWDBAl69ekWXLl3Yvn278jP/7LkJjUZD/vz52bVrF6NHjyZnzpzpwppCiIyl/ewtXryYgwcPKq9rKx3q6upy5MgRnJ2d6d+/P6ampgAkJSVx69YtPD09GTJkCL179/7oc2xqaqoEdzQazX8+uHPz5k1evHjxyWUPHjzgwoULtG/fHldXV1JSUtiwYQN9+vTByMiII0eOkDNnTlJTU7l8+bKyXdp+/tixYzRu3FiZpkwb3JFpyj6fRqNRpjEzNTWldevWyjRZAOPHj6dnz548ePCAevXq4eXlxdKlSxkyZAg+Pj4YGRlRs2ZNZX0J7gghhBBCCCGEEB/7b98hEkIIITKJSqVK9/87duygefPm+Pj4YGdnh1qtxsXFhVmzZlG/fn2ioqL44YcfCA0N5dSpUwQEBDB+/Hjs7OwICQnByMjoTweFhRDie6MdVNUOGKZVtmxZ6tevz7lz5zh//ny69TODSqXC0NAQgKNHj1KwYEGKFSvG8+fP+emnn9i1axfw5wEe7UCziYkJBgYGAP/5AX0hMtuePXvo1q0b48aN49dff1VeV6lUPH/+nAsXLmBpaZnuc71y5UqMjIxo06YNEydOxNHR8aPvfWn916c8vXDhAgUKFGD06NHExcUpr2vPaVJSEvC+j1er1URHRzN06FB0dXWJjY3FxsYGgOTkZHr27El0dDTw//2nSqXiypUr5M+fH39/f1asWKHsT/rYP5c2WHbz5k10dHQICAhgzJgxxMfH4+npmS7AEx4eTnBwME5OToSHh9OlSxemT5+OpaUlu3btUkKpQgghhBBCCCGE+DSZNksIIYTIRKNGjSJHjhwsW7aMUaNG8cMPPwD/P2Cho6PDjRs38PLy4ueffwagZMmSmJmZYWtry9SpU5VBIanOIITIKtJOMfX06dN006EkJydjaGjI0aNHqVmzJu3bt2fx4sWZ1dR0bfXz82PJkiVYWFiQK1cunj59yvXr17GysmLVqlXUr1//o22EEN+uly9fMn36dMaNG0edOnUIDAykYsWKyvJevXqxYMECxo8fT926dTly5AizZs3C1NSUffv2YWVllYmt/z6cPHmSYcOG8csvvzBo0CCGDh1Kjhw5lOV3797F1dWVZs2a0aRJE4YPH64Ed9JeGwYOHMiyZcvYvHkzVapUSfczkpKSiI+PV9aX4M7fM2DAAPbu3cuuXbtwcHBApVIxc+ZMgoKCMDU1ZcOGDZQrV05Z/8GDB5w4cYJnz56RK1cuKlasSI4cOeTvFSGEEEIIIYQQ4i9IeEcIIYTIJMePH6d8+fJYWVmhVqtZunQpP/zwQ7ob29oB3uvXr+Pl5cWePXuoUqUK+/fvV/YjN8KFEFlVv379CA8PJyAggOrVq1O3bl1l2YMHD2jZsiXnzp1j9+7d6QbUM0NoaCi+vr4MGTKEHj16ULBgQR4+fMjs2bMZN24c1tbWrFy5knr16gES4BHiexEfH8+0adMICgqiQYMG6QI8e/fuZdSoURw9elRZ39XVlZ9//hknJycJiXymU6dOERISwsaNG/H19U0X4NFoNPTq1YuFCxdiYWGBlZXVR8Gd5cuXM2rUKEqXLs2SJUswMzP7w58lfe/f1759ezZt2sThw4cpVaoUwF8GeD4knwUhhBBCCCGEEOKvyV/OQgghRCYpW7Ysc+bMQaVS8erVK2XqFz09vXSVdzQaDQUKFGDmzJnUrVuXgwcP0qlTJ2U/ksMVQmQVH06nYWBgQMWKFRk3bhwNGjSgffv2bNiwgYSEBOzt7Rk6dChv377l0KFDQOb1h/Hx8WzcuJG8efPSvXt3ChYsCKBMbTh58mSeP39O+/btlSpqfzaFlhAiY31qWittf2RhYcHAgQMZNWoUO3fuJDg4mCNHjgBQu3ZtwsLCmDdvHt27d2fKlCns378fJycnVCqVhBX+grYPLF26NMOHD6d58+ZMnjyZiRMn8uLFC+B9X9muXTuKFy/O69ev+d///qdMlQUwf/58goKC0NfXZ8aMGZiZmf1p3yrBnb+vc+fOJCUlMXz4cFJSUoD3f6/079+fUaNG8fbt23RTaH3q/MtnQQghhBBCCCGE+GtSeUcIIYTIBKmpqejr6wOwaNEievbsiUajYfXq1bRu3RpI/2Sw9r/TTqHVvn17VqxYAcjTrEKIrOXGjRu4uLgA8PbtW/bt28eiRYs4cOAAL1++pFixYvj4+CjhmCtXrnD48GElNJPRnj59SokSJShevDg7d+4E3vfbarVaqYw2ePBgpk+fTs6cOVmyZAkNGzbMlLYKIf5YZGQkRYoUoUSJEkD671fx8fFMnTqVMWPG0KhRI/z8/KhWrdon9yNVET9f2u+7J0+eZOzYsUoFHl9fXyWos379esaMGcP58+dxc3OjYMGC3Lt3j2vXruHg4MDOnTtxdnaWc/8VJCUl0ahRI44dO8bWrVupVasWKSkpGBgYpKvAY2lpycqVK6lcuXJmN1kIIYQQQgghhPguySifEEII8ZV9WEkiJSUl3aBCt27dmD9/Pnp6evTq1YsNGzYA6asyaP/bxcWFWbNm0bBhQ1atWqUM/kpwRwjxPUv7PIGPjw9NmzZl9+7dAJiamtKkSROWLl3K8ePH6d69O0lJSfz000/07duXc+fOkZqaqjzx/6kKGhnB0NCQkydPKlXUdHR00NPTU64BTZo0wcLCgpSUFBo3bpxu+kMhRObbtm0bHTp0YOzYsVy6dAl4//0qbQWe3r17061bN3bs2MGsWbM4fPjwJ/cl4ZHPp6Ojo/TbZcqUSVeBZ/LkyTx9+hSA//3vf8ybN4/g4GCSk5M5deoUxsbGDBw4kP3790tw51/68Nqp/b1XqVQYGRkRGBiIRqNR/k7RBne0FXiCgoK4c+cO3t7eSnUeIYQQQgghhBBC/D0y0ieEEEJ8RWmnTFi+fDl9+/alRo0atG3blrVr15KYmAi8D/DMnj2bt2/f0qVLl78M8MycORMPDw+MjIwy58CEEOILUalU6aYx0dPT48qVK4SGhrJv3z7l9WzZsuHi4sK8efM4fPgw06ZNw8HBgeTkZOLi4liwYAEajSZTBm5z5sxJmzZtePHiBdu2bePt27fKMu0AaMmSJbG1taV27drY29tToECBDG+nEOL/fViEuHDhwnh7e7Np0ybGjBnDxYsXgfQBnty5c1OnTh00Gg2bN29m6NChnDx5MsPb/r37MNiett8uU6YMAQEBSoBnypQpSoCnQoUKDB8+nLNnz3L+/HmOHj3KmDFjyJ07twR3/iXtuTt8+DDx8fHK3y/a6XxdXV0pWbIks2fPVsKn2oCqnp4e/fr1Y/78+WzcuBEDA4PMOgwhhBBCCCGEEOK7pp/ZDRBCCCGyqrSDyEOGDGHWrFlky5aNPHnysHv3bqKioujfvz8//vgjpUuXpmfPnujq6tKvXz+6dOmCrq4uzZo1SzeorQ3w5M+fn61bt2Jra6v8rLTrCSHE9yDttFJz587l4MGDvHr1CoB9+/bx7t079PT0qF69Onp6esqUgzY2Nnh7e9O6dWtu3bqFj48PBw8eZN26dbRq1eqr9Il/NTDcrFkztm/fzsSJE3F0dKRRo0Zkz54dfX19VCoVS5cuRU9PjzVr1pCUlISJiYkMNguRSdJOh/Xu3TuMjY3Jnz8/AwcORFdXl+nTpwMQGBhI0aJF0dXVJTk5GUNDQ6pXr07p0qUpUqQIv/zyC46Ojpl4JN+ftP3eyZMn+f3334mPj6dQoUKULl0aY2NjypYti7+/PwCTJ08GwM/PD2trawDMzMyU/Wn7eulL/73x48czfPhwSpQowejRoylWrBj58+dHR0cHe3t7+vXrR0xMDHv37qVGjRrK50h7Le/evTsg08YJIYQQQgghhBD/lFTeEUIIIb4S7WDClClTmDZtGt27d2ffvn2cPXuWbdu2UbNmTWbOnEl0dDTJyckAdO/endmzZ5Oamoqnpyc7d+78w/1KcEcI8b3TDp77+fkxZMgQ4uPjadWqFcOGDaNOnTocPHiQ4cOHK0/56+vro9FolIoZdnZ2VKxYkfnz52NsbKys9zWDOxEREQwaNIiWLVuyY8cO7t+/D0DFihXx8vLC1NSUPn36EBISwqFDh0hMTCQiIoLF/8fefcfXeP//H39k70SIESKJPUptRa3apbbapEaQSMwMBFlCg4QkkiASe5fYo7bYs7VbW+0VI8g44/eH37m+J2irLYLP63679VY518j7uk7O+1znvJ/X6z17Nvny5eP58+dYWFjkWJUgIcT/9T0jRowgICCAhw8fAuDi4sKgQYMYMmQIP/30E6GhoZw4cQJ4OTWeWq0mLi6OZ8+eER4eztmzZ8mbN+9rlWTEm+kHNkePHk3Tpk3p2LEjffv2pXbt2ri5ubF582YAqlevnm0KrYkTJ5KampqTzf+saTQa6tatS/v27fnjjz9o164dbdu2Zfr06dy9exeALl26UKtWLaZPn86VK1eU19Gr0/fKe5sQQgghhBBCCPHvGGhfrRUthBBCiHfm1q1bNG3aFHt7exITEylevDharZbk5GS8vb0xNjbm6NGjODg4ZLsLPCYmhsjISFJSUnBycsrhoxBCiPdn1apVtGvXDjc3N4KCgnBxcQHgxo0bTJ8+nbCwML7++mtCQ0OpX78+kD20qFKpUKvV1KtXj4sXL3LmzBkcHBzeS6jR19eXiIgIzMzMyMjIwNLSkg4dOjB06FAqVKiAVqtlzpw5JCQkcODAAQAsLCx48eIFzs7O7Nq1CxcXFwldCpFD9IN4t2/fpkOHDuzbt4/x48fj7u6uVHa5evUq0dHRREVF0aJFC3r16kWrVq2YO3cukydP5ssvv2TevHmYmJjI6/lfGD16NOPHj6dDhw707dsXc3NzNm3aRGRkJM7OzkydOpXmzZsDL6vzhIWFsW7dOvr27cv48ePJlStXzh7AZ0D3d/umv9/Dhw+zatUqpk2bxtOnT6lYsSLNmzfH19eX2NhYRo8ezdChQ5kwYQKmpqY5dARCCCGEEEIIIcTnR8I7QgghxHt0/PhxqlSpQlRUFN7e3mRmZrJy5UpGjBiBgYEBhw8fxsHBgczMTB48eICjo6Oy7bNnz7CyspLS80KIz9r48eMZPXo0e/bsoVatWtn6PLVajb+/P5GRkdSvX5+AgAAaNmwIZA/wPHv2jJYtW3Ljxg0OHjz4zgZ29X/HggULGDBgAB07dqR37948fPiQRYsWsWzZMlq1akVgYCCVKlUC4PLly2zYsIF9+/ah1WopXbo07u7uODo6Sp8uRA7Rf+0tX76cixcvsnr1ag4ePAjAuHHj8PDwwN7eHoBr164xY8YMwsPD0Wg0FChQgNu3b+Pk5ERKSooSNBT/zPbt2+nYsSNNmjQhODiYEiVKoNVqWbZsGT179qRo0aIcOHAAOzs7ZZtjx47h6+vLb7/9xqlTpyS88x+9+j6bnp6OqakpJiYm2dY7fPgwu3btYurUqdy8eZOyZcvSokUL4uPjcXV1JSUlJdvzJIQQQgghhBBCiP/GOKcbIIQQQnyqXr1TVaVSYWyc/a1VpVIBYG1tDUBycjL+/v4YGhpy6NAhHBwcAHj69ClfffUVCxYsoG7dugBYWVkBUnpeCPF50lUbu3z5MgBpaWlA9j7PyMiIPn36sH79evbu3cv48eMxMTGhbt26Sv+bkZHBjBkz2LlzJ15eXu8luPP06VOuXbtGtWrVGD16NEWLFgWgcePG5M6dm+nTp6PVagkODqZixYoUKVKEgQMH0r9/f2WqLwMDAwnuCJGDdK89X19fZsyYQcWKFfnyyy9xdXVl2bJljB49mqysLAYNGoS9vT3Ozs6MGjWKunXrMnHiRMzNzcmXLx/jxo2jUKFC8nr+l06cOEFaWhoDBgygRIkSZGVlsXLlSvz8/JRglJ2dHVlZWWRmZmJlZUXlypWZOnUqjo6O5MqVS6od/Qf6f7dz585l69atnD59GhsbG9q2bUv9+vWpWLEiANWqVaNatWr06NGDpKQkkpOTmTRpEgBnz55VPucIIYQQQgghhBDi3ZDwjhBCCPEv6E9xlZmZiampqRLcWbZsGR07dgTAzMwMQ0NDVq9eTUZGBhMmTMDQ0JCDBw+SN29e4OUA8fjx43n06FGOHIsQQuQEXR9ap04dEhMTOXDgAE2aNMnWvwKUKVOGChUq8PjxY3bs2IGlpSUFChSgZMmSwMt+1tbWlu+++47o6Gjg9XDlv6HbfsSIEZw/f57Lly/TokULihYtqoQ1LSwsiImJwcDAgPj4eABCQ0P58ssvsx2jbl8y0C9EzkpISCAiIgJPT0/8/PxwdnYGoEOHDsTExBAcHIyBgQFeXl7kzp0bKysrmjZtSt26dbGwsCArKwsTExMJ7vwLur79wIEDWFpaUqVKFbKysvjpp58YMWKEskwXbD937hx79uyhd+/emJmZUb58+Wz7Ef+cVqtV/m6HDx9OdHQ01tbW5M6dmzNnzpCSkkLp0qWZOnUqTZo0ASArK4v8+fPj7++Pn58fU6dO5dixY/z444/kyZNHng8hhBBCCCGEEOIdkk/YQgghxL+g+5K6RYsWREVFoVarARg8eDCdO3cmOTkZgC+//JJevXqxZs0aRo4cCcCvv/5Kvnz5gJdfoi9atIiVK1fSrFkzKleunANHI4QQ75dupl79GXs1Gg0AlSpVomTJkowbN46dO3diaGiIVqvNtu69e/dwd3cnKCiIjRs3smvXrmz769u3L2vWrFH2+64qMqSlpXHhwgWSk5M5ffq0sl9dNR14GciJjo7Gw8ODtWvXEhwczLFjxwBkQFOIj8y+ffuws7PD3d0dZ2dnpXJI+/btCQkJoUqVKgQFBTFz5kwePnyobGdmZgagBLUluPPP6frDihUr8uTJE/bv38/27duV4M6hQ4eUYDu8rJA0efJknjx58sb9iH9O9x42depUoqKiGDRoEPv37+fixYvs27cPLy8vzp07R7du3di2bRsAJiYmSiDWyMiI4cOHk5SUROHChVGr1fJ8CCGEEEIIIYQQ75B8yhZCCCH+pSNHjnD69GlCQkJYunQpnp6exMTE4OvrS7Vq1ZT1+vTpQ926dXn8+DHff/+98rhWq2X69OkEBgZibGzMlClTsLa2zjZgLYQQnzq1Wq0MGL548YLU1FRSU1OVAb/y5cvTt29fVCoVzZo1Y8OGDdm2Wb16NVevXqVw4cJ07twZOzs7oqKiePz48WshHa1W+04HEq2trYmOjsbb25usrCyWL1+uBHMMDAxeC/B4eXmRnJzMtGnTZDoRIT4ymZmZ/PLLL5ibm1OoUCGlv9C9juvWrcvAgQMBGDVqFHFxccp0fq9W0RL/XpkyZdBoNHh7e9OnTx+MjIxeC+7ExcVx8uRJ2rdv/86mQhQv3blzh6VLl1KyZEk8PT0pXbo0AJUrVyY6OpqQkBAePHjAmDFjlGktDQwMsr3n6cJsEmITQgghhBBCCCHeLQOtjBAKIYQQ/9q2bdsICgri4MGDqFQqhgwZgq+vL46OjkoZea1WS3JyMhEREezfv5/ixYtTtWpVLly4wLlz5yhYsCCbNm3C1dVVpmEQQnxW9Pu0mTNnsn79ek6fPo2xsTGtW7fmm2++oVmzZgCMGTOGsLAwTExMaN++PVWqVOHKlSusWrUKY2NjDh48SL58+ahRowZ3797NNv3g+6A/9daNGzcICwtj+vTpdO/enVGjRikDnvrrqVQqAgMD6devHy4uLu+tbUKIf2fgwIHEx8ezevVqWrZsqTyu/zquV68e165d49q1a0yZMoVBgwa9k6n4/he87RRKHh4ezJgxAzMzM9avX0+DBg2UZYsWLSI4OBgrKys2btxI/vz55fz/B68+J5cuXaJKlSq0bt2aOXPmACjV7nTr9e7dm3nz5rF69WpatGgh518IIYQQQgghhPhApPKOEEII8S/osq8NGzYkX758qFQqjI2NyZs3L46OjsDLu7R107e0a9eOxMRERo4cSVZWFhs3bsTIyAhvb2927twpwR0hxGdHq9Uqfdrw4cPx9PTk8OHDODg4kJqayqRJk+jZsyfTp08HIDQ0lNjYWL7++muWLFmCr68vCQkJ5M+fn+3bt5MvXz4uXrzIpUuXKFasGJaWlu+srbopvPTbrv/vQoUKMXr0aHr37s2CBQsIDw/n7NmzQPYKPMbGxoSFheHi4iKVd4TIIX91f9JXX30FQGBgIL/88ovyuG7604yMDK5evUqTJk2oVq0aQ4YMYc+ePRJc+Av79u1j9uzZwP9d+/4Z3bLRo0fTtWtXMjIy8PHxYerUqSxevBg3NzcGDx7MixcvSE5OJn/+/O90KsT/NfrBncuXL6PRaMjMzCQ9PZ3z58/z6NEjJZijP2Xlt99+i0ajYePGjTl8BEIIIYQQQgghxP8W45xugBBCCPEpMjAwQK1W8+jRIx4/fkzHjh357bffCAkJIVeuXLi5uWFpaakMYhgaGlK6dGnCwsLw9/dHq9ViZ2enLJPgjhDic6MbbI2LiyMqKoqhQ4fSr18/SpQowYULF1i7dq0S6jE2NqZv3754eHjw/fffc/bsWa5du0aBAgWoUKECDg4O3Lp1i5kzZ3L//n2aNGmClZXVO2mnfv+7YsUKDhw4wLVr13BxcWHQoEE4OTkBULBgQUJDQwFISkoCwM/PjzJlyigBHv0BZmNj+aglxIem/3pOS0vD0NAQExMTTExMAOjZsyd79+4lISGBkJAQRo0aRdWqVZXXa3JyMpaWlnh5eXHixAl69OjBokWLqF27do4d08fs4cOHfPvtt0rVFjc3t2zXvq/SPVaoUCGmTJlCvnz5mDZtGj4+Pmg0GgoUKEDdunWJiorCyclJro//I9359vDwYPv27SxevJhy5crRqFEjdu/ezcmTJ6lTp45ynjUaDUZGRtSqVQtAeZ+V8JQQQgghhBBCCPFhyLRZQgghxD/wprLx9+/fx9DQkBMnTuDv78+JEyeYMmUKbm5uWFhYKNvoD2RkZWVhYmIiZeiFEJ8trVbLkydP+P777/n999/ZunUrxYsXz7bOkiVL6Nq1K8WKFWPx4sVUrVr1jfs6c+YMs2bNIi4ujnbt2rFo0SLld/yXPlS/X/bz8yMqKgqNRkOuXLl48OABjo6OJCYmUr9+fczNzQG4desWY8aMISkpiT59+jB48GDKlSv3r9sghHg39F/PU6ZMYePGjdy/f5/q1avzww8/UKNGDQCuXbuGr68vy5cvx8nJiVGjRlG8eHEOHTrE7NmzMTY25sCBA5iamlKkSBGKFSvGrl27JJD3J9auXcsPP/yAhYUFoaGh9OrVC3j7KbQOHz7MkydPuH37NtWqVaNQoUJYWVlJcOc/0D/3CxcuZNCgQXz77beMGzcOV1dXYmNj8fb2pmDBgqSkpFCkSBHl/VSlUhEdHY2Pjw+zZs2id+/e8nlFCCGEEEIIIYT4QGTaLCGEEOItqdVq5YvrtLQ0MjMzyczMxMHBgdy5c1O/fn2CgoKoUKECQ4cOZc6cOTx79kzZZuPGjYSEhAAod4DLF+FCiE/drl27WLly5WuPGxgYkJ6ezpkzZyhTpowS3NFNywHQuXNnxo4dy8WLF5UpbF6dsurAgQN06dKFGTNm0L17dyW48y6mUtENbgYFBREREcEPP/zA3r17uXfvHtOmTePWrVv07duXdevWkZGRAYCjoyOhoaG4u7uTmJhIUlKSTJElxEdA93r29fVl+PDhHDlyhAcPHjBz5kwaN27Mhg0bAChcuDDR0dEMHTqU69ev4+npSZMmTRg9ejSGhoasX78eOzs7zp07R1paGqVLl5bgzl9o2bIlCxYs4MmTJwQEBCiVyd52Cq1q1arRsGFDunXrRsmSJbGysso27aL4Z3RVkODlZ5d79+5RrFgxgoODcXV1BWDgwIH079+fmzdvUqtWLZKTk7l69SoAixcvJjExkS+++IJWrVoB8nlFCCGEEEIIIYT4UKTyjhBCCPEW9O9gjYmJYePGjaSnp1OiRAlGjx5N4cKFlXU3bdpEUFAQv/76K5MnT6ZVq1b88ssvjBw5klu3bvH777+TJ0+enDoUIYR4Z+7evUvNmjW5fPkyK1eupE2bNtmW37hxg2rVqmFtbc3OnTtxdHTMNgio1WrZtWsXDRo0oF27dixduhQDA4Ns1RouXbrE2rVryZs3L127dgXevqLD21i7di3e3t40bNiQkSNHUrx4cVQqFeXLl+f+/fvKNCLx8fG0aNFCqcBz/fp1oqKi8Pb2xtnZ+Z20RQjxz+lXaNm2bRtdunShS5cu9O3bl7JlyzJlyhSCgoJ4/vw5q1evpmXLlsq2W7du5erVq1y4cIGyZcvSuHFjChQowI0bNxg3bhwzZ84kPj6efv365dThfdT0++J9+/bRrFkzChQogI+Pj3LO3mV/Ld6el5cXR48eJS0tjUaNGjFlyhQAVCqVEkYbPHgwCQkJpKenY2dnh62tLbdu3cLJyYnt27fj6uoqz58QQgghhBBCCPEBSXhHCCGE+Ad8fX2JiIjAysoKc3NzHjx4QIECBVi5cqUyHQPA5s2bCQ0N5eDBgxQqVIgnT55gaWn5Wml6IYT41M2ZM4cJEyZw7do1Fi5cSLt27bIt79+/P4mJicydO5du3bopA4G6Affnz5/j4OBA9+7dmTlz5ht/h/5g47scSMzIyGDAgAHs2LGD5ORkKlWqRFpaGtWrVyc1NZUff/yRtLQ0hgwZgqOjI1OnTqV58+ZKgOfVYxFC5JwbN25w4cIF3N3dWbNmDaVLl1aWzZkzh2HDhvHo0SPWrFnDd99996f7uXr1KnFxcURGRtKlSxfmzZv3IZr/ydHv97Zv386DBw+YN28e69ev54svvmDIkCH06dMHkABPTmjYsCE7duwgd+7cuLm5ERERobyX6j93q1atYufOnezevZtChQrx5Zdf4uXlhaOjo7y3CSGEEEIIIYQQH5iEd4QQQoi/oP+l9ebNm+nRowddunTB3d2dkiVLMm7cOKKjozEzM+Onn36iTp06yrYHDx5k0aJF7N69my+++IIJEyZQuHBh+SJcCPFZ0B+MXbRoEaNHj+bWrVuvBXh++uknevbsSWZmJlu2bOGbb75RAowqlYqEhAQGDhzIlClTGDx48AcPN86ePZv79+/j6+tLeno63333Hb/88gvh4eHKwHOjRo3Yvn07BQsWZMKECXTq1AlTU9MP1kYhxF8bOXIk4eHh1KpVi4IFC7Js2TK0Wi1qtVoJ/s2dO5ehQ4dmC/Dopt/T9Tnr16/H29ubhw8f0qZNG+bMmQNI+ORV+v20j48PixcvRqPRULt2bVJSUkhNTcXR0ZGxY8fSu3dvQM7hh6L/3HTp0oWlS5fi4ODA3r17KVGihPI8vPp8PH78GDs7O+VzinxeEUIIIYQQQgghPjz55kQIIYR4hS7XqtVqlS+tX7x4gZGREebm5gwYMIBy5cphampKSEgIYWFhaDQa2rdvT0pKirKfr776iqioKHbs2EFiYqIEd4QQnxXd4B9A165dGTduHI6OjnTr1o2VK1cq63Xo0IExY8ag0Who1KgRMTExHD9+HI1Gw5w5c4iNjaVkyZJ06dIF4INXJevVqxe+vr4ALF++nAMHDtC3b1969OihrFOzZk0qVqyIWq0mPDwctVr9QdsohPhr+fPnx9zcnOPHj/Pw4UPlcSMjI6WfcnNzY+rUqdjb29O6dWt++uknDA0Ns/U5RYsWpXz58gQFBUlw5y/ozll0dDSRkZF069aNHTt2sHz5cnbs2MHkyZN58OABgYGBJCUlAdnfM8S7o38/ni64k5mZCcDixYvp0qUL9+/fZ8CAAVy9evWNwR0AGxsbAOVx+bwihBBCCCGEEEJ8eFJ5RwghhPj/rly5Qq5cuciVK1e2x/39/Zk0aRJt2rQhb968zJgxA8g+jUtcXByBgYEYGBiwYsUK6tSp81r1CJkqSwjxOXrbCjyTJ08mIiKCO3fuAJArVy4eP35MsWLF+Pnnn3F1dc3xQfJBgwYRFxfHiRMnKFu2rPJ4kyZNsLKywt3dnUqVKuHo6JhjbRRCvJmuipdKpWLhwoVKIFCr1aLVapW+ZcGCBfTs2ZP8+fNz+fJlzMzMMDAwUALW6enpr02NJ7LTarWkpaXRpk0bTp8+zd69eylWrFi2dZKTk+nRowf58uVj1KhR9O3bF5Bz+i7p3xTw4sULnjx5Qv78+cnIyMDMzExZ7/vvv2fFihW0aNGCadOm4eLiIs+DEEIIIYQQQgjxEZJP6kIIIQRw/PhxSpQoga+vL8+fP8+2zNzcHCsrK9atW8fvv/9Oeno6Go0GY2Nj5Q5iT09PgoOD0Wq1dOrUiW3btr0W1JHgjhDic2RoaKhUovmrCjw+Pj4sW7aM8PBwGjVqRPPmzQkLCyMlJQVXV1fUanWODyRaWloCcO/ePeWxxYsXc+7cOZo0aULz5s1xdHSUyjtC5BD96og6uioj7u7uTJ8+HQMDA/r27cuqVasAlGmxdNds3bt3Z9myZRw+fBhzc3Pl+kwXgtAFd/QDPyI7Xdjpjz/+oFChQkpwR7+yTvPmzfHz8+Pq1atMmzaNxMREADmn74hGo1H+ZiMiImjUqBHFihWjUqVKuLm5cf78eWXd5cuX065dO9avX4+Xl1e2CjxCCCGEEEIIIYT4eEjlHSGEEAK4du0a1apVo169esybN08ZuNH58ccfmTRpEunp6axfv5769esrd7vq37k6ffp0PD09KV++PIcPH8bU1DQnDkcIId6bt7lbf+HChYwZM+aNFXgApSqAriLZxzKl4IoVK3Bzc8PS0pLRo0fz+++/s379eoyNjdm9e7dU3BEiB+n3E+np6aSnp2Nra/tafzRr1iwGDhyIpaUls2fPpk2bNsDrFXhe3af4Z168eEHt2rW5cuUK+/bto1SpUq+9P+zcuZMGDRpgYmKCiYkJiYmJdOrUKQdb/fnx9fUlIiKCYsWK4eLiwq1btzh79iz29vYkJCTw3XffKZ9HOnTowMqVK2ndujUREREULVo0h1svhBBCCCGEEEIIfRLeEUIIIf6/+/fvY2Zmho2NDZs2beKrr77C3t5eWR4eHs6YMWMwNzcnJSWFChUqvDHAM3fuXBo2bIiTk1NOHYoQQrwX+gPdmzdv5ty5c5w/fx5XV1dat25NiRIllHX/KsCj6zM/xukEJ06cyMyZM7l06RIGBgZUqlSJFStW4OLiIgP9QuQQ/dfezJkzWb16NWfOnKFAgQIMGDDgtesu3RRaVlZWrwV4PrY+52Onf850/9ZNHTthwgQCAgIYOXIkYWFhyjpqtRpjY2OysrKoUaMGzZo1Izk5mS1btlCoUKGcPJxPnv5njoMHD9K2bVu6dOnCoEGDcHFxISsri8DAQKZPn45Go2HRokU0b95c2b5z584sW7aMnj17kpiYKO9pQgghhBBCCCHER0TCO0IIIcQrYmJiGDx4ML6+vowaNQo7Oztl2cSJExk1ahSWlpbs3r2bihUrvjHAA3I3txDi86Lfx40cOZL4+HiePHmiLM+VKxcTJkygZcuWFCxYEMge4Fm8eLEygP4h2/qqPxu819/m8uXL/Pbbb9jb21OqVCly5colfboQOUT/tenj40NkZCSFChWiTJky3Llzh9OnT+Pm5sbw4cMpW7assl1CQgJeXl7Y2dkRExMjFV/+hVf7vVf7zwMHDtC9e3cuXbpEREQEQ4cOzVZRbebMmYwbN46DBw9SoEABjI2NpS99R3777Te2bt3KhAkT2L59OyVLllRCVQDTpk3Dx8eHXLlycfTo0Wyhqf79+zNy5EhcXV1zqPVCCCGEEEIIIYR4EwnvCCGEEK+4dOkSHTp04MyZMwwdOhR/f39y5cqlLJ80aRIjR47E0tLytQo8QgjxuQsKCiIkJAQ3Nze+//57HB0dWbBgAYsWLeLx48eMGDECDw8P8ubNC8CiRYsIDg7m/PnzrF+/nm+//fa9tk+/P/799995/Pgxjx8/pkKFCuTOnRsjI6M/7bP/LPTzNlOFCSHer/HjxxMSEkLfvn0ZOHAgZcqU4fLly5QrVw5DQ0OaN29OcHAwpUuXVrZJTEzE3d2d0qVLc/ToUczNzaXyzlvS7/cSExM5cOAAqampfPfdd3Tq1AkLCwsAkpOT+eGHH3j69CkDBw6kefPm1KhRg8WLFxMfH4+9vT3r16/HxsYmJw/nsxIeHk5oaCjNmjVDo9GwcuVK1Gq1UtFO97wNHDiQ+Ph4QkJCGD16tDJlpY5+2EcIIYQQQgghhBA5T8I7QgghxBtcu3aN9u3bc/z4cXx9ff80wGNnZ8fmzZupWrVqzjVWCCHegzcFVn755RdatGhB9erViYqKwtnZWVmWnJzMjz/+yKlTp5gzZw7ff/+9siwpKYlZs2axdOlSChcu/EHaPG7cOObPn8/ly5dRqVSUKlWKdu3aERAQgKWlpQRyhPgIPX/+HEtLy9ce37dvH25ubnz99dcEBARQokQJnj59SvXq1Xn8+DGurq4cOHCA77//nsDAwGwVeBYtWkSdOnXea9/zOfP19SUiIgITExOysrIA6Nq1Kz4+PlSsWBGAtWvXEhQUxPHjxwEwNzcnPT0dV1dXtm/fjqurq0xZ9o6o1WqWL19OYGAg58+fJ0+ePBw5cgQXFxdlHd3728WLF6latSqNGjVi+fLlOdhqIYQQQgghhBBCvA35tloIIYR4A2dnZ1asWEGlSpWYNGkS4eHhPHr0SFnu6+tLeHg4qampdO/enaysLCQPK4T4HJw6dYobN25gaGiIRqPJtuz69evcunWLZs2a4ezsjFarRaVSAdC2bVsGDx5MZmYmPj4+3LlzR9mud+/ebN26lcKFC6NWq99b23VhHH9/f8aOHYuLiwvh4eHMmDEDAwMDJkyYQLNmzXj+/LkEd4T4yGzbto0ePXpw4sSJbI9rtVrOnDnD06dP6dWrFyVKlCAtLY2aNWuSmprKpEmTWLRoEU2aNGH16tWEhoZy5swZZfuuXbu+977nc6J/Pbty5UpmzpyJu7s7hw8fZvv27fTq1Ytly5YxduxYjhw5AkDLli1ZsGABS5cupUuXLnTr1o3g4GD27duHq6srarVagjvviJGREW3atCE8PJyqVavy4MEDZs+enW0aSx0LCwsMDQ2V0JUQQgghhBBCCCE+blIfVwghhPgTugBPu3btmDRpEkC2CjzDhw/H0tKSFi1aYGJikoMtFUKId+PkyZNUqFCBJk2akJSURMGCBbNVqHn69CkApqamwMu7+42NjZWKCl27dmX9+vUsXryYS5cukT9/fmV7XTWN9z3FYHJyMjExMfTt2xc/Pz+KFy8OvKwE4ebmxs2bN2UQX4iP0NKlS0lOTsbY2JixY8fyxRdfAGBgYECZMmWIiYmhXr16ZGRk0LNnT27evEl4eDidOnXC2NiYGjVq8PPPP7NhwwZSU1OJjY2lWLFiyv5letO/p18d58WLFxw/fpyiRYvi6+ur9KXFihWjYMGChIeHAxAYGEiVKlUoU6YMZcqUUaqu6fYlU8u+e+bm5jRr1gytVktAQAAzZsygSJEitGrVCnt7e+U9e/369aSmpiqVqKT6kRBCCCGEEEII8XGT8I4QQoj/Of/ki2tnZ2dWrlyZLcAzYsQI7OzsAPDw8ACQgQkhxGehfPny1KtXj59//hlvb2+io6MpVKiQ0sflyZMHgBkzZtCsWTMcHR2Bl4PrGRkZmJmZUa9ePRYvXsyNGzcAPniFm3379mFkZMTAgQMpXrw4arWaJUuWEBQUhKurKwcPHsTGxob09HTMzMxkIFOIj8S0adMwMjJixowZqFQqQkJClADP119/rVQP2bdvH7t27aJNmzb06NEDY+OXX2t88803rFu3DktLSw4fPqxcq4m3p+sP/f39OXHiBFZWVrRr147ixYujUqkwNjamcOHCeHp6YmBgwI8//ghAUFAQlStXBl6/Jpbr4/fD3Nyc5s2bA+Dn58fw4cM5dOgQ7u7uFCxYkOTkZKKjo3FxccHb2xtA3u+EEEIIIYQQQoiPnIR3hBBC/E95U8jm78I8ugBP+/btmTp1Kk+fPmX8+PHY2toq68jAhBDiU6cbmN2xYwctW7YkOTkZrVZLdHQ0Tk5OADRp0oSWLVuydu1aZs2ahaenJ3ny5CErKwszMzMAfv31V6ytrSlduvR7aee2bdtYvHgxcXFxSgUgnczMTI4ePUqBAgWoUKECmZmZJCcnM2rUKAwNDTl06JASQLpy5Qpnz56ldevWMoWWEB8BU1NToqOjUavVzJo1CyBbgEcX0rlw4QKpqal89913mJubAy+v5ebMmYORkRFbtmzhxYsX5MqVK1vlMPF2UlNTuXXrFtu2bUOlUmFhYfFalTVHR0clwP7jjz9iZGTEqFGjqFatmnJNLEGR98/MzEwJ8IwZM4b4+HgSExPJly8fFhYW5MuXj/Xr1+Po6Cg3GgghhBBCCCGEEJ8A+RZLCCHE/xTdl9be3t5MnDgReLvBBd0UWo6OjqxduxatVvte2ymEEB+asbExGRkZAKxdu5YOHTqwatUqvLy8+OOPP5T1AgICKFeuHJGRkYSHh/PHH38oUweuWLGCdevWUaVKFZydnd95G58/f8706dNJSkrC29ubzMzMbMtNTU2xtbXlxYsXpKens27dOvz8/JTgTt68eYGX03317NmTxYsXv7YPIUTOMTExITY2lr59+5KcnMzYsWM5ffo08H/Xa9bW1gBs376dO3fuALBs2TL2799PlSpVMDY2JleuXGi1Wgnu/Av29vaMHTsWLy8v7OzsOH36NCdPngRePge6a2BdgGfUqFGsXr2auLg4pTqS+HB0AZ7Q0FCqV6+OSqWiZ8+eJCcns2PHDlxdXVGpVBLcEUIIIYQQQgghPgEGWhl9FEII8T9Eq9Vy8eJFSpYsSb169Vi9ejU2NjZvfXfwjRs3MDIyokCBAv9o+i0hhPjY6d+Vf/fuXR4/fkyjRo24d+8eTZo0ISYmhsKFC/PixQu2bdtGYGAgx48fx9XVlRYtWnD16lUOHTqEsbExe/fuxcXF5b1UvTh16hTjxo1j2bJl9OrVi/j4eExNTZXfFR0dzZAhQ2jTpg3Hjh3D0NCQvXv3KlN8AURFRTFhwgR8fHwYNmyYDPAL8ZHJyspi4MCBzJo1i7Zt22arwPPo0SN69uzJpk2bqFWrFhYWFhw4cABbW1v27t2rVAoT/5x+n33hwgWio6OJi4ujTZs2REZGKqFM/WvgGzdusHjxYjp16kThwoVzrO2fg//y2SI9PZ2NGzfi7+9PVlYW4eHhtGrVSqlOJYQQQgghhBBCiI+fhHeEEEL8T/Ly8iIhIYGdO3dSs2bNf/xluUzDIIT4nOj3aaNHj2bJkiUYGRmRmZnJ7du3ycjIoHXr1kRHR1O4cGEyMjK4fv06oaGhrF+/ngcPHlCoUCGqV6/O1KlTKVy48HudouPMmTOMHTuWlStX8sMPPzB9+nRlCq3Tp0/z7bffcv36dRwcHLh+/bqyTKvV8tNPPzFq1Cjy5s3L6tWrlWo8QoiPy18FeI4fP05cXByzZ8+mUKFCfPHFF8yYMeO99z2fk1evZXVTJ+q7dOkSU6ZMITY2lg4dOjBp0iRcXFyA7EET3b7k3P97+ucuKyuL9PR0tFpttml6/+7zSkZGBhs3bsTX15cXL14QERFBy5YtsbS0fO/tF0IIIYQQQgghxH8n4R0hhBD/U3Rfeq9evZq2bdvSunVrFixYgJWVVU43TQghclxISAhBQUEMGTKE7t27U7hwYQ4cOMDkyZNJSUmhTZs2REVFZauucPPmTVJTU3FycsLU1BQLC4v3NoCrP7h8+fJlfHx8SE5Opn///kyZMkWpMLBt2zaaNm2KRqNh5MiRfPvttzg4ODBr1iyWLVuGRqNh3759ODs7SxhTiI9YVlYWnp6eJCYm0rZtW4KCgihfvryy/OzZszg4OGBhYYG1tbWER96S/nlatGgRe/bs4dixY9SvX5+GDRvSuHFjZd3Lly8TGRn5twEe8e/pPx8zZ85ky5YtnDlzBlNTU3r06EHjxo2Vv/u3DfD4+/vz4sULgoKC6N69uxJiFUIIIYQQQgghxMdLwjtCCCE+W7oBWf0vufX/3bBhQ06fPs2+ffsoWrSoDOAKIf6nnT9/nsaNG5MvXz6WLl1KkSJFlGXp6em0a9eOTZs2ZavAA68PJL6vwVz9wc3Zs2dz584d5s6dy82bN3n69Cmenp5MnjxZCfDs3LmTgQMHcvbsWWUfZmZm1KxZkzlz5uDs7CwD/UJ8Al6twBMcHEy5cuWA7NVjJEjydvTPma+vLzExMZibm5M/f36uXbuGsbExU6ZMoW/fvso2ugBPXFwcnTp1IiwsLNt7hPj39P9uhw8fTlRUFE5OTpQoUYK7d+9y6tQpGjRogLu7Ox07dnyrfWZkZLBp0yZ69OhBr169iIqKep+HIIQQQgghhBBCiHfE+O9XEUIIIT4NmZmZyl2l+gOyjx49wt7eHgADAwNlvQEDBtCpUydiY2OJiIiQ4I4Q4n/a8+fPuXnzJp07d6ZIkSLoMv5arRZzc3Pmz59PixYtWL16NVqtlpiYGAoXLvzagPn7GjzX9ek+Pj5Mnz6d6tWr06hRIwwNDUlISCAuLo6MjAxlILp+/fps2LCBc+fOcfToUczNzfnqq68oV64cdnZ2EtwR4hNhYmJCbGwsWq2WxMREjIyMCAgIoEKFCtmu3SS483Z05yw0NJSpU6fSq1cvevfuTY0aNViwYAE9e/akX79+ZGZm4unpCUCRIkUYNmwYRkZGREdHY2trS1xcnFw7vwO6v9tp06YRFRWFp6cnnp6elC5dmtu3bzNy5Ejmzp2Lg4MDbdu2xcTE5G/3aWZmRtOmTdmxYwdVqlR534cghBBCCCGEEEKId0Qq7wghhPgs7Ny5k40bNzJw4ECcnZ2Vxz09PVmxYgWBgYHUqFGDypUrK8uuXLlCgwYNMDQ0ZPPmzRQrViwnmi6EEB+FgwcPUrNmTZo0acKSJUvIlSuXskyr1aLVahk/fjxjx47FwsKC6tWrs3jxYgoUKPDB2jh//nzc3Nzw9PTEz89P6e/3799PUFAQW7Zswd3dnaioKKUCz5tIpTUhcsabXntvWzEnKyuLQYMGMWPGDPr06UN8fLwyjZ74ZzZs2ICXlxf16tVj5MiRlCxZkrS0NGrVqsWdO3fIysri0aNHTJ8+nX79+inbnT9/nnnz5uHu7p7telv8e1qtlkePHtGmTRsePXrE8uXLKVmyJBqNhuXLlzNy5Eg0Gg1Hjx4lT548/yp4Ku95QgghhBBCCCHEp0E+vQshhPjkPX/+nNjYWCZNmkR8fDzXr19Xlj158gQbGxu8vLxo3LgxAwcO5ODBg6SlpeHq6oqvry+XLl1i165dOXgEQgiR86pVq8bXX3/NyZMnOXPmTLZluoG/Bg0a8OWXX+Lk5MSBAwc++MD58ePHMTU1pVu3bjg7O6PVatFoNNSsWZNJkyZRo0YNEhIS8PPzIz09XWm7fhUhQAYxhcghutfesGHDmD59OvCy8sjb3FNkYmJCVFQUPj4+jB49WoI7/5JKpWLHjh3cv38fLy8vJbhTvXp17t27x/Tp04mLiwNgwIAByvMEUKJECYKCgpRpB8V/Z2BgQGpqKkeOHKFBgwaULFmSzMxMli1bhp+fH1qtliNHjpAnTx4A/vjjD549e/aPfoe85wkhhBBCCCGEEJ8G+QQvhBDik2dpaUlAQAAdO3Zk0qRJREdHc+3aNQAWLFjA9u3bmTdvHkWKFCEpKYkGDRrQunVrtm/fjrOzMyVKlGDq1Kn88ccfOXwkQgjxfmk0mj9dptVq6dChA3fu3GHYsGHZ+kQjIyM0Gg3Jycnky5eP3377jRs3buDg4PCX+3yXtFotFy5cwNTUlKJFiwLZqwl8+eWXTJgwAXg5/ciQIUPIyMjA0NBQqeoh0+oIkTP0+4krV64wffp0PD09WbhwIfD2AR5TU1MmTpyIi4sLKpXqvbX3c2ZsbEzjxo2JiYmhSpUqpKen065dO+7evUtoaCgtW7akc+fOuLu7Ay+rWIaHhyvb66q+yLSD747ub9/S0hKAFStW4O/vj6GhIYcOHcLBwQF4eVNCs2bNWLduXY61VQghhBBCCCGEEO+PhHeEEEJ8FipWrEhAQABt27YlMjKSadOmceXKFQCcnZ3p3r0769atY+fOnTRp0oRffvmFxo0b8+OPP3L79m3u3r3LxYsXgb8e3BZCiE+VWq1Wgi6//PILmzdvJjExkVOnTvHw4UOMjIzo0qULXbt25dChQ7Rp04Y1a9Zw7949AJYvX87PP/+Mvb09KpUKe3v7DzoVh4GBAaVKlSItLY0lS5YALwePdYOeKpWKevXq0ahRI7744gtmzpzJqFGjPkjbhBB/Tr/v2bhxI8eOHaNcuXKYmJjQo0cPFi9eDLx9gEdHKu/8ew0aNKBbt24ArF+/npSUFH744Qd69OihnNe8efNSuHBh8uTJw/jx40lLS/tHz494O1qtFktLSwoVKsT8+fOZMmUKI0aMwNDQkIMHD5I3b15l3cmTJ3Pjxg0l5COEEEIIIYQQQojPi4FWvn0RQgjxiVOr1crdv6dOnSIwMJDVq1czbNgwvL29KVy48GvbnDp1ip9++olFixZx69Ytnj17xnfffcfq1aulMoMQ4rOjH7IJCQlh1qxZ3Lx5E41GQ548eahevToTJ07kiy++4ObNmwQFBbF06VLS0tIoVKgQtra2nD17loIFC7J3716cnZ0/eLsB9uzZQ926dSlbtiyxsbHUq1cPgMzMTExNTQEoU6YMNWrUwMzMjJEjR+Li4vJB2iqEeJ1Wq1Wuq3x9fUlMTKRw4cI4OTnx4sULdu7cCcDcuXPp0aPHa9uI92/MmDGEhYVx9erVbNfM7du3x8HBgV69euHs7EzBggXlufkP/i7sGhQUREhICFZWVtjb23P+/HnMzMyUbZcvX86IESMoV64cCxYswM7O7kM1XQghhBBCCCGEEB+IVN4RQgjxSXm1Kk5WVpYS3Hny5AnlypUjODhYqcATExPD9evXlfV1UyyUK1eOoKAgVq1axdKlSylRogRbt25l//79AHJnsRDis6IbMBwxYgTBwcGULl2a6OhoAgIC+OKLL9i4cSPffPMNx44do2DBgkyYMIG5c+fSsWNHLC0tsba2pmfPnuzfvx9nZ2fUavV7aeerffyrA521a9cmKCiIc+fOER4ezubNmwGU4M7ixYvJzMxk4MCBTJ8+XabWESKH6YIeMTExRERE0Lt3b1avXs369evZvn078fHx2NjY4ObmxoIFC5Rt5Drsw9EFRLZu3ao8tmzZMg4ePEihQoWoUaMGBQsWRK1WS3DnX9BqtdmqT+3bt48VK1awa9eubJ9R/P396dy5M8+ePaNMmTLcvXsXrVaLVqtl6tSpBAQEABAfH4+dnZ1UChVCCCGEEEIIIT5DUnlHCCHEJykiIoKWLVtSsmRJAPr160dWVhYxMTFYW1tz8uRJQkJCSE5OZtiwYQwaNAgnJyfg/4I5+gMQO3bsoGHDhowZM4bg4OAPf0BCCPGebd68mXbt2tG9e3dGjRqVrSKNl5cXcXFxODk5sWXLFkqVKqUsu3fvHjY2NhgaGmJqapqt2tm7pL/flStXcvToUX755RdatmxJzZo1qVChAgAXL15kypQpxMfHkydPHnr37k3dunXZv38/ixcvxsTEhN27d2ebakQIkXOysrJo27Ythw4dYufOnZQtWzZbBZelS5fSrVs3NBoNCxcupEuXLoBU4Pm3Xu2j/+487ty5kwYNGmBtbY23tzf3799nw4YNmJqakpKSQsGCBT9Esz8re/fu5enTpzRr1ixbxZ0RI0YQERGhBGDLlStHXFwctWvXRqPRcOnSJcaMGcPSpUsxNTXlyy+/5N69e9y6dYsSJUqwdu1aXF1d39v7sBBCCCGEEEIIIXKWhHeEEEJ8cgIDAwkNDcXd3Z3IyEiCg4OZPHkyXl5eBAUFkTt3boC/DPDo02g03L9/n6+++gobGxv27t2LlZXVX5a2F0KIT83EiRMZMWIEe/fupWbNmko1AGNjYwB69+7NnDlz6N+/P5GRkZiamn6wwUH9wU1/f39iYmIwMDDAysqK+/fvU6FCBXx8fOjWrRsAf/zxB6tWrWLEiBG8ePFC2U/ZsmVZt24drq6ufztFiRDiw0hLS6NSpUrY2Nhw7Ngx4P+qbOleo1OmTGH48OEAzJ49Gzc3N0ACPP+U/vk6ffo0X3zxxVttt2zZMoYOHcqtW7cwNTWlUqVKLFmyBBcXFwmK/EM3btzAxcUFQ0ND1q5dS9OmTQEIDw8nICCA5s2b07BhQ06ePElSUhLGxsasX7+exo0bK89fTEwMO3fu5Ny5c5QoUYI6derQs2dP8ubNK8+HEEIIIYQQQgjxGZPwjhBCiE/OgwcP6N+/PytXrqRMmTKcPXuWMWPG0LdvXwoXLpxt4OJtAzzPnz+nXr16aDQa9uzZg4WFxYc+LCGEeK88PT2ZPn06p06dylb5QjcQmJaWRuXKlbG0tGTfvn1YWlp+8DYGBgYSFhZGt27d6N+/P7Vq1WLmzJl4eHjg4uLC2LFj+eGHH5T1z549y/nz57l69SrFixenWrVqODg4yOCmEB+R9PR0GjRowMGDB9m2bRv169dXlulCdjdu3KBhw4Y8fvyYO3fusHTpUr7//vuca/QnbujQocyZM4fjx4/j6ur6p+vpXzP//vvv3L59GxMTE8qWLYudnZ30pf/S5MmTGTNmDNbW1syZM4cWLVrQqFEj7O3tiYiIwNnZGYC4uDhGjhzJ06dP2bx5M40bN862n/T0dMzNzZWfJZQqhBBCCCGEEEJ83uRTvxBCiE9Onjx5+OmnnyhYsCDnz5+nXLlyNG/eXAnu6Ctfvjxjx46lbdu2REZGEhsby9WrV7Otk56ezowZMzh69CjffPONBHeEEJ8lR0dHALZs2YJarVYGbI2MjMjKysLa2ppixYpx6tQpfvvtt9f60/dt1apVJCYm0qdPHwICAqhVqxZPnjwhNjYWW1tbrly5gp+fH/PmzVO2KVOmDK1atcLb25tvv/0WBwcHNBqNDDYLkQP+rM8wNzenU6dOaLVa5s+fz/Xr15Vlugo8efLkISMjg+bNm2Nubs7w4cM5fvz4B2n350ir1ZKWlsa1a9eA/zvPrzIwMFCet5IlS1K3bl1q1qyJnZ2d9KX/gu48+/j4MHHiRB4+fIibmxvz58/n2bNn9O3bF2dnZ1QqFfAyVDt58mSsra1p2rQpW7duVfajVqsxNTXNtl8J7gghhBBCCCGEEJ83+eQvhBDik7R582YePHiAo6Mjp06dIikpiatXr75xagVdgKdDhw6Eh4ezYMGC1wYx0tPTadWqFZMnTwb+fABKCCE+Zrq+S78Py8rKAqBt27Y4OTkxc+ZMLl68qCxXq9WYmJgAoFKpKFmyJK6urh90qpr09HS2b9+ORqOhf//+lCxZkrS0NGrUqMHdu3eZP38+M2fO5P79+wQEBDB79mxl21f7cxncFOLD0w8EAko4Qadt27Y0a9aMRYsWkZiYyKVLlwAwNjZGq9WyYMECTE1NmTp1KuPHj+f69evs37//gx7D56RDhw4YGxsTHBxMenr6X/aLf9bXS1/6zxkaGip/+97e3kRGRvLo0SP8/Py4dOmScoOAgYGB8t7l7u5OREQE1tbWNGnShG3btmFoaKj8p9uvEEIIIYQQQgghPn/yDYAQQohPgv7grEajoWbNmqxevZrNmzfTrl07EhISCAkJUQI8Wq022zbly5dn5MiR9O3blx49emT7Etzc3Bxvb29WrVql7P9DDloLIcS7oD94/vjxY+7duwegBHNcXV3p0KEDZ8+epXv37hw7doznz59jZGSEVqtl5cqVHDlyhEqVKmWbpuNDMDAwwNXVlcjISCpXrqwEKu/fv09YWBhNmjShb9++NG7cmJs3bxIWFsaMGTMAGdQUIqfpV2iJi4ujW7du1KtXj7i4OE6ePAmAs7MzXl5eVKhQgbCwMIYOHcqKFSu4ceMG06ZNY+rUqVhbW6PRaGjWrBlGRkZs3LgRkED1v1GrVi0aNGjA/v37OXLkCPDn1XfEv/fqOVWr1RgbGys/Dx48mPDwcB49esS9e/c4ffo08LLi3ZsCPPb29jRu3JiUlBT5LCKEEEIIIYQQQvwPMtDKN2FCCCE+cmq1WhkUWr58Odu3b8fT05Py5csDLwep3dzcWLNmDb169WLs2LG4uLgALwd8Tpw4gaOjI/ny5UOlUmFsbJxtn/q0Wq18WS6E+CTcunULa2trbGxssvVpEydOZOnSpdy4cYPKlSszZMgQqlevTq5cubh37x4+Pj7Mnz+fIkWK0LhxY5o2bUpKSgpr1qwhPT2dAwcO4OTk9MH7w7S0NExMTDAzMyMhIQEvLy98fHwICAjA0tISeDnFyJYtW7h48SJly5bl0KFDyjIhRM7y9fVVKohkZWWRmZlJzZo1GTduHPXr1wdg69atzJo1i2XLlmXb1tXVla1bt1K0aFEOHDjAN998w5AhQ5gwYUIOHMmn4dVrWY1Go1R+MTY2Zvfu3TRu3BgPDw+mTp2acw39H7Bt2zZq1KiBlZUV8LLqjomJCZGRkQDExsbi7e0NwNq1a2nRogXw8nOHVqtVQqjR0dHExcXx888/4+zsnANHIoQQQgghhBBCiJwkt6kKIYT4qOnfzR0QEICHhwcrVqzINpWCnZ0d8+bNo1WrVsyePZuQkBBu3boFwLp162jdujXjx49Ho9Eod8O+KbgDfz51gBBCfEwOHz7Ml19+yfz583ny5InSp/n7+zNixAju379Pnjx52Lp1Kz179iQxMZF79+6RN29eIiIiCAwMxMrKipkzZ9K+fXvi4+NxdHRk7969ODk5vTYFzruiVqv/dJmVlRVmZmYAnDx5EiMjI3x9fbOFc86ePUu/fv3Yu3cv69evl+COEDlIv+rIqlWrSEhIoH///uzYsYNDhw4xbNgw9u3bh5eXF9u2bQOgUaNGzJ49m2XLluHj48OAAQOYMmUKe/bsoWjRovzxxx/ExsaiUqkoV65cTh3aJ0HX769du5YbN24oARDdtW7RokUpXbo0iYmJHD58OMfa+bnr168fjRs3Zv369QAMGTKE2NhYjI2NSU1NBWDgwIHExMQA0KtXLzZt2gS8/NyhX4Fn0KBBHD16FGdn5798vxRCCCGEEEIIIcTnyfjvVxFCCCFyjm4gYsyYMYSHh9OrVy+GDBnCF198kW09W1tb5s2bh5ubG7Nnz+by5cuUL1+ejRs38vTpU7y9vWVqFSHEZyM1NRVbW1tCQ0MxNTWlU6dO3LhxgyVLligVawoWLMjGjRsJCgoiJCQElUpF7969yZs3LyNGjGDw4MHs2LGDrKwsXFxcKFOmDLa2tn9amey/0g9jLl26lNOnT+Po6EjZsmWpV68eBgYGZGZmYmBgwJ07d0hPTyclJYWWLVsq25w/fx4zMzNq1qwJoFSYEEJ8WPrVQu7du8fDhw8pUaIEPj4+FCtWDIBJkyaRJ08eRo0axaBBg4iJiaFBgwZYWFjQoUMHOnTooOzLwMCAa9eukZCQwJIlS+jRowfdunXLseP7VERGRuLj40O+fPkYPHgwX3/9NXXr1gXAycmJQYMG4e7uztGjR6lWrZpUmHwP6taty+HDhxkwYACJiYls2bIFPz8/vL29sbe3V95TBw4ciEajYciQIfTo0YP58+fTrFkz5fnQVU7SVe95H+/DQgghhBBCCCGE+LjJtFlCCCE+etu2baNz5840atSIH3/8UZkS600yMjLo168f8+fPx9LSkpIlS5KcnIyLi4sM8gohPhsqlYqdO3fi4+PD9evXmTJlClqtlmHDhrFjxw5lWkG1Ws0vv/zCgAED+O233wgICKBv377kyZPnjfvVDR6+T7qpdXSsrKzw8/NjzJgxymObN2/m22+/pWLFinTt2pU7d+6wbNkyTE1N2bNnD/nz53+vbRRCZHfv3j3s7OwwNTXN9riPjw+bN2/GwsKCChUqkJCQgEqlwsDAQAkfhIeHM3LkSMqUKUNsbKwyhZZ+f7Njxw7c3d25d+8erVq1Yv78+a+tI14/H1evXiU+Pp7du3dz4MABDA0N6dOnD82bN+fbb7/l6dOn1KlTh+fPn7Nnzx6cnJxysPWfrx07dtChQwcePXrEt99+S3R0NEWLFgVenxorJiaGIUOGkDt3bhYsWEDTpk1zsulCCCGEEEIIIYT4iMi3YEIIIT56p06dIjU1lV69ev1lcEer1WJmZsbcuXPZunUrW7ZsYevWrbi4uKBWqyW4I4T4LGi1WoyNjalfvz4TJ06kYMGC+Pj4sHHjRurXr0/58uXRaDRKpZsqVaowY8YMSpUqRVhYGLNmzVKm8ng1x/++B8lXrFhBYmIiffv25eeff2b58uXky5ePwMBAvL29lfW++eYboqOjOX36NH5+fsTExFCgQAG2bt1K/vz5s03XI4R4v44ePUrJkiWZPXs2KpUq2zIjIyNOnz7NiRMnlNelsbExhoaGys/+/v5MmDCBs2fPMnjwYH7++Wfg//objUaDSqXC2dkZf39/Ce78CbVarZyPI0eOsGfPHlxcXPjxxx/ZvHkzS5YsoUmTJixYsIC2bdvyzTffsH//fooVK8bDhw/ZuXOnsh/xbuj+xn/55RcePXqEvb09u3fv5tixY2RkZAAvp8bSfz14e3szdepUnj59yrfffqs8L0IIIYQQQgghhBBSeUcIIcRHr1u3bqxatYozZ84oQRz9UvK6wZ3MzMzX7gjXXy6EEJ8i/WlOtFpttumntFot27Ztw9/fn+PHj+Pg4MCBAweUO/71HTt2jAEDBnDx4kW8vLwYNmwYdnZ2H/QYxo4dy6pVq1i5ciXFixcHXg56ent7s3fvXjw9PZk2bZqy3YkTJzhx4gS5c+emRo0a5M6d+71N6yWEeLOVK1fi5uZG27ZtSUxMxMTEJNvySZMm4e/vj4mJCWvWrFEqibxacUS3Xr169di0aRNmZmbZ9vP48WOlT5Jrt+z0z0dYWBhJSUlcu3aNw4cPU7FiRWW9p0+fcv36dSZNmsSWLVt48OABuXLl4vbt27Ru3Zrk5OQcOoLP29GjRzl48CBmZmZER0dz9epVpk2bRseOHZXPJq/+TYeFhTFjxgz27dsnFZGEEEIIIYQQQggBSHhHCCHEJ2DIkCFER0czY8YM3N3dsy3TDQinpaXRunVrpk2bRpkyZXKopUII8W7pD/ZlZGRkG+xesWIFVapUwdnZmZ9//pkff/yRlJQUwsLCGDBgALly5Xptf8ePH6dDhw6YmJhw+PBhbGxs3lvb9UM2umkLvby8yJ8/P2PGjEGtVisVCU6cOMHAgQPfGODRJwP6Qnx4Wq2WQ4cOUaZMGWxtbfnll18oU6ZMtv5o4sSJjBgxgpo1a/Ljjz9Sp04dZVv9AE9cXBzNmzfH1dX1L3+fLrAosp8PHx8foqOjadu2Le7u7jRq1CjbOrr/q1Qq7t27R1JSEps3b+bw4cNkZGSwYsUK2rZtm5OH80nau3cvAF9//fXfrpucnMzYsWP5448/iI2N5fvvv1cCPFqtljNnzvDFF18AL8NWNjY2EkoVQgghhBBCCCEEINNmCSGE+Ej8VZb0u+++w8DAgEWLFnHq1Cnl8czMTGUwIyEhgcOHD3P27Nn33lYhhPhQdAPetWvXpmfPnsqUNf379+f777/n119/xdDQkIYNGzJq1CgqVqzIpEmT+Omnn3jy5Mlr+6tUqRKrV69m586d2NjY/GXf+1/oVwdKSkrCw8MDf39/Dh48yOnTp8nIyMDIyEgZbP7yyy+JjY3l66+/Ji4ujiFDhvzl+RBCvF8nT54kLS0NeDntz1dffYWtrS1TpkyhcuXK/PTTT8q0QAB+fn6EhISwf/9+AgICSElJUbY1MDBQpmry9PTE1dX1tem39ElwJzvd+Zg9ezbTpk3Dw8ODCRMmKMEd/XV0faqxsTGOjo4EBASwfPlylixZgqmpKXv27MmRY/iUbd26lTp16jBr1iyePn36p+vppsVq06YNISEhFC5cmIEDB7J06VLl73/jxo107NiRoKAgAKytrdFqtRLcEUIIIYQQQgghBADGOd0AIYQQ4tW7TR8/foyFhYVyl2qtWrXo27cvs2bNIioqCnd3d6pXr64sX7FiBdOnT6dixYp88803OXIMQgjxvqSnp3Px4kVlao3nz5+TkJCAt7c3VatWBcDExIQGDRrw448/4uvry4gRI9BqtXTq1AlbW1vg/yozlCtXDni/VWx0+/X19SUiIiLbMo1Gw7Vr1yhRooQy0Kwf4Bk8eDDR0dFYW1szbty499I+IcSfO3nyJBUqVKBu3bqsX78eKysrpXpW/vz5KV26NIMHD8bAwID27dsrFXhGjx4NwNixYwkICCAsLIw6deooFbb0GRvLVxFvS9d3r1mzhnz58uHp6fnGqRF1dEEeXR+fP39+6tevz1dffcXMmTPp27evVKl8SydOnMDNzY1vvvmGXr16/WW1OkNDQ+W5atu2LQYGBgQGBuLt7c2VK1cAWLJkCffv38fNzQ2QoJoQQgghhBBCCCGyk1tXhRBC5Cj94M706dNp06YNZcuWpWbNmvj5+XH37l0sLS0ZOHAgLVu2JCkpCTc3N0aPHs3y5cvp168f3t7ePH/+nAULFmBvb6/c+SqEEJ86jUaDubk5165do2rVqkyZMoUZM2YwePBgxo0bR6FChQCUSgvffPMNkyZNonDhwowcOZJly5YplQJeHSR8H8Ed/f53+/btzJ07l4EDB3L8+HFOnDhBq1atOH78OJ6enjx48CBbu3QBnoiICNq0aUOfPn3eefuEEH+vSJEi1K1bl927d9OpUyfS0tKUsE2XLl0ICwujQIECDBw4kBUrVmSrwDN69GhCQkLYs2cPY8eOZdu2bYCEFP4LAwMD7t27x5YtW6hUqRKlSpX6y8pF+tvp2NnZUbduXTIzM99YlU282Z49e0hNTaVXr17UrVsXgJSUFFJTU9+4vi6QCi8r8ISFhVGxYkUCAwMJCgpCo9Fw8OBBihQpolTjEUIIIYQQQgghhNCR8I4QQogco18mfvjw4QwaNIiTJ09Ss2ZNNBoNkydPpkuXLmzevJkvv/yS0NBQRo4cyZUrVxg/fjydOnVi+fLllC9fnr179+Ls7IxarZZpVYQQnw1DQ0PUajUmJiZUqFBBefzBgwdKBQCVSqUM0uoCPBMnTqRw4cKMGTOGOXPmKNPffIj2Aty9e5czZ85gZWWFl5cXFSpUoFy5csTGxjJgwAC2bdtGly5d3hjgqVy5MsuWLaNIkSJvNUAthHh31Go11tbWrFu3jubNm7NhwwY6d+6cbQqttm3bEhISQqFChf40wDNu3Dh27dpFbGwsmZmZOXU4nw1LS0vs7Oy4ffs2WVlZr1Uu0gUnb9y4wU8//QRkD+88ffqUmzdvYmJiwrNnzz5cwz9xt27dIj09HXNzcwD69etH586d+f333/902kn9AM93333H0qVLWbhwIfPnz2f37t24urq+VnVUCCGEEEIIIYQQAsBA+2ffOAghhBAfyLRp0xg6dCgDBgzAw8ODsmXLcuXKFQIDA5k/fz5du3Zl/vz5yiDE6dOnuXLlCg8fPqRSpUo4Oztja2srX4QLIT5bT548ISwsjOfPn7Nt2zbOnTtH//79mTRpEtbW1sr0KLr/q1Qqdu3ahZubG/b29hw4cAArK6sP0taQkBBiYmJo2LAhefPmJSYmBo1GowQ2b926RWhoKNOnT6dRo0YsXryYPHnyfJC2CSH+XmZmJqampjx58gQ3NzdWr15N06ZNWb58OdbW1sDLoF1ycjJjx47lxo0bxMbGZptCCyAqKoo2bdrg4uKSU4fy2dBoNNStW5d9+/aRlJRE9+7dMTY2VkIiumvk/v37s2bNGg4fPoyTk5Oy7datW2nWrBldunRh4cKFOXYcn5oLFy7QpEkTbGxscHV1Ze3atQwePBg/Pz8cHR3/clvdFFqvep9TVgohhBBCCCGEEOLTJuEdIYQQOUar1fLixQuaNm1KWloaS5YsoVSpUmRlZbFu3ToGDRqEqakpBw8exMHB4S/3JV+ECyE+J28a9Hvy5Am2trZotVoqVarEiRMn6N+/P5MnT8bKyoqsrCxMTEzQarWo1WqMjY3Zs2cPxYoV+9tBxnclKyuLqKgooqKiuHHjBpUqVeLnn39Wwjm649IP8DRr1oy5c+eSN2/eD9JGIcSf0w9Cp6amcubMGXr37s358+dp0aIFS5YsUYKAbwrwdOjQAVNT02z7VKlUr1WKEa/7u2vZDRs20LVrV8qVK8f48eOpVatWtvO6ZMkSAgICqFGjBrNmzcLCwkJZdvToUXbu3Mnw4cPf6neJ/ztHa9asoVu3bjx79ox27doxefJkXF1d/zScI4QQQgghhBBCCPFvSXhHCCHEB/XqYMGlS5coXrw4I0eOJCwsjIyMDJKTk/H398fQ0JDDhw/j4OCAWq3m999/p0yZMjnYeiGEeP/0B8/VajUPHjwgT548GBoaKgOFT58+pU6dOkqAJzw8HFtbW1QqFT///DO//vor3bt3p3Dhwq/t831LS0tj3rx5xMTEcP/+fRISEvjuu++UQWb9AM/48eOJjY2lc+fOLFy4UAZChchB+tdoo0ePZtGiRZiamvL06VOePHnCs2fPaNmyJYsWLcpWyWvlypWMHTuWO3fuEB4eTvfu3V8L8Ii/pt9Hr1mzhqtXr2JoaEjdunUpV64cBgYG3L59m6lTpzJ16lTKlClDly5d6N69O0ZGRixatIj4+Hg0Gg27du2iUKFCUvnlHenSpQtLly7F2toaR0dH4uPjqV27tvyNCyGEEEIIIYQQ4p2T8I4QQogPRn9g4urVq7i4uHDnzh2cnJwYNGgQERERLF++HB8fHwwNDTl06JBSiSErK4tChQoxduxYvLy8cvIwhBDivdHvJ6dNm8aqVas4cOAAhQsXpkKFCkydOpX8+fNjYGBAWloatWvX5sSJE/Tq1YuIiAh27NiBn58fT58+5cyZM+TOnfuDtl83WKwL8ISGhmJtbU1SUhJff/21MmCsW+/69evExcUxYMAAnJ2dP2hbhRBvFhwcTHBwMN7e3vTu3Zs8efLw+++/M3ToUE6ePPlaBR6AVatW4eHhgb29PUeOHMHS0jIHj+DT5efnx+TJk5WfixUrRu/evZVQ+6VLl5g/fz6xsbHcv3+fvHnzolKpePbsGSVKlGDt2rW4urrKVLLvUN++fdFoNJQvX57w8HDy5MnDpEmTaNy4MSYmJjndPCGEEEIIIYQQQnxGJLwjhBDig9C/+9fPz4+DBw8SGRlJ8eLFKV++PLa2tvTo0YPY2FiMjIw4ePAg+fLlU7YdO3YssbGxzJ49m9atW+fkoQghxHuh308OHz6c6OhoSpcuTc2aNblw4QI7d+6kVKlSTJ48mQYNGmBhYcGzZ8+oX78+R48eJU+ePGRkZJAnTx62bdtG0aJFc2RaD93vfPbsGXPnziU4OJhcuXKRkJBA7dq1lQCPrvqD7v8ytY4QOe/KlSvUq1cPR0dHlixZgqurq7IsLS2N1q1bs2PHjtcq8Gi1WjZt2kTFihU/2DR9nwP9PjoqKooRI0bQpk0bmjZtyqNHjwgPD+fOnTv4+/sTFhaGoaEhjx8/5vLly0RHR/PgwQMsLCz4+uuv6dy5M3nz5pXgzjuifx7T0tKwsrJi7ty5+Pv7kzdvXiZOnCgBHiGEEEIIIYQQQrxTEt4RQgjxQSUlJTFgwAD69+/PkCFDKFasGHFxcXh7e2NiYkK+fPn45ZdflGoRWq2WpUuXMnr0aEqXLs3ChQuxs7PL4aMQQoj3Z/r06QwePJh+/frh7e1NyZIlefLkCcOHDycxMZEmTZqwbt06AIyNjXn+/DnDhg3j9u3bWFtbEx4eTqFChXI0DKMf4Jk3bx5BQUFvDPAIIT4uv/76K5UqVSIgIIDQ0FAlwKDrTx4/fkytWrU4e/YszZs3Z8mSJVhbW2fbh4RH3s6r01d5eXlx9epVoqOjKVKkCPByetn69etz/fp1/Pz8GD9+/F/2nzIl1r/36rlLT0/H3Nw82zqPHz9m1apV+Pn5SYBHCCGEEEIIIYQQ75x8qyOEEOK90mg02X7etWsXNWvWZOjQoRQrVgyAb7/9lh49eqBWq6lcuTLXr18nMzOT9PR0pk6dyqhRo9BoNEyfPh07O7vX9imEEJ8LlUrFypUrKVasGB4eHpQsWZKMjAy2b9/Opk2bKFGiBPPnz8fY2FgJ5lhaWhIXF8eqVauYPXs2hQoVQq1Wv5fgztvm/g0MDNBqtVhZWdGzZ0+CgoJ49OgRHh4e7Nix4633I4T4MF59TR47doxnz54pIRxjY2NUKhV2dnb07dsXExMTduzYQZMmTXj+/Hm2bSW483Z0QZHRo0czZMgQ1qxZQ4sWLZTgjkqlomjRouzbtw8nJycmTpxIQECA8lxlZWUp+9I9JsGdf0etVivnbt68efTt25eKFSvSqlUrJk6cSHp6OlqtFjs7O9q2bcvEiRO5d+8efn5+bNmyJdtzIYQQQgghhBBCCPFvSV16IYQQ75Xui/Dhw4cDcOfOHbp06ULRokWVO7OLFCnCwIEDgZdfmG/ZsoWyZcuSmprK7du3KVq0KGvXrsXJyUnu5hZCfNbu3bvHrl278PDwoGzZsmRmZip3+RsbG7N3714cHBwAOHz4MBUrVsTExESZdkV39//76Cf1qxI8ffoUGxubv5yW69UAj6GhIR4eHgQHB1OnTh1MTU3feRuFEG9H//Ws/+/y5ctToUIFjh49yp49e2jSpInyGtdfJ2/evNjY2HD69GmePXuGpaVlzhzIJ+7OnTvExMQAYGtrq0w5lpWVhYmJCWq1GicnJ/bt20etWrUIDw/HyMiIkJCQbNVePvT0iJ8TrVarvGf6+Pgwbdo0bGxsyJUrF5s3b2bdunXs3r2bESNGULNmTWxtbWnXrh0A/v7+jBo1iszMTL777juZ+lEIIYQQQgghhBD/idyWJYQQ4r1Qq9XKv+/fv8/69euZMmUKP//8M7du3QJeDi7r7hSuVq0aEydOZOnSpXz11Veo1WpKly5NSEgI27Ztw8XFRYI7QojPnomJCSYmJjx8+JAXL16wZs0a/Pz8MDQ05NChQ0pwB+CHH34gNDQUeP8Dt/pVCRITE2nVqhXr1q3729+rH+Dp1q0bc+bMYfHixRLcESKH6V7P4eHhzJo1S6kcYmhoyMCBA3n69Cnh4eGcPXtWuabTbbN161YqVqzI8ePHuXTpEnnz5pWqiP9S/vz52bNnD87Ozty4cYM5c+YowR2NRqNMWaYL8BQpUoTx48czceLEnG76Z0P3PjZ58mQiIyNxd3dn+/btnDp1iu3bt9OmTRu2bt3KiBEjOHbsGAA2Nja0b9+eSZMmcfLkSaKjo1GpVDl5GEIIIYQQQgghhPgMGGilZr0QQoh3TP8O7smTJ9OpUycePHjAiBEj2L59O82bN2fmzJnky5cP4LXKDW8K6ejvUwghPmdVqlThyZMn9OnTh/j4eAwNDTl48KDSZwIEBQURFRXFrFmzaN++/Xttj37/O3LkSGbMmIGtrS1jxoyhT58+b7WPt+nnhRAf1rlz5yhfvjwFCxYkLCyMjh07Ympqyh9//MG4ceNISEigZs2aDBw4kO+++w5bW1uWLVtGaGgopUuXZsmSJRgZGck12lvS7wd150z3/9OnT9OxY0fOnj1LSEgIvr6+mJmZKctVKhXGxsZcvXqV77//nuXLl+Pi4pLDR/T5ePDgAY0bN8bExITly5fj7OysPF9XrlwhPj6eyZMn8/3337NkyRJlu6dPn7Jp0yZq1apFoUKFcvAIhBBCCCGEEEII8TmQ8I4QQoj3ZsyYMYSFhdG/f3/i4+PZvn07oaGh7Nq1i4CAAKVixKv0B4H+akoWIYT4FP1ZcEXX982bN4+BAweSnp5OgQIFOHLkCPnz51fWW7x4MWPGjKFYsWIsXbqUXLlyfZB2BwQEMGHCBNzd3Rk6dCilS5d+43rSbwvxaVCpVGzevBlPT08MDAwIDg6mc+fOmJmZcfHiRaKjo1mwYAGpqakUKVIECwsLfv/9d/Lnz8/evXtxdnbO6UP4ZLza779p6sFTp07Rpk0bbt++TUBAAMOGDXtjgOfVn8V/d+7cOcqWLcuAAQOIi4tDpVJhaGiofB65cOECbm5u7N+/n4ULF9KlS5fX9iGhVCGEEEIIIYQQQvxXcnucEEKId0Z/qqzLly+zYsUKBg4cyJAhQwBo0KABwcHB1K5dm7CwMMaNG/fG/ejfvS0DwEKIz4n+4N6qVauYOXMm8+bN4+7du8o69erVo2PHjlhYWFCsWDFu3LjB/fv3efHiBZMnT2bUqFGo1WpmzZpFrly5Psh0NRs2bGDGjBm4ubkxYsSIbMGd27dv88cff/D8+XPg/6bKEkJ83IyNjWnWrBnx8fFkZWURGBjIkiVLyMjIoFixYowaNYolS5bw3XffYW5ujrm5Od26dWP//v04Oztnu+4Tf06/309ISKBdu3aULFmShg0b4u/vT0ZGBgDlypUjOTmZAgUKMG7cOCIjI8nIyFAq9OiCOrprYwnuvDtWVlZYWFhw79494OW51f8MUrx4cYYOHQrAtWvX3rgPCe4IIYQQQgghhBDiv5Jve4QQQrwzui+td+/ezeXLl7ly5Qo9evSgVKlSyl3CdevWJSwsjICAAMaOHQvA6NGjc7LZQgjxwej6SV9fXyIiIpTHa9asiaenJx07dsTFxYWhQ4diYmLCggUL+OqrryhcuDDp6ek8evSIUqVKsWrVKgoXLvzB7vQ/ceIET58+pXPnzhQpUgSNRsPz58+JiopiwYIFXL9+nWbNmtGrVy+aN28uwUshPiL6FQ11/9ZVfDEyMqJp06bMnDmT/v37ExgYCEDnzp3Jnz8/jRs3pnHjxjx69AgTExNMTU0xMTGRKiNvSavVKudp+PDhxMTEULhwYapVq8bly5eZPHkyO3fuZMGCBZQsWZLy5cuzatUq2rRpQ1hYGIaGhgwePBhzc3Nln9K/vnvm5ubky5ePFStWsGLFCtq3b68EUdVqNcbGxri6ugJw//79nG2sEEIIIYQQQgghPltSeUcIIcQ7NXv2bOrXr8/ixYupWrUq1atXV5bpKjHUqVOHsLAwateuzdixYxk/fnxONVcIIT64xMREZsyYQZcuXUhKSmLs2LFcuHCBIUOGEBUVRWZmJuXKlSMwMJDVq1fTpk0bihcvTp06dYiMjGTLli24uLh80MHzK1eukJWVBbwcuFy0aBEtW7ZkzJgxmJqaUrlyZdauXcu0adN49uzZB2mTEOLNXq18pQvuREZGkpycTGZmZrYKWUZGRjRr1ozp06eTnp5OaGgoixcvJjMzU9lfrly5sLKywsTERNlG/D1d0CY6OpqYmBj69evHhg0bWLNmDbt376ZVq1YcOXIEPz8/NBoNWq2WcuXKKQHNkSNHMmvWrBw+is+bVqslb968hIWFARAfH09KSgrw8vnTVTjasWMHpqamVK1aNcfaKoQQQgghhBBCiM+bgVZq2gshhHiHDh48yPjx41m7di0A27dvp379+spy3Z3eACkpKYwdO5Zdu3YxZcoUBg8enBNNFkKID2rQoEGcO3eO6dOnU7RoUdRqNYcPH6ZTp048evSI0aNHM3jwYExNTf90H/qVNN4n3e/Zvn07PXv2RKVSYWFhwY0bNyhYsCCTJk2iZs2a5M6dmz59+rB06VJOnTpF2bJl33vbhBCv2759O3PmzGHGjBlYWFgojx8/fpyqVatSvHhxpkyZQuPGjTExMcl2XfbixQuSkpLw9vamSpUqeHt707lz57/si8TfS0tLo0mTJqSnp7No0SJKly5Neno6W7ZswcPDA2tra1JSUsibN2+27Y4fP463tzeLFy+mcOHCOdT6/x2pqamEhIQQHR1N9erVcXd354cffiAjI4Pk5GQCAwOxsrJi69atODg45HRzhRBCCCGEEEII8RmSyjtCCCHeCV0W9KuvvmLMmDF06dIFgDlz5nD16lVlPf07vevUqcPo0aNp06YNbdq0+eBtFkKI902tVr/22IkTJ6hduzZFixYFXlbFqFGjBsnJyeTKlYtx48YRHR2tVLrR34eu/3wfwZ03tVX3e6pUqcKkSZMoW7YsLi4uDB48mOPHj/P999/j5OSEpaUlT548wdXVVQaZhcghL168YNasWSxYsABPT0/S09OVZcWLF2f27Nk8e/YMHx8ffv75Z7KysrJdl1lYWPDNN99gZmbGqVOn8PLyYs2aNTl1OJ+Us2fPcu3atTcuu3PnDgcOHKBNmzaULl2arKwsVq9ejZeXFyYmJuzZs4e8efOi1Wo5fvw4KpUKgEqVKrFz505likTxftnb2zN48GD8/Pw4ePAgffv2pUqVKlSqVAkPDw9UKhWrVq3CwcEBjUaT080VQgghhBBCCCHEZ8g4pxsghBDi0/Rq1QfdXdsAVatWZciQITx79oyFCxfi4uLCgAEDcHR0VNbV3endsGFD6tSpg6mpKSqVSilNL4QQnzqNRqNMLbNw4ULOnTuHpaUluXPnJleuXABkZWUp09BUrlyZ5ORk2rZtS2hoKIaGhnh7eyvLIXtf+y7pT8E1f/58zpw5Q2pqKu3ataNy5co4ODjQuXNnOnXqpLRB93+NRsPy5cv59ddfadSokVTpECKHWFhYMGbMGIyMjJg7dy4qlYqEhATMzc2xsbGhXbt2GBgY4Ofnh6+vL1qtliZNmmBqaqr0RSVKlKBEiRK0b9+eDRs2ULNmzZw+rI/exYsXKVeuHLVq1SI5Ofm1qiy6MM7z58/JzMwkOTkZPz8/DA0NOXTokLK+SqXCw8MDNzc3PDw8AJTrYpmm7N/Rryz1pp9f5erqSmhoKPXq1WPq1Klcu3aN3Llz8+233+Lr60vBggU/6JSVQgghhBBCCCGE+N8iI6RCCCH+Mf0vrc+fP8/Nmzd5/PgxFSpUIH/+/Jibm1OtWjVGjx6NWq1mwoQJaLVaPDw83hjg0Q30SnBHCPE50QUc/fz8mDx5crZlV65coWfPnuTKlStbn6oL8Hz//ff4+PhgZWVF//7933tbdb/f19eXiIgI5fHFixfTvn17QkNDKVSo0BsHPhMSEoiIiMDU1JSwsDDMzMz+doBUCPF+lClThjFjxqBSqVi4cCEAM2fOxMLCAmtra9q2bQugBHg0Gg0NGzbEysoKtVrNjBkzePr0KT169GDUqFGYmJhIWOFvGBoa0qFDBzQaTbapynScnZ1xdnZmz549zJs3j3HjxinBHf2psgIDAzl79qxSlU38N/o3Gly8eJFcuXKRJ0+ev93O2NiYZs2a0bBhQ7RaLaampsq+5LUghBBCCCGEEEKI98lAq6uRLYQQQrwF/S/Cx40bx6xZs5RpAmxsbOjatSs9e/ZU7tQ+cuQIwcHBbN68mREjRuDp6UmBAgVyrP1CCPG+6feTS5YswcPDg44dO9KqVSsl0Hjw4EG6detGTEzMawEegIMHDzJo0CBWrFiBk5PTe2urfsgmKSmJQYMG0bFjRzp27EhWVhaRkZHs2rWLtm3bEh0drQR4MjMzuXHjBgMHDuT48ePkzZuXdevW4eLiIoObQnwEfv/9dwIDA1m6dCndunVTAjwAaWlpJCcn4+/vj6WlJT179qRr165s3LiRuLg48ufPz4YNG7C0tMzho/h03L17FxsbGywsLFi4cCE1a9bMFsIJDg4mODgYCwsL8ubNy5EjR7JV6Fm4cCFjx46lTJkyLFq0CFtb25w4jM+G/vtQVFQUCxcuJE+ePCxbtgwbG5u32of++6MEUoUQQgghhBBCCPEhSIkDIYQQ/4h+JYnIyEgaNGiAn58fFy5c4MCBA8yYMYNDhw4xadIkGjRoQNWqVQkKCgIgIiKCZ8+e4e/vT758+XLwKIQQ4v3QarVKP6lWq8nIyKBIkSL4+vpSvHhxABo1akSjRo1YuHAhxsbGTJky5bUAz1dffcWePXvea9UL/ZCRSqXizp07VK9endGjRyuDzi1btqRly5YkJyej1WqJiYmhUKFCZGVlsXz5cm7evEn79u0ZM2YMBQoUkOCOEDnk1XBByZIlCQwMBHhjBZ527dphbm7OmDFjCAoKIjQ0FLVajYuLC3PnzsXS0lICC/+A7rp2zZo19OjRg3bt2hEZGYmzszMAHTp0YPfu3ezevZuqVatiZGTE8+fPsbCwIDY2lilTpgAwffp0bG1tX5ueVrw9/SkrfXx8iI+Pp0qVKnh6er51cAeyT1MprwMhhBBCCCGEEEJ8CFJ5RwghxD+2Zs0aOnXqRM+ePRk5ciSurq5otVquXr1KVFQUUVFR1KxZk5iYGCpXrgzA8ePHGTRoENevX+eXX37Bzs4uh49CCCHen6FDh7J8+XKKFClCjRo1mDRpEgBZWVmYmJigUqmoW7cuBw4cwM3N7Y0Bng/Fx8eHS5cucfbsWTp27EhwcDBarRaVSoWJiQkArVq1Yt26dbRp04bo6GicnJxITU3l4cOHFCpUCHNzcwnuCJFD9F97ly5dwsLCgvz582NoaPiXFXg0Gg337t1j8uTJvHjxAltbW7y9vXF0dJTX87/0+++/ExUVRUJCAm3atGHixIm4uroCLyuqjR07li1btpAnTx6cnZ159OgRt27domjRoqxbtw5XV1c59+/I+PHjCQoKwsPDA09PT0qVKvXaOhJQE0IIIYQQQgghxMdEKu8IIYT4x44ePYpGo6F///7ZBhlcXV2JiIjgxYsXzJw5ky1btijhnUqVKhEfH0/+/Pmxs7OTL8uFEJ+1u3fvcvPmTZ4+fUrZsmWB7MEdY2Njdu/eTd26dZk7dy5GRkZMnDiR3Llzf9B2ZmRkcPDgQfbu3UuePHmUqVo0Gk22tq5Zs4ZWrVqxatUqjIyMmDRpEq6urtjb2wMvB0BlsFmID08/6BEZGUliYiKlS5cmMjISFxcXSpYsSXBwMPB6BR6A/PnzK+FC3bWZhEfejn51HF3/XrJkSYYPH46hoSGxsbEASoDnq6++Ytq0aezdu5c5c+bw5MkTSpYsiYeHBz179iRfvnxy7t+RU6dOER8fz7fffsuQIUMoUqSIsuz48eM8fPiQ/PnzU7ZsWfk8IoQQQgghhBBCiI+GVN4RQgjx1tRqNYaGhrRs2ZINGzZw6tQpZVBa3/Hjx2nevDk2NjYcOXJEGQzWkakAhBCfK/1gopeXF3FxcdjZ2bF//35Kly6t9H+6UIxaraZBgwakpKQwePBgIiMjP/hA4uPHj+nfvz/Lli2jdOnSrF+/niJFiijHomsrQNu2bVm9ejW9e/dm5syZ0pcLkYP0+xsfHx9iY2OpU6cO/fr1o0OHDtnWfbUCT0JCAubm5mg0GgwMDDAwMJBg9T+gH7JZsWIFp0+f5quvvqJp06YAXL58mcjISGJjY+nQoUO2Cjzw8lo4IyNDCVHpHpM+9d3YsGED3333HbNnz8bNzQ21Ws3169eJjo5mxowZPH/+HFNTU0JDQ/Hy8sLc3Fz+9oUQQgghhBBCCJHj5JshIYQQf+rVfKeRkREGBgZUq1YNgLNnzwIvBzD0VapUiXLlynH37l3u37//2n5lYEII8bnSVa0AmDZtGt7e3jx+/Jh27dpx/vx5DA0N0Wg0GBsbo1KpMDIyYtu2bbRq1YrBgwe/18FDXZ+u1WqVf6vVauzs7Jg+fTodOnTg3LlzhISEcO3aNWUwX9dWgOTkZNzc3Bg9erT05ULkMF1/ERMTQ3R0NP3792fGjBmvBXcApQJPp06dWLhwIQMGDODFixcYGhoq+5HwwtvRaDRKcGfkyJG4u7sTHR2NSqUiMzMTgCJFijBs2DAGDhzITz/9hJ+fH1evXlX2YWhoqAR3dP2x9Kn/zpvuR9NN+bhv3z6uX79OREQErVu3Ji4ujubNmzN06FCKFi1KUFAQp06dkr99IYQQQgghhBBCfBRk2iwhhBBvpH9H8cWLF9FoNJQoUQKAypUrY2hoyODBg6latSouLi6o1WoMDAyUgYfMzEwKFiyoTKkihBD/K4yMjJQ+NCoqCrVaTVxcHN9//z3Lly+nRIkS2QI8xsbGrFq1CiBblZt3Sb9PNzAw4Pnz51haWip9dq5cuZg5cyYZGRnKNF5jx47F2dk5W4DH2NiY2bNnv9e2CiHejlar5e7duyxZsoRixYoxcODAbNMDvUoX4DEyMmLevHnY2dkRFRX1AVv8edD1m2PGjGHixIl4eHjQp08fKlWqlG09XYDHwMCA2NhYtFotERERODs7Z1tPgiP/nn61ort375IrVy5MTU0pVaoUDRo0ICEhgYSEBABKly7Nzz//zJdffomdnR1OTk4MHz6cY8eOKTcmCCGEEEIIIYQQQuQk+bZdCCHEa/QHeSMiIli0aBFWVlYkJiZSokQJWrZsyQ8//EBSUhIdOnRg6dKlFC1aVNl+xYoVnDx5klatWmFlZZVThyGEEDlGP8Azbdo0tFot8fHx2QI8arX6tfDL+w7uzJ8/n61bt3Ls2DFcXV2pXbs2nTp1wtXVlVy5cjFv3jx69uxJUlISwGsBnvfdViHE2zMwMODBgwf8+uuv9O7dWwlZv4muOknJkiUZOXIkdnZ2DBs27EM19bOza9cupk2bRpcuXfD19cXFxQXgtanHihQpwtChQwGYOXMmjx8/JikpCScnpxxp9+dE/71txowZrF+/ni+++ILg4GCcnZ2Jj49n06ZN3LhxgzJlytC2bdtsU/leu3aNfPnyUbVq1Zw6BCGEEEIIIYQQQohs5Bt3IYQQ2Wi1WuWL8OHDhxMXF0fNmjXx8/NTqkUYGhoya9Ys7t+/z5o1a6hRowaenp6ULVuWY8eOsWzZMqysrBg3bhympqavDWQIIcSn7NU+7c/6OP0AT2xsLADx8fF06dKFBQsWULp06Q/SVv0+PSoqCmtra2xsbNixYwfr169n9uzZLFu2TKlGoB/gMTIyYsSIEX9ZzUMIkXNSU1N58eIFz58/ByArK0uZMgj+rzLJ48ePuXbtGuXLl+eLL74gOjoaIyMjqaD1Fl49pwC//PILjx8/ZsCAAUpwB95cRadIkSIMGTKEp0+fsnXrVszNzd97mz93+lOX+fn5ER8fj6urK3379sXU1BSAEiVK/GmgbdmyZaxatYoKFSr8ZehNCCGEEEIIIYQQ4kOSSdWFEEJkoxt0iImJISYmhgEDBpCQkECzZs2Al1MFqFQqAFatWsXgwYMxMzMjJCSEzp07M23aNBwdHdmzZw9OTk7KdFpCCPE50Gg0Sp+WmpoK/PWUJ7oAD0BsbCxeXl4cO3aMQYMGoVarlWoY74uubZGRkURFRTFo0CAOHTrEpUuXOHLkCJ06deL333/nm2++4fTp0wBKgKdt27YkJCQQGxuLRqN5r+0UQvw7hQsXxsXFha1bt3Lnzh1MTEyU16tWq1WmFOrcuTNTp07lxYsXAErwQYI7b7Z9+3Zq1qxJZmbma2EogGPHjgGQL1++N26v6/fv378PQNGiRQkODubEiRM4ODhIn/of6f6ug4KCmDJlCr169WLp0qW0atUKINt766vnOjIykpEjRwIwa9YsbG1t3/t7sRBCCCGEEEIIIcTbkPCOEEKI1zx58oQlS5ZQunRpPD09KVasWLblxsbGSoBnypQprF+/niVLlhAdHc2qVatYv349Li4u2crZCyHEp06tVisDhgkJCXTs2JGxY8f+7Xb6AZ7o6GhGjhxJQkICRkZGHyTceO/ePZYsWcIXX3zBwIEDKVmyJCYmJpQuXZrFixczZMgQUlNT6dOnD7du3QJeBngSExP54Ycf8Pb2Vo5bCPHh/VnQQ6vV4uzsTL169bh27Rre3t48fPgQQ0NDMjMzMTAwQKvVsnjxYk6fPo2tre1rFWTEmy1btoyDBw8SGBioPKYfhnJ2dgbg0qVLAMp1sW49Xb8fGhrK3r17lW3s7e2z7Uf8e7t37yY6OpqOHTsydOhQypYtqyy7cuUK586d48mTJxgaGqLRaNi1axdff/01QUFBODg4sHPnTgoXLiw3GgghhBBCCCGEEOKjIbfZCSGEeM3t27fZv3//a1Nl6TM2Nlamivnyyy/58ssvsy3XL2cvhBCfulen6Jg+fTqlSpWiQIECb7W9/vQ0YWFhAO90upqjR4+iUqmoXr36a4OQjx494uTJk/Tu3ZtixYopfbfu90dGRnLx4kXWrVvHwYMHadOmDWq1mly5cpGUlPTO2yqEeHv6QegDBw7w6NEjzM3NqVGjhjL9UmxsLKdPn+ann37i6dOnxMXFUahQIQCSkpKYPHkyVlZW+Pv7S3jnb+zZs4fU1FQiIyOpUKECnTt3Bv5v+rHMzExMTU2pWrUqACNHjqROnTpYWVmhUqkwNDRUrpknTpxIYmIiTZs2zfY7JCjybvz22288evSIPn36UKRIEdRqNY8ePSImJoZZs2Zx584dihQpwqxZs6hbty6WlpbY29vj7+9Pv379yJs3r9xoIIQQQgghhBBCiI+K3O4lhBDiNRkZGQD88ccfykCEPl0FiVu3brF79+437kPuKBZCfE50fdr48eOJiIjghx9+YMGCBXh6er71Pl4Nv7yrMMyNGzdo0qQJTZs25cKFC68tT09PJyMjg1OnTvHw4UNl4Fi/ipqbmxtarZaUlBTg9T5cgjtCfHi6Ci4Ao0aNokGDBjRv3pwGDRrQsGFDrl69CoClpSUrVqygRo0abN68mQoVKlC5cmVKlChB//79ycrKYtOmTTg6OirXcOJ1W7dupV69eixbtgwADw8P7O3tGTx4MKVKlSIrKwtTU1MAmjZtSvPmzfn111/p1KkTT548wdjYWOk7V6xYwfz586lSpQo1a9bMsWP6nD158gSAy5cvo9VqSUxMpGXLloSGhlK0aFGaNWvGhQsX+OGHH7hz5w7VqlVj8eLF+Pn5kTdvXrnRQAghhBBCCCGEEB8dGVkVQgjxGhcXFwoVKsSJEydeGwjWH0gaOXIkU6ZM4dGjRznQSiGEeH+0Wu1rj508eZJZs2bRsGFDhg8fTqlSpXKgZa+zsbHBy8uL1q1bv7ESULly5ahXrx7nzp3j5MmT2ZbpBpp1043ofpbKEELkPN3rcMKECYSHh1O7dm0mTZpE06ZN2b9/P/Xr1+f48eMAFC5cmF27djF69Gjq1q3LkydPcHV1xd/fnz179uDq6ipVRv7C2bNn6dWrF/Xq1cPd3R1LS0sAMjMzuXHjBhcvXqR+/fpkZWUBYG5uzuLFi6lSpQobNmygRo0ajB8/np9++gkPDw+GDh3K06dPmTdvHvb29n869Zn4Z/TP41dffYW9vT3u7u7Y29szYMAAbt++zdq1a0lOTmbt2rW4ublx5coVfv31V+Dl+6Wu+pTcaCCEEEIIIYQQQoiPjXxbIYQQIhuNRoOtrS3dunXj9OnTTJ48WanEA/83kLRw4UJ+/vlnXFxcsLKyyqnmCiHEO3Xnzh0yMjLeGF65du0aV65coVOnTri4uPztvj7EYK1Wq8XW1pYRI0aQkJCAjY0NU6dOJSUlJVsAqUOHDqSmpjJ8+HCuXLmiPG5oaIhGo2HdunUYGRkpIZ43hZeEEB/Gq9Vx1qxZQ+fOnZk5cybDhw9n1apVhIaG8vDhQ9q2basEeExMTAgJCWHdunUcOXKELVu2EBISQoECBSS48zeOHTvGnTt36NixI3Xr1gVg8+bNmJqakpSURJ8+fdi/fz916tRRAjw2Njbs2LGDrl278vDhQ0aPHk3Hjh1ZsGABpUqVYu/evbi4uKBWqyUo8i+9+j6q/9qoXbs28+fPp3v37rRo0YJx48Zx9OhRmjdvTp48eYCXledcXFwoXbr0B223EEIIIYQQQgghxL8h9e+FEEJkoxtc6NatG3v27CEpKYkXL14wYMAAqlWrhqmpKbNnz2bSpEnY2dnh7++v3MEqhBCfskOHDtG+fXuGDRuGt7f3a1NFXb9+HQAHBweA1wbDNRoNhoaGpKamYm9v/0EGaw0MDNBqtVhYWACwZ88ehg0bRtmyZUlKSqJq1aoYGhrStWtXjh49ypw5c2jdujVjxozh66+/xtHRkUWLFpGYmEiZMmVo2bKlsl8hxIeh1WoxMDBQ+hBdvzJhwgTq1q2LsbExvXv3xtXVlaysLMzMzPD19cXU1JTQ0FDatm1LcnIylSpVUvalCy/o+iEJ7vy1tLQ0VCqV0pf269ePWbNmsWXLFho2bMjkyZNRq9XMmTOHOnXqkJKSgomJCdbW1syePZsLFy5w8uRJMjMzKVOmDCVLlsTGxkZCU/+B/rlbuHAhu3fv5vDhw1SpUoVq1arRr18/mjdvTu3atbG1tVVePzrLli0jJSWFWrVqKe/bQgghhBBCCCGEEB8zA63cViuEEOJPHDp0iICAAHbu3ImZmRlFihQhIyODP/74AycnJ7Zs2SLTMAghPhtbtmyhadOmuLu7M23aNCWYqBsQXLRoEd27d6dLly4kJCQo06pA9sH36tWr0717d4YMGfLO2/jq4GRmZiampqbKz6mpqcyePZsJEybg6OhIQkICVapUwdjYmPv37zNmzBiWLFnC48ePyZs3L1ZWVty8eZOCBQuyfft2XF1dX/sdQoj3Z8+ePezYsYNhw4Zlq2So64/MzMzInTs3a9asoUqVKsD/9QNZWVlMnTqV0NBQcufOzapVq6hYsWIOHcmnJyUlhV9//RUvLy9u3LhBhw4duHTpElWqVGHTpk0MHjwYHx8fChUqBMDjx48ZOnQoc+bMoXr16kqAR9f/v+rPHhd/T//c+fj4EBMTg729PQUKFOCPP/4gNTWVtm3bsmLFijduHxsby9SpU9FoNOzatQsnJyd5PoQQQgghhBBCCPHRk2/lhRBC/Knq1asTGxvL9OnTqVChAmq1mkKFCuHj40NKSooEd4QQn5UGDRpw/PhxJk2ahImJCUeOHCE9PV0JsrRu3ZoKFSrw888/s3HjRmVKQd00WxqNhlmzZnHjxg20Wu17mTZL15a9e/eSlZWlBHf8/f3ZuHEj9vb29O3bl4CAAK5fv467uztHjhwhKysLBwcHfvzxRxYsWECPHj1wcnKiePHiDBs2jL179yp9ugR3hPgwXrx4gbu7O4GBgWzcuDHbssaNGzNixAhsbW25d+8e58+fB1BeoxqNBhMTE4YMGcKYMWN4+vQptWvX5tSpUzlxKJ+cbdu2Ua9ePQ4cOMCtW7coVKgQoaGhqNVqNm3aRMuWLRk8eLAS3FGpVNjZ2TFlyhR++OEHDh06pEyhZWBgQGZm5mu/Q4Ii/57u3E2aNImpU6fSt29ftm/fzi+//MLx48epWrUqycnJ/PDDD8o2Go2GnTt30qhRIwIDA7G0tGT79u04OTmhVqvl+RBCCCGEEEIIIcRHTyrvCCHE/7B/cgeqSqXixYsXWFtbAy+/VJfgjhDic/Xjjz8SGBhIXFwc3bt3x8zMDLVazYIFCxg2bBiOjo4MHz6cTp06KRV4li5dSlBQEJaWlmzcuJF8+fK9l7a1adOGffv2kZSUxHfffYePjw+RkZEEBAQwatQoLCwsePLkCUlJSYSEhODk5MTMmTOpWrVqtqnAHj9+jI2NDfAyFCR9uhAf3okTJ0hMTCQ4OJhcuXKRlZWVbTrS0aNH8+OPP5I7d25SUlIoVaqU8lrVr8AzYcIEFi1axPbt2ylYsGAOHtHH77fffqNJkyYULVqU4OBg6tatC4CHhwczZswgT548qNVq5s6dS9OmTZWQpO6861fgqVWrFtu3b89WAU28G7dv36Zx48bY2dmRmJhIqVKlSE9PZ+fOnfTu3Rs7Ozt2795N3rx5lW2WLl2Kv78/HTp0wNfXl/z588t7mxBCCCGEEEIIIT4ZEt4RQoj/AW+aZsXQ0DDbIO5fBXmkzLwQ4n+JVqtl3bp1eHp6YmRkxNixY+natSvm5ubcu3ePpKQkpkyZwoMHD6hZsya1a9fm119/Zf/+/VhbW5OSkoKLi8t7mX5KpVKRmJjIqFGjcHFxwdHRkY0bNzJq1Cjc3d1xcXFR+uw3BXiqV68ulXWE+MjoXrOjR48mMzNTCQHqBAYGEhoaSr58+di3bx9FixZ9LcCjC1nb2NhIWOFvrFy5kk6dOjF58mQGDx4MwJo1azh48CDPnz+nRIkSxMTEcO/ePeLj42nTpo0SqNIP8Pj4+JCYmEiLFi1Yu3ZtTh7SZ+no0aNUq1aN2NhYPDw8yMrKYuXKlfj5+WFoaMjhw4dxcHBArVZz/vx5SpcuDcDNmzfJkycPZmZmMg2kEEIIIYQQQgghPinGf7+KEEKIT5n+AM6SJUtISUnh3LlzWFpa0rNnTypWrEiJEiUwMDD405COBHeEEP9LDAwMaN68OUlJSfTt25fAwEC0Wi1du3Ylb968uLu7U758eSZNmkRKSgp79uzB2dmZZs2aMXHiRGWKjvcxeG5sbEy/fv1wcnKiXbt2nDhxglatWjFgwACcnJyUgUqtVoutrS29e/cGICQkhH79+pGQkED16tWlXxfiI2JgYMC9e/eYMWMGjx8/xtbWlmHDhikBnuDgYABCQ0OpVasWe/fupVixYtkCPMbGxtjY2KDVaiW48zfUajVqtVr5uX///iQkJLBhwwYaNGiAqakpefLkYezYsXh4eGBgYEDr1q0xMTHByMgItVqNnZ0dEydOxNbWFm9v7xw8ms/X48ePgZfve2q1mp9++okRI0ZgaGjIoUOHcHBwAOD58+cMHToUDw8PWrVqpVSe0mq1EtwRQgghhBBCCCHEJ0Uq7wghxGdMP4zj4+Pz/9i7z4Aorvbv49+lCwpiAxUQRAUVo7FhLLHErrErJvaCRBFQUUAFEbAiFgS72LCLJfYSjdh7jcaCvWLHStvd54XPzh8sicmtEPX6vElgZtYzA3NmmPOb6xAVFYWenh7m5ubcv38fQ0NDatasSVBQEDVq1Mjm1gohRNb7q7fy1Wo127dvx93dHY1Gw/Dhw/n555/JkSOHss6lS5d4+vQpjo6OGBkZYWJikiVVLyZMmMDAgQMxNDTEwcGBCRMm0KhRo0xBzDcr8IwePRoDAwPWrVtH+fLlP2n7hBD/3Llz52jTpg2XLl1i8ODBDBw48J0VeKysrNi7d2+mCjziwyUmJtK5c2f27duHq6srO3bsoF+/fvj6+lK4cGHgdf+/cuVKgoKCePjwIdOnT1cCPPC6CpqBgYHSz+q+Fh/P9evXKVWqFM2bN8fNzQ1vb29UKhWHDh3KNFVWv379mDt3Lps3b+a7777LxhYLIYQQQgghhBBC/G8kvCOEEF+BiIgI/Pz86NevH127dsXFxYVNmzaxZMkSFi9eTNmyZZkyZQpVq1bN7qYKIUSWyTjoffjwYYyNjdHX16d06dLKOunp6ezYsSNTgKdDhw6YmJi88zOzaprBFStWkJiYiFarZdiwYdjY2BASEkKrVq2UdsD/VU57+vQp0dHRLFq0iN9++42CBQt+8jYKIf65c+fO0bJlS65evfreAM+IESNQqVRcunSJIkWKZGNrP18HDhygVatW3L17l/r16zNlyhQcHR0BMlU0iouLe2+AR/zv3hegVavVpKSk0KNHD5YtW0bu3LmxtLTk8OHD5MmTR1lvwYIFBAcHU6FCBebNm0fOnDmzsvlCCCGEEEIIIYQQH5WEd4QQ4gt3/fp1mjVrhp6eHqtWrcLe3l5Z9urVKwIDA5k4cSKtW7dm6tSpmd5kFUKIL1XGkE1gYCBRUVGo1WoMDQ3x9PQkKCgIY2Nj4O0AT0hICB06dMDY2DhLwjrv+zeSk5MxMTFh0aJFeHp6YmtrS2hoKC1btsy03r179yhQoADPnj1DpVKRM2dOqdYhxH/Y3wV4Bg4cyOLFizl8+LBSKUZ8GF1/6uPjQ1RUFAULFuTJkydMnz6dNm3aKJXVdKESXYBn2LBhJCUlERERgZubm1TZ+QgyXoeOHDmCVqtFrVZTpUoVZZ2dO3fSuXNnbt68Sb9+/ZgwYYLyM5w5cybh4eHo6enx+++/U7hw4SwL0AohhBBCCCGEEEJ8ChLeEUKIL9wff/yBq6sr3bp1Izo6Gsg8EPzw4UO6d+/Otm3b2LVrFxUrVszO5gohRJYaPXo0QUFBuLq6UqlSJdavX8/ly5dxc3Nj9OjRSuAxY4BHT0+PgQMH4u7ujpGR0SdtX8bBzbS0NF69eoVKpSJXrlzKOsnJyaxatYo+ffpga2vL8OHDad26NQBr1qxh9OjRjBo1ih9++AHIuupAQoh/7+8CPElJSVhYWEgQ719auHAhp0+fpmTJkkyZMoWzZ88SGRlJx44dlcpqGQM8K1eupHfv3hQsWJBDhw5lmj5R/HMZr0PBwcFER0fz6tUrkpOT8fb2xsPDg5IlSwKwbt06vLy8uH79Oi4uLtjb23Pjxg0uXrxI4cKF2bJlC/b29nIuCCGEEEIIIYQQ4rMn4R0hhPiMZXzwrXtb9c03gQ8ePEjVqlVp3rw5q1atIj09PdM6Wq2WqVOn4uXlRUhICEFBQVm6D0IIkV1evXpFixYtsLW1ZejQoTg4OHDmzBmmTJnCjBkzaNGiBRERETg4OACvAzw7d+6kefPmlCpVip07d2JmZvbJ2pdxIHLWrFls2rSJ06dPU7hwYbp06UK3bt2UdVNSUli5ciWenp5YWVnRp08fjI2NiY6O5vz581y8eFGm1xHiM5MxwBMYGEi/fv0y9TkSxPsw7ztOKSkpGBsbs379eoKDgzl//jyRkZGZpkbUBXjUajUbN26kYsWKMu3gRzR8+HBCQ0OpXLkyVapUYceOHZw5c4YGDRoQHByMq6srAPv372fdunWsXr2aZ8+eYWdnR506dejbty/W1tYS3BFCCCGEEEIIIcQXQcI7Qgjxmco4EHH79m0KFSqkLJszZw4lS5bku+++49GjRzRq1IgLFy5w4MABnJyclAfcuiDPhQsXcHZ2ZuTIkQwePDi7dkkIIT4p3SCszsOHDylbtixRUVGZppq6evUqkyZNIioq6q0AT1paGvv376dYsWKZ+t1P2daBAwcSGRlJvnz5KF26NFevXuXy5cv4+/vj4+ODtbU18Hogeu3atXh6evLgwQMMDAywt7dn69atUpVAiM/UuXPnaNeuHX/88QcTJkygX79+2d2kz0rGfu/x48c8ePAAU1NT8uXLp0yNCHxQgOddnyn+mYzH7sGDBzRv3hwXFxcCAgJwcHDg4sWLLFy4kLFjx/L9998TGhqaaRqtFy9e8OzZM/Lnz4+enh4qlUp+HkIIIYQQQgghhPhi6P39KkIIIf6LdMEdV1dXmjVrxo0bNwDw9PSkZ8+eXLp0ifT0dPLkyUOjRo1ISkrCzc2N69evo6+vT1paGgYGBqjValauXAmAk5MT8DoYJIQQXxK1Wq0Mvu7YsYPNmzezYcMGihUrRpkyZYDXlXUA7O3t6devH15eXqxZs4aBAwdy9epVAAwNDfn+++8pVKgQarX6k7VX19bRo0cTFRVF79692bRpE7/99hsxMTGYmJgwduxYRo0aRWJiIgDGxsa0bduWvXv3MnToUCZOnMiuXbskuCPEZ8zZ2ZklS5bw/fffK9PhiQ+j0WiUfi88PJzatWvj5OSEo6MjlStXZsuWLSQlJQHQtGlTQkNDcXJywsfHh0WLFpGcnAyQKbgDSF/6P9Adu6VLl3L48GHOnj1L+/btlYBs8eLF6dOnD0FBQezatYthw4Zx8OBBZXszMzOsra3R19dX/haSn4cQQgghhBBCCCG+FFJ5RwghPnMNGzZk69atNGvWDAsLC2JjY+nfvz/9+vXD1tZWWa9Vq1asWbMGR0dHYmNjcXJywtLSksWLFxMWFoaJiQm//fYbefPmzca9EUKIT2vIkCFMmDBBmWYwJSWFkJAQ/P39MTIyylRhQVeBZ/r06VSrVo158+Zl6lc/lrS0NAwNDd/6/q5du/Dw8KB8+fIEBwdTokQJXrx4QaVKlXj8+DG2trYcOXIEb29v/P39KViw4Dunh5HgjhCfP121RDmfP0zGvnDgwIFMnDiR7777jh9//JEbN26wadMmnj59ire3Nx4eHhQoUACADRs2MGzYMC5dusSIESNwd3fPVKFH/O82bNjAjz/+SMmSJdFoNBw8eBBzc/NMv9v37t1j1qxZhIWF8f333zNq1CgqVqyYzS0XQgghhBBCCCGE+LQkvCOEEJ+pjAPM7u7uxMTEANCjRw8iIyMxNTVFq9UqA9QAP/30E8uWLcPQ0BBbW1uMjY25dOkS1tbW7Ny5E3t7+7emBhBCiM9ZxgHcCRMmMGTIEH744Qfq1KnDrl272LdvH5aWlkycOJEGDRpgYGCQqR+8du0aoaGhbNu2jWPHjpEvX76P2r7ff/+dJUuWEBISQsGCBTMtGzVqFMOGDWPv3r24urry4sULXF1defDgAZGRkRQqVAgPDw/OnTuHr68vAwcOxMrK6qO2TwghPmexsbF4eHjQpUsXBgwYQPHixUlLS2PkyJGEhoZSuXJldu7cqUyRBbBx40Y8PT0xMDDgxIkTmJmZZeMefH7e/FsiNTUVIyMj5evr168zadIkFi9ezL1791i8eDFubm6oVKpM12xdgGfMmDGULFmSmTNnUq5cuazeHSGEEEIIIYQQQogsI6OzQgjxmdLT01OmbMmfP7/y/YSEBJ48eQK8HrQ2MDBQpoJZsmQJU6dOpU2bNqSmppI/f348PDzYt2+fMq2KBHeEEF8KjUajDAKmpqZy7tw5mjdvzpQpU/D19WXmzJmMGDGCJ0+eMHjwYH777TfS09PR09NDo9EAUKRIEUJCQjh16hT58uVTvv8xvHz5kvnz5zN79mxCQ0O5e/dupuU9e/Zk6tSpuLq6kpKSQocOHbh9+zahoaG0bt2aGjVq0KFDB+B1MMnX15cHDx58tPYJIf4deT/mv2PLli1YWlrSu3dvihcvTmpqKmvXrmXu3LkUK1aMdevWYWJikqlvb9y4MbNnz2b37t0S3PmHtFqt8rfEvn37AJTgTlhYGJs3b8bOzg5vb286d+6MiYkJs2fP5uLFiwBKgAegQIECuLu74+XlxcOHDylUqFA27JEQQgghhBBCCCFE1pHKO0II8ZlLTk5m/vz5JCYmcuzYMdauXUv9+vWJjo6mWLFiyhusb07LkpiYiJWVlUzDIIT44oWEhPDq1SsWLFhAWFgYPXr0UCoDPHv2jLi4OPz9/bG2tiY8PJy6deu+VYEHeOeUVP+rP/74g4kTJzJ37lx69uxJaGgo1tbWb/3bu3fvplmzZrRt25Zp06Yp/fXSpUsZMmQIxYsX58yZM5w+fRpLS8uP2kYhxId7s3KXjY3N395ffYq+5Wun1Wp58eIFTk5OVKxYkV9//ZXU1FRWr16Nn58fenp6HD58WKmmtnv3bpycnJTps3SkIuW/U79+fY4dO8b8+fNp0qQJAwYMYNKkSYwePZp+/fphbGzM9evXmTx5MpGRkTRq1IhJkyZRtGhRIPM58fDhQwwNDTE3N5efhxBCCCGEEEIIIb5oBtndACGEEP8bExMTunTpopT779ChA0uWLKFv375MmTIFR0dH0tPTMTQ0RKvVkpSURO7cuZWpVXQDShLcEUJ8iW7evMmsWbNISkoiR44cFC5cGPi/AdlcuXLRpk0bAPz9/fHz8yMiIoLatWtnCjwCn2Rw3cXFBV9fX9RqNbNnzwZQAjwZnTx5kqSkJFq2bJmpv/7999+xs7Nj4sSJWFtbY2lpKUEAIbJJxiB0ZGQk06ZNo1atWkRGRmJsbPzW+rp+SHefJkHqj0elUpEzZ07y5s3L3bt3efDgAXv27FGCO4cOHco0DWK3bt0oW7Ysy5cvz/QzkKDIv/Pjjz+yc+dOhg4dyrRp09i4cSMBAQG0b99eORfs7Ozw8fEBYNKkSQBMnDgRR0fHTFNo5c2bF8hc1UcIIYQQQgghhBDiSyRPPoQQ4gtgYmKiTKG1aNEifvrpJ7Zu3YqnpycXLlxQKuusX7+ekJAQjh49qmwrA7xCiC9Z4cKFWbVqFeXLl+fBgwfExMTw6NEjDAwMlKk5dAGesWPH8vDhQ7p168bevXuzrI2lSpXCz8+Pzp07M3v2bIYNG/bWFFoODg4A7Nq1C3g9iLlixQq2bdtGmTJlKFWqFHny5Mk0VZgQIutoNBol9OHn50dgYCBWVlbUqFHjncEdeB0MWbduHfXq1SMxMRF9fX2Zcusj++abbzh+/DjBwcH4+Pigr6/PwYMHM005O3bsWJKSkqhfv76EQz4CrVaLl5cXy5Yt4/Tp02zevJnmzZvj6+tLkSJF0Gg0yu+5ra0tPj4+9OvXj40bN9K/f38uX74MvP03ilzbhBBCCCGEEEII8aWTyjtCCPGF0NfXV6bAWrRoEQBLlizBw8ODSZMmcf78eYYNG8bt27cZPHhwNrdWCCE+Pt1b+rpBQZVKhUqlomLFiowbNw5vb2/WrVtHpUqV8PDwwMLCQtkmV65ctG3bllevXjFjxgycnJw+aVt1FTZ0lTd0AR7gnRV4HBwcqFixImPHjiUhIYHk5GQOHTqEqakp/v7+yufKwLMQ2UN37oWFhTF+/Hj69OmDj48PxYoVe+f6Go2GlJQUhg8fzvHjxxkyZAjTpk3DyMgoK5v9xdL1rX5+fuzbt49p06ZRoEABDh48mGlqrKVLlxITE0OpUqVo06aNBEQ+At0xvHbtmnI9Pnv2LIcOHaJRo0bo6ellCprqAjwAU6ZMoWvXrsTGxlKkSJHs2QEhhBBCCCGEEEKIbKLSyqt9Qgjxn/dPplHQBXgAunbtyoIFCzAwMMDAwICCBQuybds2ihYtqgxqCCHElyBjP5mWloZGo8lU7UKtVnP06FE8PDy4evUqQUFB9OjRI1OAB+DFixcAmJmZZckUNhcuXKBEiRLK12fPniU8PJwFCxbQs2dPQkJCKFiwIADx8fFMnjyZTZs2kSNHDsqVK8fcuXOxs7OT6XaE+A84duwYrVq14ttvv2XChAlKxSyA48eP8/TpU8zNzXFxcVGm5UtISKBdu3Y8ffqUU6dOYWpqml3N/yK9fPmSmTNnKtMyBQYGUqVKFSwtLZk5cyZz5sxBT0+P3bt3Y2dnJ/fH/4OMx06r1bJp0yYuXbpEamoqfn5+ODs7M2LECFq2bKmsD/8XfLt58yZhYWFs2rSJo0ePZqqOJIQQQgghhBBCCPE1kPCOEEJ8RmbMmEGTJk2wsbH5y8GFjAGecePGkZCQgKGhIUOGDKFQoUIyyCuE+Ow9e/aMnDlzolKpMvVp0dHRbNq0icePH+Pq6sqgQYMoUKCAMn2gLsBz5coVhg0bpgR4smPAdvDgwYwdO5aNGzfSsGFD5ftvBniGDx9OoUKFlP1+/Pgxenp65MmTB1NTU+nThfiPWLNmDa1atSI6Opo+ffqg0Wi4desWkydPZurUqbx69Qpra2s6d+5MSEiIEjBcsmQJXbt2JSIiAi8vr2zeiy/Pw4cPWbZsGRMmTODy5cvkyJEDeF0hpmzZsixevJgiRYpIX/o/yHjs9uzZw9OnT2ncuDFpaWkYGhoyd+5cevbsibOzMyNHjqRFixaZtr9//z758+fn7t27mJiYkDt3bglSCSGEEEIIIYQQ4qsj02YJIcRn4tdff6V3796MGjWKgICAv3yYrRuk1tfXZ9CgQcD/vQ0rAxNCiM/dnj176Nu3L1OnTqVKlSpKnzZo0CDGjx9Prly50Gq1HDhwgL179xIWFkbt2rUxMjKiQoUKzJgxAw8PD8LCwtDT06NLly5YWlpm+X6Ym5sD0K1bN+bNm0eDBg0A3jmFli7AkytXLnLlyqV8hlarlT5diP+IlJQUAP7880+OHDlCfHw8ixYt4s8//6R+/fqULl2alStXMnv2bNq1a0f58uUBqF27Nt988w13797NzuZ/sfLmzYu7uzvNmzdn1qxZPHjwAH19fWrWrEmtWrXIkyeP3B//DzQajXLsQkNDiYmJ4caNG+zatYvq1asDr69zKpWKHj16MHToUDQaDa1atQJeh96mT5+On58fderUAV5f2yS4I4QQQgghhBBCiK+NVN4RQojPxJkzZ3B1daVs2bKsXbuWPHnyKNO8vE/GqWCEEOJLoFarmTx5Mr6+vpQvX54pU6bg6urKzp07adeuHW5ubnh7e2Nqasr8+fOJjIwkX758hIeHU69ePYyMjNBoNBw5cgRPT0+OHj3K9OnTcXd3z5b+Mjo6Gh8fH/LmzUtsbKwS4IHMFXg8PDwICgpSKvAIIbLPm/dXuq8fPnyIu7s7a9asQV9fH61Wi5OTE1FRUXz77bdYWloye/ZsevXqxbJly2jbtq3yGcePH6dMmTJK5UTxf86dO4eVldUnC1lKhZd/L+O5MGjQICZNmkTbtm3p06ePEtzJaN68efTs2RN7e3sGDhyIkZEREyZM4MKFC1y+fBkbG5us3gUhhBBCCCGEEEKI/wwJ7wghxGdA92A8KCiIkSNHsmnTpkwDvEII8TVJSkpi3rx5hISEYGNjw6JFi4iPj2fcuHFs2bIFZ2dnZb01a9bg7+9Pnjx5GDduXKYAz4EDBwgJCSEmJuaTDhi+OTCs1WozVRWYPHky/fv3f2+AZ/z48cydO5eBAwcyZswYGWQWIhtlrNCSlpZGenq6Mg0TQEJCAitWrODatWuULVuWTp06kTNnTmW5p6cnS5YsYc+ePZQqVeqtIJBUgMlsy5YtNGrUiEmTJtG3b9+P2v9JyP3jWbRoEe7u7nTv3h1fX18cHBzeu+7ixYvp3LkzGo0GAwMD7O3t2bp1K/b29vL7L4QQQgghhBBCiK+ahHeEEOIzohvAqFevHkuXLs2WaV6EECI76QZbk5KSmDNnDsOHD6do0aKULl0aS0tLoqKi0Gg0AOjp6fH8+XNWrVqFn5/fOwM8arUaQ0PDLBkwTEhIoFixYsp+vBng6devH3nz5mXx4sXUq1dP2e706dPMmTOH/v37Y2dn90nbKIR4v4xBvEmTJrFt2zZu3LhB06ZNady48TsrjehotVri4uIYPHgwzs7OLFmyJNMUeOJtx44do2HDhpQqVYrg4GBq166tLJPgzX9L586d2bJlC9u3b8fFxeVv19+3bx9btmwhV65cdOzYEWtrawnuCCGEEEIIIYQQ4qsn4R0hhPgPed9D6/T0dGUahXbt2rFlyxb27t2Li4uLlPoXQnzRMg7QarVaNBqN0k8+e/aM2bNnEx4eTmJiIpUqVWLLli3kzp0702foAjz+/v4UKFCA4OBgfvzxRwwNDbNsP/r168eCBQtYunQp9evXV/YnY4AnPDycgIAA8ubNy8KFCzNV4NFdB2RwU4jsN2jQIMaPH0/u3LkxMjLi3r172NjYEB0dTbNmzYDMQR/ddH/R0dGkpaWxd+9ebG1tJYDyHrq+0dfXl4ULF7Jo0SKl3zx58iRly5bN5haKjJ4+fUrx4sVxcnJi165dmf5u0fm733W5tgkhhBBCCCGEEEKAjPYKIcR/iO6htZ+fH2FhYezbtw8g0wPwzp078+zZMyZOnAggwR0hxBdLo9FkGuxTqVRKP7lv3z5y5sxJjx49GDRoEEWLFuX69evs27cPtVqd6XNy5sxJq1atGDduHGfOnCEqKuqtdT61/Pnz8+TJE/r168e2bduU/VGpVEqlID8/P1q2bMnDhw/p2bMna9asUbbXXQdkcFOIrKc7RwG2bt3KnDlz8PT0ZOfOnZw7d46xY8dy8+ZNunTpwurVq4HX92fJycmcPXuW7777jpCQEPLly8eePXuwtbVFrVZLcOc9VCoVenp6vHz5kqSkJGUKpr59+9K2bVvOnDmTzS0UGZmZmZE3b15u3brF/fv33wru6K7ld+7cITIy8p2fIdc2IYQQQgghhBBCCAnvCCFEtniz6FlaWpry/+fOnSM6Oprg4GDq1q1Lx44d2b17Nw8ePACgSpUqlC5dmvXr13P+/Pl3fp4QQnwJdOHENm3aMHr0aOX7PXv2pG7duhw6dAhzc3O6d+9Onz59SEtLIyAggKNHj77VL+bMmZPmzZuzdOlSFi5ciImJySdrd8aBfp2hQ4cyceJEzp07R9++fd8K8KSmpgJQpEgRbG1tuXXrFsOGDSMlJeWTtVMI8WF0fdHNmzdJSUmhUKFCeHt7880335A7d24GDRrEjBkzePnyJd26dVMCPCYmJty+fRsnJyf69evHunXrsLOzkyojH6hMmTKkp6fj6elJhw4dmDp1Ki1atCBfvnzZ3TSRgb6+Pi4uLly5coX58+fz8uVLZVnGClTjx49n+PDhyt8vQgghhBBCCCGEECIzmTZLCCGyWMay8RcvXqRo0aLKAE5QUBBeXl6oVCqOHj1KaGgof/zxB69evaJ8+fL4+/tTt25d9u3bR5MmTRg/fjz9+vXLxr0RQohP648//uCbb76hQIECjBs3jsOHDxMdHY2npyeDBw+mUKFCACQlJTFnzhxCQ0Oxs7Nj5syZVK5c+b2VLT7V4HnGz92xYwePHz+mXr16mJubAxAZGUn//v0pVqwY0dHR1K9fP9N1oVWrVpQrVw5nZ2dcXV0pUqTIR2+jEOKf69+/P7Nnz8bFxQUbGxtWrFihVPDSnfMxMTH06dOHHDlyEBMTQ+vWrQF4/PgxOXPmxNDQUKY7/QAZ+8T+/fsr1Vq6detGVFQUpqamMuVYFvu739tDhw7RunVrzMzMCA8P54cffsDMzExZvnz5coYMGUK5cuWYP39+pmVCCCGEEEIIIYQQ4jV5aiiEEFlMN9DQuHFjOnTowNmzZwHw9fVl5MiRzJw5E0tLSxo2bMj69evZtWsXnTp14sqVK7Rp04bKlSszZ84cLC0tmT17NgkJCdm5O0II8dGlp6cr/+/i4sL+/fvR09Ojd+/eREdH4+vrS0hIiBLc0Wq1WFhY0KNHD4YNG8aNGzfo1asXhw4dem9lsk8R3NFoNMrnBgcH06FDB2WKF10FHR8fHyZOnEhCQgJ9+vRh7dq1ynVhxYoVHDlyhPz589OuXTuKFCmS5dN7CSHerWjRorx48YJjx44pFRP19fXR09NTqm316NGDqVOnkpyczC+//MKiRYsAsLS0xNDQEJDpTj+ESqVS+m5jY2Pl+3/++SeJiYmAVJ3MSmq1Wvm9XbduHaNHj6ZXr14MHz5c+Tvkm2++oV+/fiQmJuLt7c2wYcM4deoUly9fZsyYMQwZMgSNRsOkSZMwMzOTn58QQgghhBBCCCHEO0jlHSGEyCK6N4S1Wi1PnjwhNDSUGTNm0LBhQ4yMjFi+fDmDBw+mV69eFClS5K03ik+fPk18fDyRkZHcv3+fp0+fYmFhQWxsLE2bNpUpGIQQX4Rdu3axYsUKhgwZQsGCBZXvN2vWjPXr12NqasrgwYMZOnQo8H+VbnR95tOnT4mJiSEsLAwHBwciIyOpVq1allZo8Pf3Z/z48XTt2pUuXbpQo0YNIHPlgsmTJ9OvXz/09PTo1q0bSUlJxMfHkzNnTvbs2ZNp34UQ/w3z5s2je/fuACxevJj27dsDr+/xtFqtcn7PnTuXHj164OTkxLFjxzAxMZEqMf/CxYsX6dOnD9WqVePChQssXbqUmjVrEh0dTenSpaX6ThbIeN3y8/NjypQppKamYmJiwosXL7C0tGT06NG4ubmhp6dHbGwskydP5sKFC5iYmCjnRqlSpVi1ahX29vbyN4sQQgghhBBCCCHEe0h4Rwghssj9+/fJnz+/8vXDhw+ZN28efn5+AHTo0IHRo0dTuHDhTA/K3yxTf/nyZS5fvkxUVBTr1q2jatWqbN++PdObyUII8Tl6+fIl7u7uLFmyhJkzZ9KjRw9UKhVXr17F29sbY2Njfv/9d1QqFcOHD6d3795K1YuM/3369Clz586lf//+/PDDD2zcuFGpevGpxcXF0blzZzp37kxAQAD29vaZlmfs0xcuXEhERASnTp3C0NCQsmXLsmLFCqXijgxuCpH1MoatdcGQtLQ0pQ9ZsGABXbt2pVChQkydOpVmzZop22UM8CxdupRq1apha2ubPTvyhbhw4QKFCxfGzMwMd3d3YmJiqFWrFtHR0ZQqVUoCPFlkxIgRDBs2jF69etG9e3cqV67M4sWLleo7M2bMwN3dndTUVO7du8e0adO4f/8+KpWKqlWr0qRJE/LlyyfXNiGEEEIIIYQQQoi/IOEdIYTIAgcOHKBq1apMmzYNDw8P5fsBAQGEh4ejp6fH999/rwxEAG8NRrz5dXp6Oq1bt2bz5s3s2LGDatWqyQCGEOKzd+TIEfbs2UOXLl2wtLQkOTkZExMTbty4gaWlJefOnaNx48ZotVqGDx+Op6cn8H+D6xkr8CxbtoxGjRphY2PzSdqacRBS9+96enoSGxvL/v37KV269Du3y9hX37p1iwcPHgDg4OCAubm5DG4KkU0ynntPnjwhKSkJU1NTjIyMsLCwUNaLiYnB3d0dW1tboqKi3hvgefMzxfu9GVZ/33Hr1asXs2fPlgBPFjpz5gxNmzbFxcWFSZMm4ejoCMCyZcvw8vLCyMiIU6dOkSdPnr/8nDd/xkIIIYQQQgghhBAiM3lyIoQQWeDGjRsAbNiwgbS0NNRqNVqtlty5cxMYGIi3tzd79+5l4MCBHDlyBEB561uXsXwzuGNgYIC/vz9paWls3rz5rXWEEOJzouvrKlasiLe3N5aWlvj7+9OzZ08ePnyIra0tOXPmpGLFiqxcuVKpvjNlyhTS09MxNDRErVazdetWVqxYQa5cuXB3d8fGxga1Wv3R2vn7778TEhICgL6+vvLZWq2Wly9f8ttvv1GgQAGcnZ1JT09/a3uNRoNKpeLVq1cAFC5cmLJly1K2bFnMzc3RaDQy0C9ENsgYFpk8eTI//vgjpUuXpmzZsrRq1Yrly5cr6/bo0YNZs2Zx48YNvLy8WLt2LfD6PuzNezE5n/+eWq1WQh3r1q1j+PDhdOvWjQEDBnDw4EEePXqkrDtz5kx69uzJzp076du3L2fPnlXumcWncfPmTa5du0aHDh1wdHQkPT2dJUuW4O/vj5mZGSdOnCBPnjwkJyeTkpICkOn6p/vZSHBHCCGEEEIIIYQQ4q8ZZHcDhBDia9C2bVtsbGwoVaoUhoaGHD9+nG+//RY/Pz9SU1NJTk7G3Nyc0aNHAxAWFkaFChUyDQC9fPkSU1NT1Go1Bgavu29jY2NMTEx4/PhxtuyXEEJ8LCqVSgkm6unp8ejRI06ePMnWrVvJkycPgYGBFChQAIAaNWqwatUqWrVqRUhICGq1mr59+7J582Y8PT3R09OjUaNG5MyZE/g4g+darZaUlBTat2/P/fv30dfXJzAwEH19faWagKmpKfb29pw6dYpnz56RO3fud06DmJSURGxsLL169cLIyCjTvyODm0JkPa1Wq/QTvr6+TJw4kRIlSvDDDz/w+PFjfv/9d37//XcuXbrE4MGDgdcBHgB3d3cGDBhASkoKbdu2lSD1P5QxsOjn50dUVJQSAAGIjY2lRYsWBAYGUqRIEeB1gAdg9uzZ+Pj4MH78eL755pusb/wX6F0Vj+7cuQOgVNxZsWIFAQEB6OnpcejQIfLlywe8Dvl07dqVNWvWKN8DeblACCGEEEIIIYQQ4kPJ6IAQQnwiV65cUR52A3z33XdYWFgwcuRIKlSowMKFC9HT08PExITcuXPj4eHBkCFD2L59O0FBQRw+fFjZdvXq1TRo0IArV64oD9STkpLYvHkzqamp2NvbZ/XuCSHER6XRaJRg4u7duzE1NSUqKoqff/6Z6OhoQkNDuXfvnrJ+9erVWbVqFSqVin79+lGuXDl69OiBRqNh27ZtSnDnY1GpVJiYmLBr1y4KFSrEsGHDlAo8enp6SlU1e3t7EhMTGT58OOnp6ejp6aHRaDJNpRMaGkpAQAAXLlz4qG0UQvw7unBBTEwMUVFRDBgwgK1bt/Lrr7+ya9cuFixYQKFChRg6dCjjx49XtuvRowcxMTFcvnyZiIgIkpOTs2sXPlu6fnHkyJGMHz+e7t27c/DgQa5du8b06dNxdHQkJiaGfv36KZUs4XWAx8PDg+3btzNixIh3VjoT/0zGENvo0aMJCwtDo9GQN29eAFauXMnChQszBXfy58+vbB8eHs6pU6cy/f0jhBBCCCGEEEIIIT6cVN4RQohP4OTJk7i6utK3b18GDhyItbW1sszOzo78+fPTuXNn9PX1+emnnwAoWLAgHh4eAIwaNQq1Wk2fPn1ISkpiwoQJnDp1KtObsKmpqYwfP54ffviBgQMHZu0OCiHER6YbwPXx8SEuLo5x48bx888/4+fnh0ajYerUqQAMGzZMqcBTvXp14uPj6datG69evaJChQpMnz4dW1tbpYrPx5Seno6TkxM7d+6kWrVqSngnODgYQ0NDpX0bNmxg/vz52Nvb07dvX6UdWq2WuLg4Nm7cSO3atSV4KUQ2SEtLU87XjDQaDb/99huWlpZ069YNOzs7tFotKpWKjh07YmlpSceOHRk0aBAuLi40aNAAgG7dupEjRw6qVKmCiYlJVu/OF+HMmTPMmTOHGjVqMGjQIKVv7NmzJ02aNKFHjx78+uuvODo6EhYWhqGhIQYGBkybNo1cuXLRu3fvj97ff410IbaRI0cSFBSEt7c3T58+pWHDhpQvX56pU6diZmaGsbExJ0+exNzcHHh9bVuwYAGbN2+mbdu2FCtWLDt3QwghhBBCCCGEEOKzpdLK5PBCCPHRnTt3jq5du3LmzBl8fHzw9PSkYMGCyvK4uDi8vb25e/cuixYtUgI8AImJicTExDBq1ChevnyJoaEhhQoV4vfff8fe3j5TOfsrV67g4OAAkGlqFiGE+Fxk7LvWrVtHz549admyJf7+/kr/dvr0aUaPHs3SpUvp06ePEuDRDay/evVK+ZwcOXK8c9qPj0X32QkJCVSrVo379+8THBxMcHCwss6vv/6Ku7s7T548oX379vTu3ZucOXOycuVKYmNjUavV7N69G1tbW2UfhBCf3p49e9i5cyfdu3enUKFCmZY9e/aMSpUqkStXLqX6oe5PZd05OmPGDHr37o23tzeTJk16Kwj0KUKDX4Ndu3ZRt25dRowYgZ+fH0CmvvH8+fO0a9eOp0+fsn//fqytreXYf0QZr5mPHj2iSZMmFC9enLCwMGWqsmXLljFkyBCuXLnC1KlT+eWXX5TtY2JiGDt2LPr6+mzfvp1ChQrJtU0IIYQQQgghhBDiX5BRXiGE+Mi0Wi3Ozs7Mnz8fV1dXxo0bx5QpUzKVkG/Tpg2RkZFYW1vToUMHlixZoiyzsrLCy8uLdevW0b17d4YOHcrevXvfCu5otVoJ7gghPmsZp5LSarWkpKSQI0cOfHx8cHBwUAbOy5Qpw+DBg2nfvj1Tp05VptDSDQzmyJEDMzMzcuTIkWnaj09BX18ftVpNsWLF2Lt3L/nz5yckJESpwgPQoEEDFi1ahI2NDQsXLuT777+nXLlyjB49mrx58xIfH4+trS1qtVoGN4XIIs+fP2fEiBEMGzaMffv2ZVqm62uMjIz4448/OHDgAPA6tKNSqdBqtWi1Wpo1a0aBAgXYs2cParX6rbCIhEf+ncTERNLT07l79y7wujpSxr7R3t6e+vXrc+3aNbZu3QrwVvUkOfb/nu6aOWvWLM6cOUNiYiKdO3emSJEiyrlRv359+vTpg7W1NYGBgTRu3JjQ0FAaNmzIwIEDUavVbNq0iUKFCsm1TQghhBBCCCGEEOJfkidcQgjxkfzxxx+4uLgoD6udnJyIjo6mb9++jBs3DiBTBZ62bdsCr6eI6dChA4BSgcfMzIzatWtTs2ZNZeDozUoSGR+KS3BHCPE50vVjvr6+TJo0iSZNmtCoUSNKliypLNe9va8L8ABMnToVfX19hgwZgpWV1Ts/81PS19cnPT1dCfDoptDSarUMHz4cExMT6tWrx5EjR1i6dClXr15Fo9FQuXJl6tatS548eT5pdSAhxNty5sxJYGAg1atXp27dusDrQE/OnDlRqVTkypWL9u3bExgYyLp16yhdujS5cuUCUII6+fLlw8DAACsrKzl/P6Ly5ctjY2PDjh07SE1NxcjISOkj1Wo1xsbGNGjQgPHjx6NWq7O7uV+MjNehrVu34uHhQb58+dDX18fGxkZZT6vVYmlpibu7Oy4uLkRERLB9+3Y2b95MiRIlcHNzY9iwYUpwR84NIYQQQgghhBBCiH9HwjtCCPER/Pnnn3zzzTe4uLhw6tQp5fvOzs7/KsCjp6f3VjUdeRAuhPhSaTQajIyM2LhxIzVq1ODZs2fKgHrGMI4uwGNoaEhUVBQ5c+ZkxIgRnzSw877KZgYGBm8FeEJDQwEYPnw4AHny5KFPnz7v/Ezp04XIOroQYPXq1alatSp6enoMHTqU9PR0BgwYoIQAGzZsyJo1a5g4cSJ2dna0bt1aCeyo1WoWLlzI/fv3KVeu3FtTaol/r2DBgnz//fcsXryYLl26sHjxYvT19ZWpsTQaDb/99hv6+vpK1Unxv8l4HTp48CD169dn0KBBLF68mFu3brFnzx5KlCiR6fpnbm5OgwYNaNCgATdu3OD58+c4OjoCZApcCSGEEEIIIYQQQoh/R0o1CCHER5A7d26qVq1KoUKFeP78ufJ9jUajBHhq1Kjxzim02rZtm2kKrTlz5gBSTUcI8eXTDX5PnDgRX19fzMzMOHHiBMePH1cqjr2pTJky9OvXD09PTzw8PD7pwLlarVb64n379jF//nyioqLYtWsX8DrAo9VqM02hFRoammkKrdTU1Lf2V/p3IbKWSqVCo9EAr8+/W7dusXXrViZMmMDs2bOV6ZrKly+Pl5cXBQsWpF+/fgwZMoT169fz6NEjpkyZwrhx47C1tcXLy+utcKH4d7RaLaampowfP57ixYuzbNkymjVrRmJiotJXrlq1ilWrVlGxYkXKlSuXvQ3+QuiOrZ+fH9999x2bN29myJAhtG/fHlNTUyZNmsTFixff2k53Xba1tcXZ2RkjIyOMjIw++ZSVQgghhBBCCCGEEF8DlVY3iiCEEOJf0VVlePLkCYaGhpiZmTFnzhy6d+8O/N/b3ufOnaNv377s3r2bQYMGZarAA7By5Urat2+PhYUF169fJ0eOHDIoJIT4ouj6w4wyvqkfFBTEyJEjKVCgAPv378fBweG9b/Knp6cr1TA+xYBhxoo7QUFBREVF8fTpU2W5j48PvXv3pkSJEsr3EhISqFatGvfv3yc0NJTAwMCP3i4hxN97s2KWbiomgBcvXmBmZsaRI0cIDg5m+/btDB06lB49elCoUCEAli1bxuzZs9m+fbvyGSqVCmdnZzZs2IC9vb1UGfmIdMfy1q1bNG7cmNOnT1OwYEEcHR3RarWcPHkSCwsL9uzZQ5EiRd5bEU38vYzHbsuWLXTs2BE3Nzd++eUXXFxcePz4MWPGjGH8+PGUL1+elStXYmtrm82tFkIIIYQQQgghhPg6SHhHCCE+gowD0osWLaJTp07Uq1ePLVu2ZFr+dwGe9evX8+2331K4cOFs2Q8hhPhUMg50p6Sk8PDhQ8zNzTE2NsbQ0FBZTxfgsbKyYu/evRQtWjTLB8kz9umDBw8mPDycli1b4uHhQb58+Zg0aRKxsbF069aN/v374+LiomybkJBAzZo1uXPnDuPHj6d///5Z1m4hRGZTp07lxx9/VMIHvXv3xtDQkPDwcExMTDh8+DBBQUHs3LnzrQDPnTt32LFjBwcPHkSlUlGuXDmaNm1K/vz5Jbjzgd4V2Hwf3TG9f/8+EyZMYM+ePRw7dowSJUpQrlw5RowYQeHCheXY/w8y/jzu3r3L3LlzWbBgAevXr8fR0VE5troAT0REBBUqVJAAjxBCCCGEEEIIIUQWkfCOEEJ8ZLdu3cLb25vVq1fToEEDNm3aBLw/wNO3b1+sra0zfYYMTAghviQZ+7To6GhWrlzJnj17KFSoEI6OjoSHh1OyZEnMzMyAdwd4dJV2slJsbCy+vr64ubnh7e1N8eLFSUlJoWzZsly5coW0tDTat2/PkCFDMgV4zp07R/v27Vm7di12dnZZ2mYhxGsDBw5kwoQJ9O/fn3HjxhEQEEBERAQ+Pj4EBweTO3dugLcCPD179swUrH4zgCJVXz5MxuP04MED8uXL97fb6K4V6enpqFQqEhISsLW1RV9fH2NjY7k//kgCAwNZv349hQoVokiRIkybNk05trrfdwnwCCGEEEIIIYQQQmQ9eeoohBAfkVqtpnDhwkyZMoXWrVuzZcsWGjVqBLyebkGr1eLs7Ex0dDTff/89kyZNYsyYMdy7dy/T58jAhBDiS6HVapU+bcCAAfTr14/ExEQaN25Mvnz52LlzJ40aNWLOnDlKXxgWFkZgYCCJiYnUqlWLixcvZnlw5+HDh6xcuRIHBwd69uxJ8eLFefr0KeXKlSMpKYkxY8bQsWNHli5dysSJEzl16pSyrbOzM0ePHsXOzg61Wp2l7RZCvObj48MPP/zAxIkTKV++PBEREQQFBdG/f39y586N7h2WSpUqERYWRq1atRg5ciSzZ88mMTHxvZ8rwZ2/p1arleM0bdo0mjZtSlRUFBqN5i+3010rDAwM0NfXp0SJEpiammJsbJzpWiL+vZcvX2JoaEhCQgKbN2/m/PnzbwV3tFotlpaWBAQEMHDgQE6dOkXt2rW5fft2djdfCCGEEEIIIYQQ4osmTx6FEOJfetcAhG5QwdramsmTJ/9tgMfZ2Zm1a9dibGycpW0XQoisoqtYMWPGDKKioujbty8bNmzg119/5cCBA4wdO5a8efMSGBjIr7/+qoRdQkNDCQ4O5ubNm7i5uaFWq8nKgpHGxsY8fvyYPn36ULZsWV69ekXDhg15+PAhY8eOpX///nh5eZEjRw7mzp3LpEmTOHHihLK9buBaBpuFyHparRZbW1s2b95M/vz5OXv2LN9++y0tW7bEzs4OjUaj3JPB2wGemJgY7ty5A/DB0z6J1zQajdLvBQQEEBAQwPPnz7G0tPzHwaeMx15+Dh+Hqakpffr0UarbnThxgpUrV2Y6JzIGeAYPHkyPHj1ITU2V4JoQQgghhBBCCCHEJybTZgkhxD+k1WozDUzs27ePO3fukJqaSsGCBalVq5ay7u3bt/Hx8WHlypXvnEIrISEBc3NzChQo8Na0DEII8SVp3rw5J0+eZNu2bRQvXlyZBis9PZ1Vq1YxaNAgUlJS2Lt3L46Ojsp2ERERtG3bliJFinyytr1vGpxnz55hZmaGSqUiJCSEcePGERQUhLe3N6ampgA0btyY69evc/bsWTw8PIiKisryKkFCiHdbv349zZs3p0CBAiQmJtK/f3/8/PywsrLKFFLQ3X8dOXKE4OBgNm3axMSJE/Hy8pLAwr80bNgwRo4ciYeHB/369aNEiRLvXE/ufz+dvzq2iYmJLFq0iOHDh+Pi4kJERATffffdWwEelUpFUlISWq2W3Llzy7RxQgghhBBCCCGEEJ+QhHeEEOIDHDlyhLt379K0adNM3x8yZAiTJ0/m5cuXyvd+/PFHwsLCKFGiBCYmJty5cwcfHx/i4uKoX78+mzdvBjI/UJcH4UKIL9mjR48oVqwYLi4u7Nq1Swnu6PqAz8pEAAEAAElEQVTB9PR0AgMDCQ8Px8PDg2nTppGWloahoaHyGbptPjbddCEAGzZs4NSpU/Tr148cOXJkWq9BgwYkJCRw4sQJcuXKpbTJ0dGRli1bYmVlRYcOHbCzs/vobRRC/DuXLl0iISEBa2trfH192bFjB3379iUwMFAJTmu12kz3YAcOHGDy5MmMHTsWW1vbbGz952vnzp389NNP1K5dmxEjRlC0aFFl2YULF1Cr1ZiamiqhTLkP/vgyXtueP3/O8+fPSU9Px8bGRlknMTGR2NhYgoODqVixIqNHj1YCPDoZ/16RoJUQQgghhBBCCCHEpyWvBQshxN+4e/cutWvXxtraGpVKRZMmTYDXU7qMGzeOZs2a0ahRIwwMDJg6dSrr1q3j2rVrhIeHU6tWLQoWLMjkyZMBiIuLo0qVKhw4cCDTw28ZsBBCfCneNdBnYWGBvb09V65c4c6dOxQsWFBZptFoMDAwwMfHh3nz5nH9+nWATMEd4JMEdzJWUQsKCmL27NkkJiZSsmRJWrRooezDq1evuHjxIqampqSlpSnbx8bGolKpaNOmDdWrVwcyD5gKIbLOu4IFjo6O2NjYYGxszKJFi3BzcyM6OhqVSsXQoUMpUKCAss2ZM2coXLgwVapUoUKFChgaGsr5/C8lJCRw7949WrZsSdGiRVGr1Tx+/JjJkycza9YsHj9+jIuLC7169aJXr15yH/yRZfy9nTJlCr/++iunTp1CrVbTokULGjVqRKtWrbCysqJTp04ABAcHM3jwYEaPHk3VqlWVz5Kpy4QQQgghhBBCCCGyjjwlE0KIv2Fpacm4ceN4+PAhQUFBrFu3juTkZPbv30/nzp2ZOHEiPXv2pGvXrvz6668MHTqUy5cvM3ToUO7duweAtbU1kydPpl69evzxxx/cv38/m/dKCCE+Po1GowzuJScno1KplEHEsmXLcuvWLSIiIkhKSlKm5NDR19dHrVZn6eCgbsBYN2DZtGlTjh49qgR3dExNTWnXrh1nz55lxIgR7Nq1i4iICEaPHk2uXLkoWbJkpv0QQmStjH3HnTt3OH/+PH/88QcAxsbGAFhZWbFs2TJq1qxJVFQUI0eOJCkpCYB169bRvHlzIiIi0Gg0SnhQzud/59atW2i1Wm7dusXVq1eZPXs2zZo1Y8yYMTg6OtKmTRvOnz/PpEmTuHbtWnY394ui1WqV39sBAwbQr18/Ll68SKVKlTAzMyM2NpZffvmF0NBQACXAExISwpEjRwgKCmLnzp3ZuAdCCCGEEEIIIYQQXy+ZNksIIT5ASkoKixcvxsvLC2dnZ3r06MHAgQOJjY2lVatWaLVapYLDgwcPGDlyJJGRkXTp0oW5c+cqn3P//n1UKhX58uWTKQKEEF8sLy8vbt26RUxMDJaWlgDcuHGDBg0a8OjRIwIDA+nYsSO5c+dWtpk7dy59+/YlICCAoKCgLJueY/369XTo0IFWrVoRHByMvb39O9c7evQowcHBbNmyBbVaDUDJkiXZuHEjRYoUkT5diGySscpIREQECxcu5M8//8TQ0JCGDRsybNgwSpYsqQRyEhMTad++PfHx8TRt2pRSpUqxZs0a7t69y9GjR3F0dMzO3fmsvNlP6/rBixcv4ubmxpkzZzA3N+fx48c4ODgwadIkKlSogLW1NUOHDmX06NHs3r2batWqZeNefJlmzZrFL7/8Qv/+/enVqxclSpTg0qVL7N69m0GDBvH48WNGjBhBQEAAAA8ePCA2NhZfX19atWrF4sWLMTIyyua9EEIIIYQQQgghhPi6SHhHCCE+UHJyMosXL8bb2xtbW1tSUlJYtWoV5cqVIz09HQMDA2UQ49atW9SoUYPU1FSOHTtGgQIFMg3syiCvEOJL8mafVqxYMS5fvkyXLl2YMGEClpaWpKSksHbtWgYNGsSTJ0/48ccf8fX1JXfu3GzevJkJEyag1WrZvXs31tbWWdb2oKAgJkyYwI4dO3B1df3LdRMSEjh9+jT79u2jRIkSNGvWDCsrK5laR4hskjE8MnDgQCZOnEiZMmVo1KgR9+/fZ/ny5ZQtW5bBgwdTt25dJYyQlJREx44d2bBhA8bGxjg5OfHrr79SpEgR5Z5O/LWM/d6bxywlJYUTJ04wefJkkpOTKVeuHD4+PpibmyvrdOjQge3bt3Ps2DEKFSqU5e3/Umm1WlJSUmjXrh1Hjx5l9+7dFC1aNNM68fHxtGrVCktLSxYvXkzlypWB1y8ZrFq1isaNG2Nra5sdzRdCCCGEEEIIIYT4qkl4Rwgh/oHk5GSWLFmCr68vT548wdPTk6ioKOD/BpBSU1MxMjLCy8uLKVOmcPLkScqUKZPNLRdCiE8j4wBubGwsDx8+ZP/+/cTFxWFoaEj79u2ZOHEilpaWPH36lPj4eIKDgzlx4gQmJibA6/7T3t6eTZs2YW9vnyVhGI1Gg1arpVq1aly9epUzZ84oVYIyBpF0waT3VQKS4I4Q2W/SpEkEBgbSs2dP3N3dKV26NDdu3KBKlSrcuXOHsmXLMmLECOrVq5epmsiGDRvIkSMHZcuWJW/evHI+f6CMgc3o6Gj27t1LWloaTZo0oV69etjY2PzltitXrsTPz49KlSoxb948TE1Ns6rpX4T3XY90P5dHjx5Rrlw5rK2tOXTo0FvbaLVaJkyYwKBBgxgxYgRDhgx567PlXBBCCCGEEEIIIYTIevJKoRBC/H9vVo54V3UcExMT3NzcMDQ0xMfHhyVLllCtWjXat2+PSqUiJSUFY2NjAG7evEnevHmxsrLK0v0QQoispBvc8/X1Zc6cOdjZ2dG8eXMaNWrEn3/+yYIFC1CpVEoFnqZNm1KrVi0iIyO5fv06r169wtXVlXbt2lGgQIGPPmD4vkFOXf/u5OTEsWPHOH/+PFWrVs20ju468PjxY/z9/YmKilL6+Df3XwiRPc6ePcuiRYuoU6cOv/zyC87Ozjx9+pT69euj1Wrp0aMHcXFxBAYGotVqqVevnnIeN2nSRPkc3fSn4u/p+k8/Pz8iIiIwMTFBq9WyatUqGjZsyMSJE3Fycnpnfz516lQiIyPRaDRMmDABU1PTLJsm8UuQ8e+T3bt3Ex8fz+3bt4mKilKOtZmZGZaWlty7d49Hjx6RJ0+eTMdYpVJRq1YtAPbt20dycjJGRkbo6ekp68i5IIQQQgghhBBCCJH1ZM4WIYQg84PwZcuWcffu3UzBnYxFykxNTWndujXjx48nJSWFkSNHMmfOHABlMCguLo7du3dTpkwZzMzMsnBPhBAi6y1YsICJEyfSuXNn1qxZQ2hoKMuWLWPdunVUqFCB+fPnM2DAAB4/foxKpSJXrlwEBgYyc+ZMYmNj6du3rzK94McaMHz8+DHA3w4Ily1blvT0dCIiIrh+/bry/fT0dOU6MGPGDGbPns2+ffs+StuEEB/PtWvXuHDhAt7e3jg7O/PixQu+//57Hj16xPjx4wkNDaVr166cPHmSsWPH8ttvv5GamvrW58h0pv/Mjh07WLRoEX379iU+Pp4LFy7g5ubG5s2badOmDX/++Sf6+vpotVpSU1O5cOECNWrUICQkhFy5crFr1y5sbGxQq9US3PlAGf9ecXd3p127dowePZqzZ8+yY8cOZR0DAwO+//57rl+/zowZM4DXv99qtRq1Wg28vvblypULCwsLTExM5PdfCCGEEEIIIYQQ4j9AntAIIQT/N2Dz3Xff8dNPP+Hq6kpUVBRHjhwB/m/wV/fAO0eOHLRv357Jkydz7do1evXqRcuWLZk+fTqdOnUiMDCQnDlzMm/ePMzMzJAZCoUQn7uXL1++9T1d37Znzx709PTo0aMHDg4OaLVaTE1NKVWqFDt27KBcuXLMnz+f/v37K6Ga9PT0tz7vYw0ebtu2ja5du3Lq1Kn3rqNru5eXFw0aNGDTpk1Mnz6dS5cuAWBg8LpA5cqVK5k7dy516tShfPnyH6V9QoiPp1GjRixfvpy6deuSlpaGp6cnly9fZtiwYbRq1YqCBQvSsmVLVCoVR48epXv37uzevTu7m/3ZefNe9s6dO5iYmNC7d28qVaqEra0tS5YswdvbmzNnztC2bVv+/PNPZQqmY8eOYWBgQOfOndm0aRNFihSRqZn+gYzBndq1a7Ny5Urq16/P8ePH2bp1K/Xq1QNeX0f19fVp164dJiYmDB06lJkzZwKvq+no6+ujVquZN28ez58/p1y5ctm1S0IIIYQQQgghhBDiDSqtjCgLIQQAf/75J6VLl8bIyIhy5cpx6NAhcuXKhbu7O61bt8bV1fWtgeXk5GSWLVuGr68vjx49wsXFBSsrK2rUqEH37t2VN4plYEII8TmLj48nJCSEyZMn4+LikmmZRqOhefPmbN++nevXr5MvXz5leg5d/7d3715atGjBq1evaN26NZMnT8bCwuKTTJWya9cuateuTZ06dZg8eTIlS5b8oG38/Pw4fPgwlStXpk+fPhQoUICtW7eyYsUKAPbu3Yutre07p1QUQnx67zr3UlNTMTIyUr6+desW1atXx8XFhXXr1infv3DhArVq1aJTp05s376ddevWUbBgwSxr++cu471sUlISpqamjBs3jmPHjhEXFwdAWloahoaGAPTr14/JkydTqlQpli9fTqlSpXj58iVPnz4lT548GBkZSV/6L7Vr145t27YREhJCly5dsLCwUI7lm9fU5cuX0759ewD8/f1p0aIFzs7OLF68mKioKNLT09m9e7dM8SuEEEIIIYQQQgjxHyFPy4QQgtdvE5csWZKePXuSmpqKj48PS5YswcbGhgkTJlC3bl2aNm1KfHw8t2/fVrYzMTGhVatWTJw4kTx58vD8+XP8/PwYNmyYBHeEEF+EtLQ0duzYwc6dOxkwYAB//vlnpuV6enoUKlSI5ORktmzZkmnwUDdlSsmSJcmbNy8ajYbY2FhCQ0N5+fLlRw/uPHjwAH9/f8qUKUNQUNAHBXcAqlWrRlRUFK1bt+bgwYN06dKFRo0aMWvWLOzt7dm9eze2trao1WoZbBYiG2Q89x49esSNGzcAlOCO7n2U8+fPc+3atbeqiaxYsQJzc3P69OnDnj17KFiwoFJNUfy1jNMZjh8/nlatWlG3bl1+//137t69y4sXL9BoNBgaGirHdNKkSXh7e3P27Fl+/vlnTp48iampKdbW1srPTPrSf27u3Lls3ryZLl260LVrVyUEqzuWumuq7nxo164dcXFxFCxYkLFjx1K1alVsbGzw8vJCo9GwdetWrKys5FwQQgghhBBCCCGE+I+QyjtCCJHBwoUL6dy5M507d2bevHkkJCRw5swZRo4cyZEjRzAxMaFMmTJ4eXlRu3ZtChcuDLyeTmbOnDlERkaye/durK2ts3lPhBDi47lz5w4zZ85k9OjRVK1alSlTpmQKxuzevZvmzZvj4uLCihUrlLf4MwYY69SpQ6tWrYiNjeXChQusWLGCunXrftTqO5cuXaJixYo0atSIxYsXAzBgwABq167Njz/++EGfsXr1ah4+fMiDBw9wdXXl22+/JXfu3BLGFCKbZKzQMmrUKBYvXkxCQgLVq1enU6dOtGjRAgsLCwDu3btHlSpVyJMnD0uXLsXKyoq1a9cSGhpKsWLFWLVqFcbGxtm5O5+twYMHM3bsWPLnz0/OnDm5cuUK8Lq6S5s2bd6quAbg6+vLxIkTqV27Nlu2bEFfX/+jhza/Jh06dOC3337jyJEj2Nra/uX1M+N5c/z4cfbv38/27duxsLDAxcWFjh07UqBAAbm2CSGEEEIIIYQQQvyHSHhHCCEg08PvBg0acOrUKQ4ePIidnR3weoqAs2fPMnz4cLZt2wZAuXLlqF+/PgMHDsTU1BRTU1NevHiBmZmZTAUghPjiJCYmMnXqVOXt/YwBnvv37+Pn58f8+fNp0qQJkZGR2NjYYGRkhFqtJi4uDh8fH1auXImhoSFVqlTBw8ODadOmfdQ2JiUl8cMPP5CUlMT+/fsZNWoUkyZNYsiQIQQFBf3loP1f9dvSpwuR/XThkeLFi2NpaUlCQgIpKSl4enri5+dHnjx5ePbsGWPGjCEiIoICBQpgamrKzZs3sbKyYufOndjZ2X2S6fq+RBlDHSdOnKB169Y0bdoUDw8PSpUqxdixYwkJCSE5OZnNmzdTv379dwZ4hg8fTrdu3ShSpEh27s5n7+7du7i4uFC2bFm2b9/+Ua5Lcm0TQgghhBBCCCGE+G+RJzVCiK+KRqPJ9LWuTLxKpVJKzLdo0YLExETCwsLQarWkp6djYWFB7ty5OXToEEWKFKFZs2ZcvHiR8PBwihQpwogRI9BoNJiZmQEyFYAQ4stjZWVFnz598Pf3Z9++fXh6enL27Fm0Wi358+cnMDCQJk2asGHDBtzc3Bg1ahQHDx5k5MiRBAcHkz9/fpycnHB2dsbS0pLjx4/z8uXLt/rl/0WOHDnw8PDgzp07ODk5KcGdXr16/W21jb/qt6VPFyLrZZzK59KlSyxdupTevXvz22+/sW/fPjZu3Ejp0qUJDw9n1KhRPHjwgFy5cuHt7c3EiRNxcHDA1NSUVq1asXv3buzs7FCr1RLc+UC68M2xY8d4+vQpz58/p1u3bpQqVQoAf39/Jk+eTK5cuWjYsCFbt25V7qf19fWVn9/w4cMpUqQI6enp2bYvXwKNRkN6ejoGBgYfvM358+fp27ev8rPQ/a2ju+7KtU0IIYQQQgghhBDiv0Uq7wghvkqRkZH07NnznVVyHj9+TMWKFQH49ddfcXFx4Y8//qBmzZoYGxszZswYOnfuzIULF5g6dSqnT59m7ty5SpUeIYT4kr1ZgSc6OhpnZ2f09PS4cuUKkZGRrFy5klu3binblChRgg0bNuDo6MiFCxcoV64cbm5uzJ0796O1K2M1jUqVKnHy5EnMzc2Ji4ujVq1aUm1DiM/Unj17uHr1KoMGDWLr1q2UKVNGWXbz5k3atWvHgQMHGDBgAP7+/uTPn18539PS0lCpVBgYGMj0QB8o431xVFQUPj4+VKtWDTMzMzZv3gxAWloahoaGAMTExDBgwACePXumVOCRii7/3pvHTve7nJSURKVKlXj+/Dl79+7FwcHhvZ+h+13fu3cv9erVY+XKlTRq1Cgrmi+EEEIIIYQQQggh/gfyRE0I8dUZMmQI/fv3Z9CgQbx8+RI9PT3ljVS1Wo2lpSWenp5cuXKFo0eP8uDBAyW4M2rUKDp37gy8HoweO3YsGzduVN7mFkKIL5FWq1Xe2NdV4PHz82Pfvn307duXc+fOodFocHBwYMSIEezZs4cJEyYwbtw45s+fT3x8PI6Ojty+fZvJkyeTnJxM1apVP2obdcGctWvXcvToUSpUqMCLFy/o06cPf/75pwR3hPgMTZkyhe+//564uDi++eYbypQpg1qtVvojGxsbVqxYQZUqVZgwYQJjx47l4cOHSgUYQ0NDpVKJBHf+nlqtVoIjz58/p3r16lSpUoUjR45w6tQpzp07B4ChoaFy39ujRw8mTJiAubk5TZs2Zf369RLc+R/ojt2hQ4dITU1Vrl0WFha4urpy9+5dpk+fztOnT9+5va7yEUBERARWVla4urpmTeOFEEIIIYQQQgghxP9EKu8IIb46z54946effmLHjh1069aNcePGYWpqmmmdQ4cOUbt2bWXgx8TEhLFjx9K1a1fg7bdihRDiS/JmH/euihXvqsBTsmTJ94ZkLl68yPz58xk3bhzt2rUjNjYW4KNXxLl16xanTp3C0dGR1atXK1O2rF69mpIlS360f0cI8ekdOXKEvn37cujQISwsLDh69ChFixZ9a71bt27Rtm1bjh49Srdu3RgzZgy5c+fO+gZ/IXx8fNi8eTOHDx/m2LFjhISEEB8fz9ChQwkLC1PWy3htmDt3Lj169KBQoUJcunQJIyMjCU3+S507d2bt2rUsWbKEunXrKlWODh06hJubG1qtltGjR9OiRQty5MihXEcz/jwWLFjAoEGDaNeuHePHj8fIyCg7d0kIIYQQQgghhBBCfAAZeRZCfFXS09PJlSsXS5cu5YcffmDatGl4e3uj0WgyrVe5cmW8vLxITk6mQIECREVF0aVLF0CCO0KIL1vGygtLlizBy8uLatWqERISwo4dO5T1dBV4/P392b9/P56envz555/Kcl0+PD09nYMHD9KgQQMmTpxI+/btleCORqP5nwZ33+y7AQoXLkyDBg0oUaIEXl5eDB48mGvXrtGyZctM7RNC/PdVqFCB6dOnU69ePZKSkoiKiuL+/ftvrVe4cGHi4uJwcHBgw4YN2dDSz1vG93lmz57NnDlzqFSpEg8ePKBWrVoEBQVRpUoVRo4cSXh4uLKuvr6+UoGnW7duLFq0iAMHDmBsbCzBnX9Jq9XSqVMn8ufPT//+/dm+fTtpaWkAODs707lzZ+7du0dISAhLlizh8ePHyrHWBXeWL1/OyJEjyZ07N0OGDMHIyAh5Z0sIIYQQQgghhBDiv08q7wghvjq6t1KfP39OixYt8PLyonnz5spy3durW7dupV27duTJk4fjx49jYWHxzuoTQgjxpcgYTvTz8yMqKgpjY2Py58/PlStXKFCgAIGBgfTp00fZRleBJzw8nOrVqzN+/Hi++eabTJ+7a9cu1q5di729PX379n3r3/o3MvbH586d49GjRzx58gRHR0ecnJyU9V6+fMn48eMZNWqUVOAR4j/qrypwabVaTp48iaenJydOnCAwMJBevXqRN2/et9a9e/cuKpUKKyurj17V60uVsS9+8eIFgwYN4vLly8ycORM7Oztlvd9//50hQ4Zw8OBBxowZg5+fn7LszftjuV/+32i1Wvbs2YO7uzvPnj1j+vTpNGnSBD09PRITEwkPDycmJgY9PT1q1qzJgAEDsLCwQE9Pj8mTJ7NhwwYMDQ3ZuXMn9vb28vMQQgghhBBCCCGE+ExIeEcI8VXSPcT+u4fZjRo1YsuWLcyZM4cuXbqg1Wql6o4Q4osXEhLCiBEj6NKlC+7u7ri6urJs2TJ+/vlnjI2NGTVqFP369VPWT0xMZMaMGQwfPpzWrVuzZMkSDAwMMn3my5cvlSkK/9fgTsbtR40axbx587h8+TIajQZra2saNGjAtGnTMDExUf5tXYDH3t6e1atX4+zs/K//fSHEx5PxXuzJkyc8efIEQ0NDChcurKyjC/D07t2b06dPM2TIEDw8PN4Z4AGpkvhvBAYG8vLlS7Zu3Uq3bt3w9fVVqpvpjuXvv//O0KFDOXDgAKNHj8bf3z87m/xF02q17Ny5k06dOjF37lzq1aunBNLu37/PsmXLmDdvHseOHcu0Xa5cuahTpw5RUVHY2NhIcEcIIYQQQgghhBDiMyLhHSHEV+19b2XrHnRv27aNtm3bUqNGDdatW5cNLRRCiKy1YcMGPD09qVu3Ln5+fpQoUYKkpCRq1qzJjRs3SE5O5tWrV0RGRuLl5aVsd+fOHRYvXky7du2wtbXNkrYOGjSI8ePHU716dWrXrs2zZ8+Ii4vj5s2bfPfdd8TFxVGwYEEAXr16RUREBOPGjcPY2Jj9+/dTrFixLGmnEOLdMgYLJk6cyNKlSzl+/Dj58+fnhx9+YMKECeTLlw94O8AzdOjQ91bgEf/MrVu3aNWqFYcPH0ZfX5+xY8cyYMAAZXnG++WMAZ6goCBCQkKyq9lfPK1Wy6NHjzL9jut+Funp6bx8+ZLY2Fhu3rzJs2fPsLKyolGjRjg5OZErVy4J7gghhBBCCCGEEEJ8ZiS8I4T4avyb6RPu3LlDuXLlePLkCffv38fc3PwTtU4IIbJfWloagYGBzJgxg+3bt1OhQgWeP39O5cqVefz4MbNmzSI1NZU2bdoAMGHChEwVeHTVLrJiwHDNmjX89NNP9OzZk0GDBinTu9y+fRsvLy9Wr15NjRo12LFjh9KWV69eERISwooVK9izZ48S7BFCZL2M1XF8fX2JjIzE2dmZunXrcu3aNdauXUutWrWIiIigbNmy6OnpZQrwnDt3Dk9PT3x9fbG0tMzmvfn8HTp0iIkTJ7Js2TKqV6/OrFmzMk1BmPE+Oj4+Hg8PD548ecLFixfJlStXdjX7q/Hm3zF/93eNTBsnhBBCCCGEEEII8fmR8I4Q4quQ8QH2li1buHTpEr/88ssHTamwceNGSpcuTZEiReRBuBDii6bRaFi5ciXp6en89NNPJCcn8+OPP3LixAlGjx5Np06dMDY2pkuXLsTGxmJubo6vry9BQUFZ3tZhw4YxYsQI9u/fj6urK/A6fGRoaMj9+/fp2LEj27Ztw8/PjzFjxihBgeTkZFJTUzE3N5eqBEL8B4wfP55hw4bh7u6Ou7s7pUuX5sqVK3z33Xfcu3ePKlWqEB0dTbly5ZQAz6lTp2jXrh1arZYjR45IuPof0N3Lvuue9sCBA4wZM4Z169bh7++Pj48PVlZWb20LsG/fPhwdHbGyspL744/kXcdRpoATQgghhBBCCCGE+HpIeEcI8cV7s9T/gAEDOHnyJNevX8fGxuaDP0cGeYUQX4Pk5GRUKhXGxsYsXbqUrl274u3tTWhoKCYmJgAEBASwePFi7ty5g5WVFefOnSNnzpxZ1katVsvPP//M6tWruXPnDpaWlsoAp+6/V65coXz58pQtW5bt27ejr6+f6Xogg81CZJ3nz59jZmb21jl34sQJunXrhqOjIyNGjMDZ2ZmkpCS+++47Hj9+TI0aNVizZg2urq5MmjSJcuXKKefyn3/+Sd68eSU88g9kvJd99eoVycnJmJqaYmhoqAREDh48SEhICNu3bycwMJBevXq9N8ADEi75WN4MRj18+JAff/wxm1slhBBCCCGEEEIIIbKSPGUTQnzRMj4I37FjB/7+/ly6dIl9+/ZhY2NDSkrKB3+WBHeEEF8DExMTjI2NATh9+jSpqan0799fCe4AnD17Fjc3Nw4ePMiBAwfImTMnWZ0HNzc3JzU1leXLl6PVapXBYz09PVJSUnBwcMDFxYWDBw9y/fp1NBpNpgFnGegXImvs3r2b77//noMHD77VT1y6dImTJ0/i6emJs7MzL1684Pvvv+fRo0dERkYSERFBixYt2Lt3L35+fpw8eVI5l0uVKoWVldVb57Z4t4zBnalTp9KqVSu++eYbKlasSEBAAPv37wfA1dWV4cOHU6dOHUaMGMHMmTNJTExUPufNYy3Bnf9dxr9Xtm/fTr9+/fDz8+PBgwdZfm0VQgghhBBCCCGEENlHnrQJIb5YbwZ3AgIC+OOPP9i2bRtVqlThyZMnREdHExISIg/GhRAiA61Wi0ajUQKOmzdvVpYtX76cY8eOkTt3bsqXL4+NjQ1qtTpLB89VKhXdu3fHwsKCZcuWkZCQoCxLT09XwkdqtRpnZ2dsbGxkgFmIbKBWqzl06BAnTpzA29ubI0eOZLrnat26NevXr6d27dqkpqbi7u7OtWvXCA4OplmzZtjZ2dGzZ08MDQ3Zu3cvbdq04cyZM5n+DTm3/55Wq1WCO76+vvTt25erV69SoUIF8ufPT0REBF26dGHJkiUAVK5cmbCwMOrUqcPIkSOZPXs2d+7cyc5d+GK9+ffKkCFDOH36NPPnzydfvnykp6dnWlcIIYQQQgghhBBCfLnkSacQ4ov0vuDO77//jqurK0lJSSxevJihQ4eydu1aeWNbCPFVUKvVH7SeSqVCT0+PNm3aADB48GCGDBlCnz59GDBgAMbGxnTv3l1ZPzsqkzk6OtK+fXt27txJUFAQJ06cQKPRYGBggEajYfny5Zw+fZpvv/0WjUaT5e0TQrzuG7p378748eO5cOECvXr1UgI8uiBC48aNAbh58yY7d+6kbt269OrVS6n2lTt3bszNzenYsSPGxsbkz58/2/bnc6W7z502bRpRUVH07duX9evXs2bNGnbs2EG3bt1ISEggMjJSCW1WrFiRsLAw6tatS1BQEMuWLZO+9CN7198rp0+fJj4+nsqVK5OUlMSSJUuIjY0FpGKcEEIIIYQQQgghxJfOILsbIIQQH9uHBHdiY2MZPHgwVatWZceOHW9tJ4QQXxqNRqOEbGbNmoW9vT0//PDDe6tWaLVaqlSpwooVK+jWrRtjxozBxMSEcuXKsWTJEgoWLJhpGpasli9fPjw9Pbl//z7Lly/n4sWL1K9fn+bNm7N+/XpWrFiBpaUlYWFhGBsbSx8vRDbQarVYWlrSrVs3NBoNYWFh9OrVi5kzZ1KxYsVM654/f567d+9Sq1atTP3KunXrsLOzIyQkBAsLC3LmzIlGo5GKO//Qixcv+PXXX3FwcMDDwwNHR0dSU1P59ddf2blzJ46OjmzYsAFjY2PS0tIwNDSkYsWKDBkyhNy5c9O6dWs55h/RX/29ogvuLFiwgKFDh9KoUSM6deqUzS0WQgghhBBCCCGEEJ+aSiu1l4UQX5B/EtwpX7488fHxwOtpVgwMJM8ohPjyDRs2jBEjRhAWFoaXlxfm5uZ/u83Fixe5fPkyOXLk4JtvviF37tyfLLjzTz/3zz//ZMGCBcTExPDgwQPg9TQ6FSpUYPny5RQpUiRbQ0ZCfO1092ZPnjwhJiaGsLAwHBwclACP7r7t4sWLVK1aFScnJ7Zu3YqBgQGrV68mKCiI0qVLs3z5cgwNDSWI9y/dvHmTcuXK0bJlS2bNmkV6ejorV67Ez88PPT09Dh06pFQ1OnPmDDly5KBo0aIASphH+tKP40P/XhkyZAjlypVj165db20nhBBCCCGEEEIIIb48Et4RQnyRtm/fTmBgICdPnpTgjhDiq5ZxsPXGjRv88MMP1KxZk4CAABwdHf92+3cNFmZF1Ys1a9ZQv359TE1N/3bd1NRU7t27x/bt20lLS6N48eKULVv2k4aMhBAf7kMCPE+fPqV///7MnTuXYsWKYWZmxqVLl8ibNy/x8fHY2dll9278Z2Xsp7VaLWq1+q1724sXL1K5cmXat2/PtGnTWLx4MYMHD34ruPPy5Utq165N586d6d27t1Tb+cjkRQMhhBBCCCGEEEII8T7y9EcI8cU5f/48vXv35vr16+zatUspPS8PwoUQXyNdcGXVqlUUKlQIrVZL7969Pyi4A7zzLf9PPZg7Z84cevbsybp162jSpMnfrm9gYICNjQ1dunTJ9P2MU4UJIbLOm6E/3f/nzp2bHj16AGSaQqt8+fKYm5szevRoChcuzKZNm3j27BmNGjVi/Pjx2NjYSBDvPTIe67t372Jtba3c286dO5dSpUrh6upKkSJFKFeuHCtXrqRMmTKMGzfureAOwPjx4zl79iyFChWS4M4noPtZbd++naFDh0pwRwghhBBCCCGEEEIo5GmcEOKz9FdFwxwcHGjQoAHbt2+X4I4QQgAzZsygTZs2SrhFNxXKf5WlpSUAS5cu5dWrV2g0mr9c/30DzDLwLETWU6vVSkDh8ePH3Lhxgzt37ijLc+fOTbdu3QgKCuLKlSv06tWLY8eOkZ6eToECBQgODmbPnj0cPnyYhQsXSnDnb+iOtaurK82aNePWrVsA9OnThx49epCQkEBaWhpGRkbUr1+fBw8e4Ofnh0aj4cqVK0pwR6vVsnTpUubOnUu1atWoXbt2tu3Tl+7o0aP4+vpy4sQJdu7cKcEdIYQQQgghhBBCCAFIeEcI8RnKOCiUlpaWaVl6ejpGRkZMnjyZatWq8fjxYxYsWEBgYKA8CBdCfLWaNWtGlSpVuHTpEk+ePOH27dvA677wv6hly5a0bNmSzZs3c//+ffT09P4ytCmE+G/IWO1qzJgx/PDDDzg7O1O6dGnatm3L1q1befbsGXny5FECPFevXlUCPLrtjY2NyZUrF4aGhgAS3PkAuXLl4siRI3h5edG1a1emT59Ov379qFmzpnIcBw8eTNOmTXn58iU5c+bk4cOHPHnyBK1Wy/jx4wkMDESr1TJ79mxy5879t8FJ8e88efKEfPnysXPnTnnRQAghhBBCCCGEEEIoVFoZCRFCfEY0Go1SSWH27NnEx8ejVqspXbo0AQEB6OvrZ5o+YOfOndSpU4dq1aqxe/duQB6ECyG+Lro+7969e7Rr145du3ZRq1Yttm/fjkql+s9VtND14dOnT6dPnz706dOHyMjI/1QbhRB/LSAggPDwcJydnalYsSJHjx4lISGBAgUK8Msvv9CnTx8sLS15/Pgxc+bMYcSIERQrVozIyEiqVq2a3c3/rGS8N+7evTvz5s0DoGfPnkyaNAlTU1Mg8/1v27ZtWblyJSqVCltbW54/f05SUhKlS5dmzZo12Nvb/+euDZ+TDzl2jx8/xtLSkidPnhAbG0tgYCDlypWT4I4QQgghhBBCCCHEV0zCO0KIz9LAgQOZMGECKpVKqcZQpUoVVqxYQeHChZX1kpOTmTJlCr6+voA8CBdCfNkyDuK+y71793BzcyM+Pp727dsTGxuLvr5+tgzSvu/f1H3/5cuXuLq6otFoiI+PJ1++fH+7f0KI7JHxfD527BiNGjWiU6dO9O7dG0dHR27dusXGjRsZN24cd+/eJTg4mN69e2NqakpSUhJz585lwIAB1KtXj/Xr1yuVYsSH0R1/XWgKoHbt2ixatAhra2tlecb74AULFrB//37OnTtH4cKF+f7772nZsiX58+eX4M7/IOOxi4uL4/Lly6SlpVG/fn3Kli2LkZGRElJNT09n7dq1tG/fHldXV3nRQAghhBBCCCGEEOIrJ+EdIcRnIeOA7cqVK3F3d+fnn3+me/fu5M2bl+DgYBYsWMC3335LXFwcDg4OmSrwgDwIF0J82TIOGJ46dYrExESSkpKwsbGhSpUqynqJiYm0bduWPXv20L59exYuXIienl62DdaOHj0aFxcXSpUqhaOjY6Zl0dHReHt7M3HiRHx8fLK8bUKIf+bAgQMkJSXRt29fNm7cSPHixZV7uJSUFHbt2kWvXr0wMjJizZo1lCxZEnhdhWTFihU0btwYGxubbN6Lz9OrV6+YN28eiYmJHD16lA0bNtCwYUOio6MpWrSocl+clpaWKRz1/PlzcubMqXwtIcmPw8/Pj4iICOXrQoUK0blzZ4KCgsiRI4dynFevXs327duJjo4G5O8VIYQQQgghhBBCiK+ZhHeEEP95b4Zw5s+fz8iRI1m/fj0lSpQAICkpibFjxzJu3DjKlCnDqlWrsLe3z6YWCyFE1so42BoaGkpMTAw3btwAQE9Pj6ZNmxIZGUnhwoUxMDAgMTGRdu3asXv37mwN8Pz666+0bNkSAAcHB3755RdatGiBo6Mjenp6HD9+nOrVq1OqVCk2bNhA/vz5M10PhBD/HcOHDyc0NJTKlSuTkpLC8ePH3wqCvHz5ksjISIYOHcqAAQOIiIhQ7vN0/5WqL/9ecnIyJiYmALi5ubFixQoaNWpEdHQ0Dg4OSjBEq9WSlJRE7ty5lW3fvN8W/0zG4zdlyhQGDRpE69atadq0Kc+fP2fs2LEkJCTQp08fwsPDlenMMpLgjhBCCCGEEEIIIcTXTV6pE0L85+kehPv4+GBtbc3WrVtp06aNEtxRq9VYWFgQEBCAn58fp0+fplWrVly5ciU7my2EEFlGNzju7+/P8OHDKVWqFOHh4YwdOxYnJyfWrl1Lq1atOHbsGBqNBisrK1asWEGNGjVYunQpP/74IxqN5pMPmGs0mkxfN2/enEOHDjF69GhevXqFv78/tWvXpkuXLpw/f55vv/2WUaNGcezYMc6ePZtpqkQhRPZ681xs3rw5OXPm5NChQzx//pxHjx4poUAdU1NT2rRpg7GxMX/++Sfp6enKfZ7uvxLc+fdMTEyU471s2TLatWvHpk2b6Nu3LwkJCRgYGKBWq9mwYQNhYWEcP35c2VaCO/+eRqPJdPyuXbtGzZo1CQ0Nxc3NjR49erB//35cXFyYOnUqgwYN4uXLlwCZzg8J7gghhBBCCCGEEEJ83aTyjhDis5Cenk63bt1YtGgR+vr6tGzZkiVLlgCvB3l0b3Y/ffqUsWPHEh4ezrfffsvixYspVqxYNrdeCCE+vY0bN+Lm5sbPP//M0KFDsbOzA15PkzVixAimT59O+fLl2bp1KxYWFgDcu3ePH374gVu3bnHx4kXy5s37ydqXsZrGhg0buHz5Mo0bN1amyjp//jxnzpwhPDycQ4cOkTNnTr777juKFStGTEwMNWvWZNmyZZkqRQghskfGijpJSUlKn3L27Fm+//57Hj16hIeHB9OmTQNen/8qlQo9PT1SU1Oxs7OjfPnyrF+/XqZo+gQyVnBp3749y5cvp3bt2kRGRnL27FmGDRvG7du3SUhIoECBAtnc2i/H4MGDefDgAb/99hve3t70798fQJmq7NmzZ1SrVo0//viD3r17M27cOExNTaXalBBCCCGEEEIIIYQAQF7tEkL852m1WgwMDJgxYwa5cuVi7ty5HD58mBs3bmBvb68MUGg0GszNzfH390dfX58RI0bQt29fNmzYIA/EhRBfvD/++IMXL17Qvn17JbiTnp6OlZUVwcHBPH/+nPnz5+Pr68vs2bPRaDQUKFCA33//HbVaTd68ed+a4uZjyVjVZ+jQocyaNYvnz59TsmRJbGxsMDY2xsnJCScnJxo2bMiePXtYvnw5cXFxbN++HY1Gw+XLl7lx4wa5c+f+ZO0UQnwY3fnn7u5OWloa8+bNQ61WU6pUKXbv3k316tWZMWMG1tbWBAcHK+e/RqNhyZIl3Lt3DxcXF6n28okYGBgo98dLly7F0NCQRYsWUaFCBfT19SlYsCAnTpygQIEC0p9+JE+ePGHRokXcv38fS0tLJRSVnp6OoaEh6enp5MqVi71791KtWjWmTZuGvr4+o0ePxszMLJtbL4QQQgghhBBCCCH+C6TyjhDiP0er1b41mKN7I/Xly5cMHDiQ6dOnU6pUKfbt24e5uXmmAI+enh5PnjxhxowZ/PTTT8ogthBCfMl69erF7NmzuXz5Mvb29kq/qetTb9++TaVKlbCwsGD//v1YWFhkGrTNigHcIUOGMHbsWHr16kW3bt2oXLlypuVvtmHPnj2cPn2ayZMnc/78+UyVPIQQ2Uer1fL48WOsrKxo164dixYtQqvVKkG9M2fOUKNGDZ48eUKHDh3o3bs31tbWrF69mnnz5pGUlMSBAwcoVKhQdu/KZ+ld98rvkrECz5gxY7h06RJGRkYMHTqUQoUKZVou/ndXr16lY8eO7Nu3j1q1arFmzRrMzc2Va5vueD979oyaNWty4sQJBg8ezMiRI7O76UIIIYQQQgghhBDiP0DCO0KI/5SMZeM1Gg2PHj3CwsICQ0NDZZ2XL18yaNAgpk2bRunSpdm7d+87Azzv+kwhhPhSDRkyhDFjxjB8+HCGDRuWaVlKSgrGxsa4ubmxYsUKzpw5g7Ozc5ZWvdiwYQPt27enXbt2DBs2jCJFirx33Tf78StXrtCwYUOeP3/Ozp07KV68eFY0WQjxHmq1Gj09PcqWLYtarebAgQOYmpqir6+v3HdlnEIrf/78aLVabGxssLCwYN68eRQpUkTu0T7Q+47Th4R43txW17/Ksf/fabVatFptpuN59epVfv75Zw4cOMCgQYMYMmRIprCs7u+Vp0+f0rp1a2bNmoW9vX1274oQQgghhBBCCCGE+A+Q+thCiP+MjIMI06dPp2nTpjg4OPDtt9/Srl07EhISSE5OxtTUlHHjxtG7d2/OnDlDtWrVePr0qTJFwJuVI2RgQgjxNejSpQv58uVjxYoV7N69G10+OzU1FWNjYwAePXpEsWLFsLOzy/Lpag4cOEBycjKenp5/GdyB11Py6Nqv0WhwcHDA39+fO3fucOLEiSxorRAiI41Gk+n/9fX1UalUlClThjt37qBSqZT7LV2ARzeFlqWlJU+ePKF+/frs3buXHTt2SHDnH8h4nBYtWsTQoUPp06cPu3bt+qB+XFeBTUd3nyzH/t/JeC6oVCrUajXwf8fT3t6exYsX8+233zJhwgTGjh1LUlISenp6aDQa5e8Vc3Nztm3bpkwBLIQQQgghhBBCCCGEhHeEEP8JWq1Weeg9YMAAvLy8OH/+PJUrVyY5OZm4uDgaNGhAXFwcT548eSvAU7NmTZKSkqT0vxDiq6TVarG3t8fHx4cLFy4QGhrK5s2bATAyMgIgLi6OY8eOUb58+SwdtNVoNKSnp7N7925MTU2xt7fnXYUfdQOgz549A1AGpXX/LViwIACXL1/OimYLIf6/jJWwdFVGdBwdHXny5Alnz55Vluvu6dLT0ylZsiTx8fHkyJGDxYsXExUVleXBwc+drr/28/OjU6dOjBkzhunTp1OrVi0CAwO5du3a336GHPOPQ1dxCmD58uV4e3tTs2ZN3N3diYmJUa5f9vb2rFy5km+++Ybw8PB3Bngykr9fhBBCCCGEEEIIIQRIeEcI8R+hG1SYMmUKkydPxtPTk61bt7J9+3YOHTpEQEAAKSkp+Pn5sW3bNjQaDaampkRERODl5cXJkydp3bp1Nu+FEEJ8PPv27WP9+vUf9Ea+SqXC2NiYn3/+mV9++YU9e/bg7u5Oz549Wbt2Lb6+vgQEBCj9pomJyTsDNJ+Cnp4eBgYGFC1alOTkZO7du5epWgH8XyWP5ORkxo8fz/Xr1zPt29OnTzl8+DAAVlZWWdJuIcRrurCCh4cHxYoVo06dOgwePJiYmBhMTEwAOHfuHPD6fNXd0+lCJy4uLuzevRsLCwsCAgIYM2aMslxmcH6/jMdmwYIFTJkyhc6dO7Nr1y7mzJlD/fr1GTNmDOPGjePKlSvZ2NKvQ8YXDQYOHEjHjh2JiYnhjz/+YM6cObi7u9OsWTMePHgAvA7wrFq1inLlyhEeHs64ceN4/PjxWxVChRBCCCGEEEIIIYTQUWnliakQIptkfJMbID09ncaNG3Pt2jU2bdpE0aJFSU9Px8DAgBcvXrBkyRICAgLInz8/u3fvJl++fAC8fPmSkSNH4u7ujr29fTbtjRBCfDyJiYl899133Lt3j+XLl1O/fv0PfjP/xo0brF+/ntGjR3Pz5k0AjI2NqVChAosWLcry6Wp0fX1ERAR+fn40a9aMZcuWYWxsjFqtRqVSKdeCoKAgJkyYwLZt26hataryGX/88QfffPMNzZo1Y82aNVnSbiHE/7l+/To//fQTKpWKK1eucO/evUwBPFtbW7777jvy589PxYoVKVKkCAULFiRnzpzkzZsXExMTpVLi48eP8ff3Z9SoUdm4R/8tWq02U3WcjPfIaWlp+Pr6cvLkSebOnUvRokWB1/1iREQEsbGxeHh4MHDgQGWZ+HTGjh2rTF32yy+/kC9fPi5evIifnx/79++nVKlSxMfHkzdvXgCuXbuGm5sbhw4dYtSoUfj7+0slJCGEEEIIIYQQQgjxThLeEUJkqX379nHo0CG8vLzeGjhOTEzE3t6eOnXqsGHDBiW4oxvQeP78Of379ycmJobBgwczcuRIZR2dN78WQojPUVpaGgsWLGDs2LE8f/6cmTNn0rBhw3/Uvz169IiDBw+SlJREiRIlcHR0xMLC4pMFd94MZL7p6dOnVKtWjTNnzuDl5UV4eDjGxsbK8ri4OIYNG4aNjQ0rVqzAwsIi0/bbtm2jXr16H/RvCSE+Ht19mO4e6/bt29y/f5/ExETWrFnDggULSE9Px8LCgvv372fatn79+qxevRpjY2P09PQ4c+YMZcqUoWPHjixYsCCb9ui/5/nz5+TMmfOtvs3f359nz55x+fJlGjdujLe3N2lpaRgaGgJw/vx5Ro8eLQGeLHLz5k0aNmyIubk5ixYtwsHBQVmWlpbGzz//zMqVK2nWrBmLFy/G1NQUeD3do6enJzNmzMDOzi67mi+EEEIIIYQQQggh/uMkvCOEyDLPnz/H2dmZ27dvs27dOpo0aZJpeXJyMmXKlMHY2JiDBw9iZmamDBjp/nvu3DkqV65M8+bNiY2NzaY9EUKIT0fX36WlpbF06VKGDRtGSkrKPwrwvC+g86lCLxn/vT179nD58mWSkpIoU6YMtWrVUtY7d+4cjRs35urVq9SqVQsPDw+sra1Zv349cXFxaDQadu/ejZ2dndLWN9sswR0hPq2/O8cyLn/69CmVKlXC2dmZNWvWcP36dY4dO8bdu3c5ePAgwcHBODg4oNVqlenxbt26ReHChYG3K858jeLj4+nevTvz5s2jRo0ayvdv3bpFhw4d2LVrFwBDhw4lLCwMyPwzyBjg6dOnD97e3hQvXjzrd+QLcfr0aYyMjHBycnpr2YkTJyhfvjzDhg1j+PDhys9BF2xLSUmhatWqXLhwgfXr11OzZk1lHd11Misr3wkhhBBCCCGEEEKIz4uMfAghskzOnDlZtGgRPXv2pHr16sDrQRt4PfBrbGyMk5MTZ8+eZeLEiaSkpCjBHR1doCc1NTVb9kEIIT41Xb9naGhI+/btCQ0NxdjYmF69erF582bS09P/9jPeNzD4KUIvugF5gMDAQJo3b07Xrl3x8fGhSZMm/Pzzz0o/7uzszI4dO/juu+/YuXMnP/30E7Vr1yY6OhobGxt27dqFnZ0darVaaeubbZbgjhCfTsZzb/v27cycOZOAgABWrFjBjRs3Mq2r1WrRarWYmppy5swZNBoNRYoUoWXLlvTu3Zt58+bh4OBAeno6KpUKfX19tFqtEtzRaDRffXAHYMeOHVy5coUlS5ag0WiU7xcuXJjw8HA6dOiAvr4+R44c4erVqwBKsBHAycmJwYMH061bN6ZMmcLcuXMzTWkmPtzVq1epVKkSrVq14vbt228tf/78OQBnz57l1atXyrliYGBAenq6cq1+8eIFhw8fBv7vmqW7TkpwRwghhBBCCCGEEEK8j8wtI4TIUjVr1qRGjRro6ekxfPhwDA0NCQgIUB5kjxkzhuPHjzN37lyKFClC27ZtMTExUbbftGkT6enpuLq6ZtcuCCHEJ/dmgAdg2LBh9OrV619NofWpaLVaZWByyJAhjBkzhjZt2tChQwe+/fZbevXqxdKlS7l79y7bt29HpVJhb2/Ptm3biI+P58KFC6SlpVG2bFkqVapE7ty5pSqBENkkYxBv8ODBzJgxgydPnijVQmxtbVm4cKFSHUalUmFhYcG3337L8uXLuXHjBvb29srn6arqZOyrMoZ1JIj3WkhICKVKlaJ+/fro6elx8+ZNbGxsAKhcuTJeXl4kJyezcuVKoqOjGTp0KJaWlpkqkzk5OTFgwABy5crFL7/8In3ov2RsbEyXLl1ISkrC3Nz8reXlypXDycmJEydOcPHiRb755htlme73uVSpUgC8evUqaxothBBCCCGEEEIIIb4Y8sRUCJFldJUX9PT0uHHjBiNGjCA8PJwpU6Yobwg7OjoyfPhwkpKS8Pf3JyAggKtXr3L37l1iYmKIiIjAzs6ODh06ZOeuCCHEJ6dSqdBoNP9TBZ6saCPA/PnzmTNnDh4eHowaNYrmzZtjZ2fHtWvXMDc3Z+fOndSpU0e5DpiamtKoUSN8fHwYOHAg9erVI3fu3JnCA0KIrKULHwQHBzN27FiaNWvGli1buHPnDsHBwTx48ICaNWuyc+fOTNsVKlSI5ORkHj16lOn7UlXn7+nuf93c3LC0tGTQoEGULFmSQ4cOKetUrlwZf39/WrRowYQJExg7dqxyrDNW4ClVqpRynyyVd/6dggULMmbMGObNm0fOnDmJiYnhzJkzyvIcOXLw008/kZCQQFBQEElJScoyPT09tFotO3fuxNDQkJIlSwIgs5QLIYQQQgghhBBCiA8l4R0hxCeRsew/oEyBBfD06VNsbW3Zu3cvuXPnJjg4mMmTJ5Oenk6OHDlo3bo1kyZNwtjYmMmTJ1OhQgVKlSqFt7c3AFu2bMHKykoGJoQQX5Q3+034v8H0/1qAJ+Ng5P3791m0aBEODg54enpSrFgxnj17hrOzM0+ePGH06NHUq1eP+Ph46tevr2ybkpLy1udJJQ4hstfhw4eZPn067dq1IygoiHr16pEvXz7Kli2LoaEh1tbWSrUR3XlbpkwZNBqNMk2Q+HBvhhXVajUvXrygQ4cOmY5nxYoVGTp0KM2bNyc8PJzw8PBMAR7dz0KmZvrfWVpaYmJiwpYtW3B3d8fHx4cLFy4Ar49rp06daNy4MevWrcPNzY0dO3bw8uVLAJYvX05sbCwuLi7UqVMHkBCbEEIIIYQQQgghhPhwKq28CiaE+IRWrFhB27Ztla8DAwPR19fH19cXc3NzDh8+TMuWLXnx4gVBQUF4e3tjYGCARqPhzp07TJgwgWvXrqHRaHB1daVr165KcEcGJoQQX4qMfdqRI0e4ffs2z54949tvv1Wm4ABIS0tj6dKlDBs2jJSUlGyZQuvN/ler1dKhQwdq166Nu7s7L1++5IcffiAhIYHw8HC6devGo0ePcHJy4uHDh9SsWZMdO3bIgKYQ/0FLliyhQ4cObN26lbp165Kens7y5csZMmQIKpWKI0eOkDdvXl69eoVarSZnzpxs3LgRT09Pdu/erUz3JP4Z3RRj8HoarZCQEOzt7Vm2bBmVKlVS1jt69CgjRozg119/JSAgAF9fX/LmzZtdzf6s6aYc00lNTcXIyEj5+saNG0ydOpXIyEiqV6/O5MmTKVGiBHp6epw/f56hQ4eyfv16VCoVRYsWxdDQkISEBPLly8fOnTuxt7d/698QQgghhBBCCCGEEOKvyJMkIcQn4+bmhpubG5GRkQAMGDCAUaNGYWBgoAxQVKpUiVWrVmFmZkZYWJhSgUdPT4/ChQszfvx44uLiWLVqFf7+/hLcEUJ8cTJOFTV8+HB+/PFHWrRoQadOnahfvz4BAQFKVYV3VeDZunVrllXg0Wq1SltDQkIYMWIEKpWKKVOm4O7ujlarZfz48Zw8eZKBAwcqUxyam5tTokQJnJ2diY+Pp02bNlnSXiHEP3Pjxg0AnJycAIiLi2Pw4MGoVCoOHTqkBEUSExPx9vbm3r17VKlShePHj2NjYyNVEf8llUqlHLvg4GCCgoK4evUqbm5umSrwVKhQgcDAQFq3bs2YMWOYMWOGTMv0L2i1WiVUc+rUKQAluBMWFsaJEyewtbXF29ub/v37Ex8fj7e3NxcuXECj0eDk5MTEiROZNWsW1apV49WrV+TIkYPu3buzb98+7O3tUavVEtwRQgghhBBCCCGEEP9I1r2mLYT46nTq1Il9+/bRv39/4uLi2Lt3LwEBAXTu3JlcuXIp61WuXJlVq1bRqlUrwsLCUKlUeHt7o6+vT3p6ulJRQvdWsgR3hBBfiowDiAEBAYSHh9O4cWPatGlDqVKl6NKlC+Hh4dy8eZPY2FhUKpUS4IHXg4wtWrRg48aN1K1b95O3Vxe8HDFiBGFhYXTt2pX79++TP39+4HVVnj179mBtbY2Xl5cyGGpgYMCLFy9o2rQpDRs2xMvL65O3VQjxz+nuz/bu3YuBgQH+/v7o6elx6NAh5TwHGDRoECdOnODVq1cUKVIEyBzuE/+cvr6+ElAPCQkBXvfxbm5umSrwVKhQAV9fXywsLOjYsaNUMfsXdMesYcOG3Lx5k5kzZ1K1alUGDBjApEmTMDIyonTp0hQsWJC+ffsCEBERgbe3N5MnT8bJyQlbW1s6depEx44duXfvHhYWFhgYGGBgYCAvGgghhBBCCCGEEEKIf0WmzRJCfBK6oM3JkyepXr06ycnJVK9enfXr12NmZvbOh9qHDh2iVatWvHjxguHDh+Pp6ZmlU8EIIUR2mTVrFoMHD6Z9+/b4+PhQvHhxAEqWLMn169d59eoVHTt2ZP78+cqgY1paGvPmzWPOnDnExcVRuHDhT9a+jH32gwcPaN26NY6OjgwfPhw7OztlvWfPnlGnTh0eP37M6dOnyZEjB2q1mjlz5jBkyBA2btyoDEBnDGcKIT6te/fukS9fvr+tBHLz5k2qV6+ORqNBq9ViaGjIwYMHleCOVqslJiaGkJAQWrZsybhx4zA2Ns6KXfhqZOxvg4ODCQsLe+cUWmlpaRgaGkpQ5F9KTk5mxIgRREREUKtWLczMzFi9ejUBAQF4eHgooTSAO3fuEB0dTUREBDVr1iQqKkqpTiWEEEIIIYQQQgghxMcidZyFEJ/UqVOnePHiBSYmJsTHxxMTEwO8frtYo9FkWrdy5cqsXr0aCwsL+vfvz9y5c7OjyUIIkaXu3r3LokWLcHFxoU+fPhQvXpxnz57h7OxMUlISY8eOpUSJEixcuJAuXbpkmkKrW7dubN++ncKFC3/S6Wp0A8Pz589n3759nD59Gjc3t0zBHQBTU1PKlCnD5cuX8fDw4MKFC0ycOJEJEyZgY2ODg4ODsq4Ed4TIGhs2bMDJyYk9e/b87bp58+alffv2PHnyhMTERBYsWJCp4s7ixYsJDw/H0tKSwYMHY2xsLNM2fWS6CjzwenpC3RRaHTt2ZN++fcp6hoaGyvrinzMxMSEoKIhJkyaxbds21qxZg5ubG/369aNIkSJKgA1QKvAMHDiQ+Ph4vLy8uHDhQjbvgRBCCCGEEEIIIYT40sioiRDik9BVhmjUqBFLlixBo9EwcOBA+vXrR0pKCoMGDUJPTw+NRoNKpVLWr1SpEgsXLsTHx4dGjRpl5y4IIUSW0Gq15MyZk/bt21Oq1P9j777DmjrfP46/wxJlKAgIKjKc1Lpn3XtrnVh3XXWguFgucKACCqKC4t4TxW3de+/VWtS6d1VUVBRI8vvDX06DaL8dAor367q+17fknBOek3CexNyf3M83vH79mtq1a/P06VOCg4Pp2rUr9evXp2zZsixZsgSNRsOiRYswMDBQluiAtC/g7tixg65du5IvXz7MzMxwc3MDQKPRYGBgoCyZEx4ezsWLF1myZAlLliwBoGDBgqxduxYbGxtlfyFE2jt69CgdOnTAzc1NCXt8jFarJWvWrHh5eXH37l2WLl1K3759qVWrFiVLlmTLli3s27cPU1NTduzYgYODg3R9+Rf+zhz4/hJaBgYGjB49WgmP/K/nUvw9WbJk4dGjR0rH0Fu3bnH9+nXs7OyU1zUd/SW0pkyZQseOHVm+fDn58+fPqOELIYQQQgghhBBCiExGls0SQnwyHytG6D4Q37VrF506deLBgweEhITg5eWVYr+rV69iaWmJnZ2dLAUghMiU9OdJ3dyo0Wi4fv06+fPnR61WK50Axo4dS//+/TExMeHRo0dUrlyZhIQE7t27h4eHB9OmTUvTserGp3P//n1mzJjBwoULuX37NpMnT061vKFuKazXr1+zaNEinjx5Qs6cOWnVqhW2trYypwuRTrRaLa9fv6ZLly6cOnWK+fPnU6NGDQCePXtGjhw5Pnicbo56+vQpUVFRrF69mrNnzwKQK1cuatWqRUhIiNLtS67n/+3998fvz61/9Tjqb5s0aRJt2rRJsZyT+G+Sk5OZM2cOz549Iz4+npCQEKpUqUJAQIByvWi1WrRarfIc3r9/n6CgIDZs2MCxY8ews7PLwDMQQgghhBBCCCGEEJmJhHeEEJ+EfnHh4sWL/PHHHxgbG5MjRw6+/fZbZT/9AE9QUBA+Pj4AbNy4kUmTJtGkSROGDBkiXRmEEJmO/jy5e/du7t27R/ny5SlUqJCyz6tXr6hfvz7Pnz/nzJkzKYIxpUqVonXr1pw9e5aJEyfi7OycZmP9WBjz/v37zJw5kylTpuDs7MycOXMoU6ZMin0+VoiWjjtCpC+1Wk3t2rW5c+cOV69eBcDT05ObN2+yePFiLC0tP3ic7lrVLd106tQp1Go1hQoVwszMDFNTUwnu/E36j9Pq1as5fvw4p06dolKlSpQtW5bvv/8e+DP4+L/u43/tK/6aLjilH6BKTExU/ubDw8MZNmwY1apVIyAggOrVq6c4Pj4+HgsLCx48eEDWrFnJnj27vLYJIYQQQgghhBBCiE9GPvUTQvxnuqVSAEaOHMmMGTN4+vQp8K4dvaenJ/369SNPnjzUrl2bxYsX07lzZ/z8/Pjjjz+wtbVl7ty53Lt3j8WLF8sH4EKITEej0Sjz5KhRo5gxYwbm5uZERkZSoEABZd6Lj4/n3r172NjYKLclJyczc+ZMbt26hbu7O8OHD1duT6sCru53e3p6otFoiIiIAN4tG9K7d2+0Wi0TJ07Ey8uLyMhIvvnmG+XY9wv6uiKpzO1CpK/k5GTy5s3L/v37GTt2LPfv3ycqKgpfX1+Sk5M/epzuWjUwMEClUlG+fPkU2/Xf94mP05/3vb29iYyMJEuWLFhZWXHo0CG0Wi0eHh6EhYX95Vz+/mMtwZ1/Rz8Edf/+fRITE9FqteTJkwdTU1MAevbsiUqlYtiwYYwaNQp/f39q1qwJwNq1a5k1axaTJk2iaNGiACk68gghhBBCCCGEEEII8V9J5x0hxCfj7+/PuHHjqFu3LjVr1iQhIYGZM2fy8OFDGjduzKhRoyhdujQqlYp9+/bRv39/Ll68iKGhIUWKFGHjxo04OzvLt7mFEJmWr68vkyZNolOnTvz0009UqlQpxfakpCQaN27Mzp078fPzw9PTk9WrVzN9+nQsLCzYunUrVlZW6TLWO3fuUK1aNW7cuIGfnx/jx49Xtj18+JDp06cTEhJCxYoVUwV4hBAZSxeae/r0KeXLl+fatWsADBkyhOHDh3902Szx6Y0fP56RI0fSs2dPevfuTcmSJTl69CidO3fm6tWrDB06lHHjxmX0MDM1/e44wcHBLF26lDt37qBSqahduzaNGzemS5cuwLtl5WbPns2wYcOoUKEC/fr1IykpiZCQEH755Rdu3bpF3rx5M/J0hBBCCCGEEEIIIUQmJeEdIcS/ph+yef78OfXr16dUqVIMGzYMR0dHAM6cOcO0adNYtGgR33//PQsXLsTc3ByAS5cucfHiRV69ekXjxo2xtbWV4I4QItOKiYmhc+fOdOrUiaFDh5IvX74U23XFxevXr1O/fn2uXr2KoaEharWa/Pnzs2PHDpydndN1iY7jx4/j6enJ8ePH8fHxISgoSNn2foBn+vTpuLm5pcu4hBB/X5cuXVi8eDEAffv2VTppyfJLae/GjRvUqVMHFxcXIiMjKVSoEG/fvmXfvn107tyZ7Nmzc/DgQWxtbZVj9Jd0Ep+Wr68vEydOpHDhwpQoUYKbN29y7NgxgBQh1efPn7NgwQK8vb1JTk7GxMSEvHnzsnPnTvmigRBCCCGEEEIIIYRIMxLeEUL8Z0uWLOHVq1cMHjyYTZs2UbNmTTQaDSqVCpVKxZUrVxg2bBhr1qxh/Pjx+Pn5ffB+0rMgLYQQ6W3QoEHMmDGDw4cPU7p06Q/uoyvaPn/+nHHjxpGUlISdnR3dunUjV65caVYw1C8W694a6n4+fvw4Hh4enDp16qMBnsmTJ+Pq6kp0dDQFCxb85OMTQvw7u3fv5qeffqJWrVps3ryZ+/fv4+vrS2BgoBIOlBDCf3P9+nVy5Mjxwa5o+/bto2bNmixZsoT27duTnJzM6tWr8fX1xcDAgBMnTmBjY0NSUhJ37tzBxcUlA84g89L/+z516hStW7emTZs29O3bF2dnZxISEti9ezft27cnPj6egIAAAgIClOP37NnDqlWryJMnD927d8fBwUGuGSGEEEIIIYQQQgiRZiS8I4T4Ww4dOkRcXBxNmjRJcfu2bdto2LAh5cuXJz4+nh07dpA7d+5UH2wfPXqUWrVqUahQIQ4ePEi2bNkkqCOEyFT27dvH2bNnGTBgQKptCQkJ1KlTh3v37nH9+nUgdXcFXYDxxYsXWFpaKj/r9kurguGHgpPv/67jx4/Tr18/Tp48mWoJrUePHhESEsLatWs5cuQIdnZ2n3yMQoh/R6vVsn//fqpVq0ZcXBwlS5bkzp07+Pr6Mn78+DSdW74GFy9epHjx4gwcOBB/f39lOTLdvB0TE0Pr1q1Zu3YtTZo0YdWqVQwdOhSVSsXx48eVjjtxcXF0796dIUOGULly5Qw8oy/fhzoXXbhwgcePH9OqVSv27NlDiRIlgD9f/w4ePEitWrVQqVSsXbuWRo0apXoNhtSvjUIIIYQQQgghhBBCfEpSORdC/E+PHj2iffv2NGvWjFOnTqXYVq5cOYYNG8bJkye5dOkS0dHRABgaGirdG9RqNRUrVqRBgwbExsYSHx8vwR0hRKby7NkzvL29GTRoEJs3b061XaVSYWhoyIMHD7hw4UKq7boiYUJCApMnT+bWrVup5sm0Khjqfo9u/LrfpVarlX3Kly9PREQEhQsXJigoKEVnAjs7O/z8/Dh9+jR2dnZoNJo0GacQ4q+9f+3pQgfVq1dHpVJhbW3NoUOHyJs3L8HBwQwbNgytVpvqehd/X0JCArVq1SIiIoKQkBDi4uKAPzuX6TrpHDp0iK1bt+Ln55cquAPvlmzav3+/srSs+OfOnz/Pr7/+miq4M3XqVEqUKMGoUaMoXry4EtzRarVKOKdKlSrMnz+fpKQkDh8+DPz5HOrfnwR3hBBCCCGEEEIIIURakuq5EOJ/srOzw9vbm59++on8+fOn2GZtbc2gQYMYM2YMAFFRUezduxd492F3YmKi8kF3XFwcuXLlwtLSMl3HL4QQaS1HjhwMGzYMb29vKlWqlGKbVqvF1NSUpk2b8vbtW9atWweQ4pv8ugDNsGHDWLJkiVIA1t8vLd25c4eZM2cyZcoUJZjzoQDPjBkzABg7dizDhg1TttnY2JA9e3alGCqESF/688iRI0dYtGgREydOJCYmhocPHyr7OTo6cvjwYQnwfCLlypUjKCiIhg0bEhQUxMSJE1PM387OztSqVYtJkybRtWtXjI2NOXbsWIrgzoIFC9i+fTv169eXZQf/pStXrlCyZEm+//577ty5k2Kbi4sL5cuX5/jx45w4cYJDhw4BKcM5arWa7777DktLS37++Wdev36NNCgWQgghhBBCCCGEEOnNKKMHIIT4vOm6QfTr14+kpCSMjY0ZOXIkJUqUoHXr1gDkzJmT3r17k5iYyJgxY5gwYQJv3ryhQYMGmJiYABAdHc3p06epXr26fGtVCJGp6LpbNG/enKZNm2JoaIiPj48S6NEVCL/77jvy5MlDQEAAefLkoVu3bsCf3+SPiYlh8+bNFClSBFdX1zQd8/tLZeXNm5cdO3bQqVMnxo4di1arZcyYMUpB39DQkOTkZGrUqEGVKlW4f/8+QUFBWFhYMHToUOV+0iNoJIRISaPRKPPIiBEjiIyM5Pnz58r2ihUr0rlzZ3r37g28u94PHz5MpUqVCA4OxtDQULnexd+nm/vLli3L8OHDMTQ0JCgoCJVKxeDBg8mZMydWVlb07NmTU6dO8fjxY4YNG5ZiacH58+czfvx4TE1NCQkJIVu2bB9c9kn8tYIFC9KsWTNev36tLF2m07RpU4yMjAgKCuLAgQOsXbuW8uXLY2xsDEBycjJGRka4urpiaWmJnZ0d2bJly4CzEEIIIYQQQgghhBBfOwnvCCH+koGBgfKhtrGxMWfPnlUKPVmzZqVx48bAuw48AwYMQK1WM27cOM6cOcOPP/5IxYoV2b9/P9u2bSN79uxERkZiamoqhQkhRKah+9a+oaEhhoaGxMbGEh0dzc2bNzE3N8fT0xOAKlWqMGHCBLp3706PHj24ePEi1apVo3Tp0sydO5clS5aQlJTE9OnTsbCwSLN5UjdWeNetwNTUFHt7eypUqMDSpUv54YcfCAwMRKvVMnbsWAwNDXn79i1ZsmQB3i0RVr9+fa5du8YPP/zwyccnhPhndEG8ESNGMGHCBH744Qc6duzI69ev2bVrF4sXL2bIkCE8e/YMPz8/4M8AT/Xq1Rk/fjzm5ubKNvH36Iem3NzcaNy4MQ8fPmTChAlkzZqVXr16YWtrS9u2bXn69Cm+vr4MHjyYjRs34urqym+//cb58+extbXl559/Jk+ePCnmZ/H36B6zdevWkZCQQNasWZk1axYVKlRQlshq2LAhKpWKN2/eEBYWRq5cuejVqxeWlpYYGRmRnJzM0qVLuXPnDt9//z3JyckYGhrKv1WEEEIIIYQQQgghRLpSaaUftBDiL+gXEe7evUuePHlYunQpI0aM4I8//mDFihU0adJE2T8uLo6pU6cyevRoAPLnz4+9vT3FixfHx8cHJycnKUwIIb5o73et+dCctnXrVkaPHs2xY8cIDQ1l0KBByrZVq1Yxfvx4zp8/r9ymUqkoU6YM0dHRaTpP6t/vpEmTmDNnDi4uLkyfPh0nJycMDAw4ceIEP/zwA9evX2fo0KGMGzdOOX7WrFlMmDCBQ4cO4eDggEqlUgKeQoiMc+DAAZo3b07t2rWZOHEiTk5OALx69Yrdu3fTqVMnDAwMmDp1Kh07dlSu25s3b9KmTRtl7hF/j/7rwIgRI9ixYwcXL17E2dmZS5cuKbd7enpiY2MDwIYNG1i/fj3r168nMTERFxcXatWqhY+PDw4ODvL++D/Qfx1avXo17u7uVK1alRkzZvDNN98o+23fvp3hw4dz6tQpunfvTvXq1WnQoAGzZ89mxYoVxMXFcfToUXLnzp1RpyKEEEIIIYQQQgghvmIS3hFC/C39+/fn0qVLhIeH8+2337Jw4UJGjhzJ06dPUwV4njx5QlRUFCNHjqRBgwaMGDGCSpUqAShLbwkhxJduxYoVVKlShbx58wLQpUsXABYuXAjAjh07GD58OCdPnkwV4Ll8+TJXrlzhyJEjZM2alVKlSvHdd99hZWWVZgVc/U4+Xl5eREREULt2bbp3707Lli1T7HPixAnatWvHtWvXaN++Pe7u7pw/f54FCxaQPXt2du7ciZWV1ScfoxDi31m4cCFdu3Zl/fr1NG3aNFXIcPny5fTs2ZP69euzZs0a4M/3ZLp9JYj3z40aNYoxY8bQp08f3N3dKVy4MNHR0cyYMYPffvuNoUOHMnDgQGxtbZVjHj58yKtXr8idOzfGxsYplicU/9yHHrvu3bszf/58qlWrRmRkJEWLFlW2bd++HX9/f06cOIFWq6VkyZLExcXx7bffMm3aNJydneX5EEIIIYQQQgghhBAZQj6dFUJ8kH6Rd+7cucydO5d27dphYWEBvCtSq1QqRowYwQ8//JAiwJMzZ0769OlDQkIC48ePx8jIiOHDh1OhQgWMjY1lySwhxBfPx8eHSZMmERISwpAhQ/Dy8mLx4sX07NmTp0+fYm1tTd26dQEYPnw4Q4YMAVACPIUKFaJQoULK0oM6+suwfGq6eXf69OlMnTqVvn37MnDgQJydnVPso9VqKVeuHKtXr2bgwIFER0ezbNkyAFxdXVmzZg1WVlapwgFCiPSnuw513V5evHgBvHsfp69WrVpUqFCBtWvXcubMGUqVKqWEqXVzgwR3/pnY2FhmzpxJtWrV8PPzw9HREXgXeC9evDgTJkxgwoQJGBkZ0a9fPyXAkytXrlT3JUGRf0er1SqP3U8//QS86xA3d+5cjI2NmTVrFh4eHikCPPXq1UOj0RAcHMz+/fv57rvvCAoKwtDQkGzZskmITQghhBBCCCGEEEJkGPlUSgiRyvsF2atXr1KyZEmGDh2aYjmXzp07A3wwwGNtba0Uq8ePH49KpWL48OGUL19egjtCiC9eo0aNOH/+PKNGjWLt2rUcOXKEoUOH4uHhgbW1tTKP/lWAB0gVZkzLMIxWq+XJkyesXLkSZ2dn+vbtmyK4o6ML8JQsWZLFixfz22+/cezYMRwdHWnYsCF2dnbSlUCIz4RuzqhWrRoTJ07k2LFjdOjQAUNDQ2Ue0mq15MqVi2rVqrFnzx7i4+NT3Ie8L/t34uLiePjwIT/++COOjo5oNBolTFK9enXevn3LmTNnGDt2LEZGRsrrg/h0dH+7oaGhLFiwgBo1anD79m0cHR2JiooC+GCAp0GDBmi1WhISEpgxYwZubm7069cPrVYrwR0hhBBCCCGEEEIIkWHkkykhRCq6QtDAgQO5evUqcXFxtGzZkgIFCgCkKAi9H+BZuXKl0knCysoKLy8vDAwMCAwM5PHjxyxevBhXV9eMOTEhhPhEatSogYODAzVr1uTYsWNUrVoVd3d3cufODfwZgFGpVKkCPIaGhnh6eir7pReVSsXTp085c+YMbdu2pVChQn+5v0ajwdHREUdHR+Uc4MNLlAghMlb+/PkpVKgQERERlC5dmh9//DHVUli///47VlZW2NvbZ/BoMwdTU1NUKhUvX74E3s2xurCUSqWiXr16dOvWjeDgYMaMGcOjR48IDAwke/bsGTzyL5/+61BSUhJHjx6ldevWjBs3DkdHR+Xv/q8CPA0bNsTIyIhhw4bh6emJRqNRXpuFEEIIIYQQQgghhMgIstaBEOKDnj9/zsaNG9m6dSsXLlzg2bNnwLsPyOFdwEej0QDQuXNnAgMDyZUrF02bNmXnzp3K/eTIkYPBgwfTr18/XF1dJbgjhPji6ZajOXr0KC9evMDBwYHjx4+zc+dOHj16BLwr4uoCPAB169Zl3LhxVKxYkYEDBzJnzpwMGfvLly95+/Ytr169AiA5OTnFdt28/vz5c86ePZtq6R2Q5V2E+BwVLlyY0aNHA9CjRw9mzpwJ/LkU1rp169izZw8VKlQgT548GTbOzEKr1WJpaUmuXLmYPXs2Bw4cSBHGTExMBKBixYrkz59fWbJMfBq616HQ0FBCQ0PZunUrjRo1wsXFBXj3d69WqwGIiorip59+Yv/+/Xh4ePDrr78q96N7bS5XrhwDBw5k3Lhx6X8yQgghhBBCCCGEEEL8P5X2Q1UZIcRX5/2lsgBu3LhBu3btOHbsGJUqVVIKE/rfdtU/bubMmcydO5c1a9bg6OiY4r5ev35NtmzZgNTLxAghxJfo0qVLnDlzBktLS6ZNm8b+/fvx9/enR48e2NraAqm71Kxfv55Zs2Yxffp0nJyc0mxs+nOz/hju3btH7dq1efr0KUePHsXFxSXF0jq6ublRo0ZYWloyd+5czMzM0mycQoj/Tv/anTdvHj169ADeLQ1UpEgRHj16xK5duzA0NOTw4cPky5dP3ov9TR96f6wvKCiIYcOGUb9+fcaPH0+pUqVSzLleXl4cPXqU8PBwnJ2dsbGxkcf+E/nll18oVqwYuXPnxsDAgFWrVlGxYsUU3ab0n4vevXsza9YsihUrRnR0dIruczt27KBnz55UrlyZpUuXZsj5CCGEEEIIIYQQQggh4R0hRIoPtq9evUqOHDmwtrbGwMCA27dv4+7uzrFjx2jbti3Lly9PdYx+YUMX0vnYsipSsBBCfIk+Nnfp5r+jR48yYsQIDh06lCrAo9VqOXfuHCVLlgTgzZs3mJqapigwfkr68++WLVu4ceMG5cuXp2zZsgB4enoSERFBw4YNmT17Nrlz5yYxMRETExM0Gg2rVq3C19eXpk2bEhYWhomJyScfoxDi7/m7y9TpvxfbsGEDkZGRnDx5kri4OBwcHChVqhTTp08nX758svTd36T/OF27do379++j1WrJmTMnbm5uyj5du3ZlyZIlVK5cGX9/f2WZwVWrVjFq1CiqVq1KVFQUKpXqf4aBxMe9/zr85s0bfv75Z7y8vLh+/To9evQgKioqVRhV/3ls374969at48aNG9jZ2aXY7+LFi3z77bcf/F1CCCGEEEIIIYQQQqQHCe8I8ZXT/0B70qRJzJ49G3t7e6Kjo7G2tsbIyIi7d+/SunVrjh07xo8//si8efNSHSsfcgshMiv9ue7Zs2c8fvwYS0tLrKysMDY2Bt7NgcePH2f48OEcOnSIkSNH0rt3b6ytrdmyZQs//vgj7dq1Y8qUKWk6X+oXhkeOHMmMGTMwMzMjIiKC+vXrKwGdunXrsmfPHqpUqcL06dNxdXUlW7ZszJ49m9DQULRaLXv27CF37txpMk4hxD8TEBBAjRo1qFmz5kf30b/+Hz9+zPPnz7l8+TIFChTAwcEBc3NzCe78TfqPZWBgIHPnzuXmzZvKdh8fH3744QdKlizJo0ePGD58OHPnzgWgatWqvHnzhosXL5IzZ04OHjxIvnz5MuQ8Mgv95+PRo0fY2tqiUql48+YN27dvp1+/frx48YLJkyfTuXNnDA0NPxrgiYuLw8rK6oNd597/XUIIIYQQQgghhBBCpCcJ7wjxFdP/sNrLy4uIiAjq1atH+/bt+eGHH4A/P+y+ffs2bdq04fjx4ykCPGnVOUIIIT4H+gW/sLAwli5dypkzZ3BwcKBs2bLMnTsXGxsbZf9jx44pHXg6deqElZUVGzdu5PHjxxw8eJCCBQumy7iHDh1KcHAw3bt3p0ePHlSoUAH4syj5+PFjOnTowI4dOzAxMcHR0ZGkpCRu376Nq6srO3bswNnZWQr9QnwG9u7dS61atfDx8SEoKOgvr0vde7sPhQQlaP3P+fj4EBoaSp06dahbty5Pnz5l1apVXLt2jTp16jBixAiqVasGQFRUFNHR0Zw5cwY7OzuKFi1KeHg4jo6OMpf+B/qPXVRUFOvWrcPBwYF58+ahUqlITExky5Yt9O7dGwsLC8aMGYO7u/tfBnjkWhBCCCGEEEIIIYQQnyMJ7wghiIyMZODAgfTr1w9PT09cXFxSbP9QgKd79+7Mnj07g0YshBBpT//b90OGDCE8PJzixYvToEEDbty4wcqVKylSpAjR0dEULVpUOe7UqVMEBwezevVqDA0NKVy4MJs2bcLZ2TldAo+bN2+mXbt2tG3blpEjR6bq+KBftAwJCeHEiROcPXuWggULUr58eXr37o29vb0Um4X4TGg0GipXrszjx485efIk2bNnz+ghfRViYmLo1KkTHTt2ZPjw4cpcevLkSRYtWsSMGTNo0KABkydPpkCBAgAkJSURHx+PhYUFGo2GLFmyyFz6H+i/Dnt7exMVFUXRokXp168fHTt2VPZLSkpi06ZN9O7dG0tLy48GeIQQQgghhBBCCCGE+JxJuwwhvmJarZYnT56wcuVKXF1d6devX6rgDoChoSEajQZHR0eio6Np164dc+fOxdLSktDQ0AwYuRBCpD1dwXDy5MnMnDmTfv360atXL7755hseP37Mvn37+O2332jevDlr1qyhePHiAJQpU4aVK1fy888/Y2BgQLly5ciZMydqtTpdOpUdP36cly9f0rNnzw8u1aJSqZSCqI+PD5B6GREpNgvx+dBqtdSqVYsJEyYwc+ZM5boVaev06dOoVCq6detGvnz5lHmxbNmy2NjY8PLlSxYsWECtWrUYNGgQAEZGRlhbWyv3odVqZS79D3Svw8HBwYSFhdG/f3969+5NkSJFUuxnbGxM06ZNAejduzf+/v4YGBjQqlUr6RAqhBBCCCGEEEIIIb4Yspi7EF8xlUrFkydPOHnyJNWqVSN//vx8rBmXgYEBSUlJODo6smTJEho3boynp2c6j1gIIdLX+fPnWbhwIY0aNaJ379588803PHv2jOrVq6PRaGjdujW///477u7uXLhwAfizs02jRo1o0KABOXPmRKPRpEsBNykpiVOnTpE9e3YlTPT+vK5WqzEwMOD58+fKbZaWlgBKdwIpNguR/j70HkwX/ujfvz/29vZs27ZN2U8aqKYNrVaLVqvl6NGjqFQq7OzsUu3j7OxM586dyZYtG5MnTyYuLg4gVYcX6fjy3129epU5c+ZQo0YNBg0alCq4o2NkZETTpk2Jiori9evX9O7dm82bN6fzaIUQQgghhBBCCCGE+PckvCPEVy4xMRGtVotarQbetafXp/s5Li6OW7duodVqcXZ2Zv369Tg5OZGcnJzuYxZCiPTy8OFDzp8/T7du3XBzc+PVq1dUr16dJ0+eMG3aNGbPnk2HDh24fPkybdu25cKFCx8s1uq6B6Q1IyMjTE1Nef78OadPnwZSFvh1QYCkpCTGjRvHr7/+CvwZ1pFCsxAZQ6PRKNdfUlKScrtKpSIpKQl7e3vatWvHnj17WLlypbJN/Hfvh6BUKhUqlYry5cvz6tUrDh06BKQONdaoUYM6deoQFxdHYmJiuo33a3Pr1i1+//132rZti5OT01+G1oyMjGjYsCGhoaHky5ePcuXKpeNIhRBCCCGEEEIIIYT4byS8I8RX4v1QDrwrVpibm2Ntbc26deu4cOFCisKEVqtVCs5t2rTBy8tLKU7obpdW9EKIzKxu3brs37+fBg0akJSURK9evbh16xb+/v40btyY7Nmz06dPH3LkyMHt27epVKkSly5d+uTj+NAc/v7tuo4/uqVDFi1aBLybr3UhTV2xf+TIkSxbtixF9x0hRMbRva/y9vamVatWnD59midPngDvlgQCaNSoEQBr164lKSnpo/OC+Pv058Xr169z8+ZNZZsu+BEYGMjRo0eV23XhKo1Gw9OnT8mdO7fSvUx8erquRrrr4H26LyA8ePAAjUaDqakprVq14ujRo+TOnVvZLoQQQgghhBBCCCHE507CO0J8BXRLpADs3buXgwcPAu++Wezi4sIPP/zAs2fPCAwM5PfffwfeFSZUKhUajYbly5dz7do18uTJI9/yFkJkSroiuP43+nUFv0qVKgFw9+5ddu/eTc2aNenevTvZsmUDIGvWrKjVapo1a4aDgwPZs2f/pGN7P0i5ePFiJSCku10X3AEoX748xYsXZ9asWYwcORJ4N9/rwpkxMTGsW7eOokWL8s0333zSsQoh/pp+4Eaj0aToYPjgwQO2b9/Opk2bqFKlCq1atWLRokVKeKF27dp06NCB9evXc+nSpXTr6JVZqdVqZV4MCwujefPmeHl5ceXKFQBatGjB4MGDuXz5MmPGjOHAgQPAnyGSmJgYLl26RIUKFSTM/gl8LIxmYWEBwJYtW/jjjz9S/FtE100OoFu3bgwdOhR49xxlzZoVkGUghRBCCCGEEEIIIcSXQz5lFCKT0y9MBAYGMm3aNF6+fMmZM2dwcXHB2NiYgIAAYmNjiY6O5sWLF4wdO5ZSpUoBMHfuXMLCwjA2NmbYsGGYmJhk5OkIIcQnpz9P3rt3j6xZs2JmZkaWLFmAPwMyN27c4MGDB9SpU0fZBu8KuAULFmT69OkYGhpibm6e4j7/K12hsmvXrqxZs4aYmBgKFSpE06ZN6d+/Pzlz5iRbtmxoNBoMDAxwc3Nj8uTJdOjQgXHjxnHlyhXq1KlDuXLlWLZsGatWrSI5OZnZs2eTPXt25TghRNrSD+LduHEDZ2dn5ecJEybg4ODAmTNn2L59O1u2bGHmzJns37+fUqVKUa9ePby9valRowarVq1i6tSpREREYGpqmpGn9MXSD30MGTKE6dOnU7FiRTp37kzBggWVQKS3tzdPnz5lwYIFHDt2jCFDhlCwYEHOnDnDihUrMDU1Zfz48R/tCiP+Hv3XzF9++YUsWbJQoEAB4F1orW7dumzfvp1du3bRsmVLTExMlGO0Wi2zZs3i7Nmz1KpVS17ThBBCCCGEEEIIIcQXS6X9q0XjhRBfNP1ODF5eXkybNg13d3c6d+5M3bp1U+xz5coVvL292bBhA4aGhhQpUoTXr19z8+ZNXFxc2LFjB87Ozp+0IC2EEBlNv8gXGBjIokWL0Gq15MmTh6CgIEqXLq2EFi9dukSlSpXIkycPu3btIleuXCxfvpxRo0ZRqFAh1qxZg4mJSYq591N59eoVbdq0YevWrTRt2pRz585x69YtHB0dKV++PMOGDcPV1TVF1599+/YxZswYDh06pCx5aGhoSIUKFVi6dClOTk4ypwuRAerVq8fOnTu5dOkShQsXxsPDgxkzZhAUFISXl5cyJx09epQTJ04wefJkbty4gaurKzVq1GDJkiUULVqUrVu3YmtrK2GF/2DatGkMGTKEvn37MmDAAFxcXFLt8+zZM6ZMmcLo0aOV28zNzfn2229Zvny5zKX/kf5jN2nSJBYtWoSpqSnr1q0jd+7caLVali5dipeXFyqVitGjR9OgQQPy5csHwPLlyxk7diympqZs3boVOzu7jDwdIYQQQgghhBBCCCH+NQnvCPEViIiIYPDgwfTp0wdPT0/y58//0X3Hjx/P4cOHuXjxIoUKFaJ8+fL069cPe3t7KUwIITItf39/AgMDKVCgAFZWVpw4cYKcOXMyevRo2rZtS86cOQH46aefmDNnDjY2NtjZ2fH777+TK1cu9u/fT758+dIkuKNz6tQpqlatyqBBg/Dy8mLt2rXMnj2bY8eOkS1bNmrXro27uzs//PADKpUKAwMDbt++zf379zl48CAmJiYUL16cEiVKkD17dpnThUhnupDN0KFDCQ0NxcLCggYNGrB8+XIGDRrEoEGDyJs3b6owzuPHj9m0aRPR0dHs3r2bpKQkNBoNo0aNwt/fPwPP6Mv2+PFjvv/+e549e8batWspVKjQX+5/5MgRHj16xO+//06pUqUoWbIkVlZWMpf+B/p/67oOSJUrV8bLy4sGDRoor6lJSUlEREQQHh7Ow4cPcXNzo2rVqly6dIlTp05hbm7OgQMHcHJykjCbEEIIIYQQQgghhPhiSXhHiEwuPj6eJk2aEB8fz5o1a1J8o3jt2rWcPn2abNmyUaFCBWrVqgW868YTHx+PpaWl8gG4FCaEEJnVgwcPaNCgAVWrVsXHxwdHR0diYmKYNGkS58+fZ8yYMXTs2BE7Ozu0Wi2jR49m7969PHv2jOLFizN+/Hjy5s2bpvOkVqslLi4Od3d3du/ezeHDh6lYsSIAs2fPZsuWLWzcuBGNRkOTJk0oXbo0AwcOJEeOHB+8PyluCpF+bty4gbW1NZaWlspt06dPZ+DAgSQnJ9OlSxfmzJnzwfnj/Ws1JiaGX375hbFjx1KiRAm2bt2qhAvFP/Prr79StmxZ+vTpQ2ho6Ef3+6u5XebSTyMkJAR/f3969+6Nh4cHBQsWTLVPUlIS27dvZ/ny5SxfvhytVouLiwvVqlUjMDCQPHnyyL9XhBBCCCGEEEIIIcQXTcI7QmRy169fp0CBAnz//ffExMQAcOzYMaZPn87ixYuV/VxdXQkPD6dJkyZotVq0Wi0GBgZp2kVCCCEywvvF1vPnz1OqVCkOHTqkBGLUajVHjhxh2LBhnD59mjFjxtChQwdy5coFvFvGSqPRkCVLFkxMTNKtYLhw4UK6du3KgAEDCA4OTvG7p02bxoABA5R9CxYsSKdOnahUqZISzhRCpK/Tp09TsWJFunbtSnh4OFmyZMHAwAAvLy/CwsIwNDQkW7ZsnDp1igIFCpCcnIyRkVGq+3l/3ho/fjwjRoxgw4YNNGnSJD1PKdM4ePAg1apVo1OnTixYsAC1Wp3isdc95k+ePOHy5ct89913GTjazOvatWvUr18fZ2dnoqKiUnQIPXjwIA8ePCBr1qw0bNhQuQZu3bpFcnIy9vb2GBkZpevrsBBCCCGEEEIIIYQQaUW+JihEJufk5ESpUqU4evQogYGBdO/enZYtW7Jx40ZGjBjB5s2bmTJlCteuXePo0aMAynIruv8WQojMQq1WK/Pb5cuXuXz5MklJSTRv3lwJ7iQnJ2NoaEjlypWZMGECpUuXxt/fn6VLl/LHH38AYGZmhoWFBSYmJgCfpGCo0Wg+uk2Xtf7hhx+oWrUqK1eu5MmTJ8rv/u233xgzZgz58+cnJCSEzp07o9Fo8Pf358cffyQhIeE/j08I8c/pOu48e/YsxfurFi1aMGzYMAYPHszLly8pW7YsFy5cwMjICLVaDfx53QNKF0SdatWqAbBmzRo0Gs1fzh9fK/3H60MKFSpEwYIFOXnyJK9fv07x2OuHpXr27Mm8efN49epVmo/5a3T//n1+//13GjVqRP78+VGr1cTGxtK/f39q1qyJu7s7TZs2ZcCAATx+/BiAvHnz4urqSrZs2TAxMUGr1UpwRwghhBBCCCGEEEJ88SS8I0Qm8bGijVarZfjw4WTPnh1/f3/WrFlD4cKFOXz4MP7+/jRs2JBGjRphaGjIvXv30nnUQgiRfvSLe6NGjaJ69eqULFmSmjVrsnbtWrZu3QqAkZERGo0GlUpFpUqVlADP2LFjmTlzJnFxcWkyPl2huFmzZqxbty7FNl2Q0tjYmOrVq/PgwQMCAwMBiI2NpVKlShgbGzN06FC8vLxYsGABO3fuJDg4mH379pE1a9Y0GbMQ4uO0Wi3Ozs78+uuvzJkzB1NTU7Zu3crjx4+pXLkygYGBBAcHExgYSHx8PFWrVuXChQsYGhqSmJioXPe3b98G3gX1dIEeNzc37O3tefPmDQYGBrJ0k579+/fz66+//s8wR/bs2alSpQqXLl3ixx9/RKPRYGhoSHJysvJ4rlixgiNHjmBubq6ENcWnZWpqStasWdmyZYuyVGWLFi2YP38+nTt3Zvz48VSrVo3IyEgOHDgAkOrvXb5sIIQQQgghhBBCCCEyA/mUV4hMQL+TxO3btzl58iQnT57k6dOnGBoa0qJFCw4cOMDKlSs5cOAAmzdvpkiRIsrSABs3biRbtmzKt7iFECIz0hX3xowZw5gxY8iXLx+NGzemSJEiAERGRnLhwgXgXWFQP8ATFBREnjx5WLRo0QeXtPlUTpw4wenTp/nxxx+VMJGObjlDLy8v8ufPz4ULF9i9ezeVKlXC1NSU8ePH061bN2VfJycnvL29cXFxITk5Oc3GLIRIKTY2NkX4xs7ODgsLC6ZOnUqjRo0IDQ3lxYsXyv5Dhw5l3LhxSoDn/PnzSlBk8+bN9O/fX1nqVKVS8fLlS2bNmsWDBw/45ptv0v8EP2O7du2iRo0ajBkz5i87jmm1WrJkycKECRMoUqQIa9asoXHjxty6dYvExEQA5s2bx6hRo7C0tMTHxwdjY+P0Oo1MSRc8e3/V7gIFCtCiRQt27dpFyZIlGTt2LCYmJuzZs4fw8HD8/Pzo06cPAL/99lu6j1sIIYQQQgghhBBCiPSi0r7/6ZkQ4oui39Y/MDCQBQsWcO3aNQBsbGwYPnw4zZo1w8XF5YPHL1u2jNGjR5MjRw42b96MjY1Nuo1dCCHSg1qtVjowqNVq6tSpg4uLC6NHj8bR0ZFz584xbdo05s2bR+fOnfHx8VEK4ro5VqvVcvLkSfLly0euXLnQarVp9k3/rVu3MmLECK5du8aiRYto0qRJinMB8PX1JSwsjKxZs2JtbU1gYCBdunRJMWYhRPr75ZdfKFasGO7u7ixatChFt5arV6/SunVrYmNjGTx4ML6+vlhaWirbg4KCGDFiBJaWlixatIhbt24xbdo0/vjjD86dO0eePHmAd8sMDRo0iJcvX7Jp0yaANJ2TvhTnz5+nUaNGuLm5MWzYMGrWrKls+9Djo3ttuHfvHk2bNuXMmTPkyJGDXLlykZyczM2bN3FycmLHjh04OzuneC0R/4z+Y5eQkMCzZ8+wsbHh7du3mJub88cff7Br1y7u3LlD/vz5qVu3Lubm5srxI0aMYMaMGURHR1OrVq2MOg0hhBBCCCGEEEIIIdKUhHeEyCR8fX2ZOHEitWvXpl27djx9+pTNmzdz8uRJmjdvTkBAAAUKFFD2j4+PJzg4mMWLF2NgYMDevXtxcnKSoq8QItOaNm0aGo2GcePGsWzZMurUqaNsu3//PgEBAcyZMydVgOf9gm1azZP6xeVt27YxYMAArl27xp07d7Czs0ux75EjR6hcuTKmpqYEBQXh6emZpmMTQvw9ly5dYsiQIWzdupUff/yRqKgoTExMlOv7xo0btGzZkgsXLuDj45MqwDNp0iRCQkJ4/PgxKpWK/Pnzs23bNlxcXFLMRbGxsRQuXBiQ614nIiICX19f5syZQ7t27QDYuXMnZcqUwcrK6oPH6B7Tx48fM2vWLA4dOkRsbCwuLi5UqFCBfv36YW9vL8Gd/0D/sZsyZQqrV6/m+PHjODg4UKJECQICAihduvRHj1+1ahXDhw8nT548rFu3jhw5cqTTyIUQQgghhBBCCCGESF9pt+6DECLdrF69msjISLp3746Pjw8FCxYkOTmZLFmysG/fPn755RccHByU/X/99VcGDhzIrl27qFOnDnPnziVv3rxSmBBCZFqHDx9mwIABfPvtt9jY2ChF76SkJIyNjXFwcGDs2LGoVCpmz56NVqvFz88PNze3VPNiWhXJVSqVUuCvX78+oaGhZMuWLVVwB+C7776jX79+REZG4uTkBEBycnKaLuklhPjf3NzcCAsLw8zMjAULFgCkCPA4OzuzZs0aWrVqRUhICECKAI+XlxdFixbl119/5c2bN/To0YNcuXKleo+mm8N0y+mJdyHMhIQEpUNZ79692bZtGwsXLvzo0rCGhoao1WpsbGwYOnQoKpWKhw8fYmdnpzy28v7439NqtcpjN2TIEMLDwylRogQ9e/bkwYMHrF+/nn379jFnzhxat26d6vgJEyYwe/ZsNBoNixYtIkeOHBJWE0IIIYQQQgghhBCZlnTeEeILoSvKfugDa09PT5YuXcrOnTspVaoUiYmJrF+/Hi8vL4yNjTl69Cg2NjbKfcTFxbFjxw60Wi3169cnR44cUpgQQmR6U6dOZeDAgcC7LjweHh5Ayq4ADx8+xN/fn3nz5tGkSROCg4MpVKhQuo7zQ8u7fOi2ZcuW0bFjR0qXLs2WLVs+GPIRQqS9c+fOER8fT5UqVZTbLl26hL+/P2vWrPlgB57r16/TqlWrj3bg0Sfv0f6e2NhYqlevjo2NDU5OTvz8888MHjyYIUOGpAix/y+yBNmnN3v2bPr370/Pnj0ZOHAg+fPnB8DDw4MZM2ZQvHhxTpw4gbGxMQAHDhxg8ODBXLp0iZIlS7Js2TLy5csn14IQQgghhBBCCCGEyNQkvCPEF2DHjh3Mnz+fyZMnkytXLiXAo9FoSE5OpkyZMmTNmpXjx4/z9u1b1q5di6+vLwYGBhw/fhxbW1sAjh49irGxMWXKlEGj0aBSqVCpVPINViFEpvX+/DZ79mx69eqFo6Mj06dPp3HjxkDqAM/AgQPZv38/58+fJ2fOnOky1vcLxn+ngNy8eXM2bNjAzz//TP369dN6iEKI99y6dQtnZ2dcXFzYvHkzRYoUUbb92wCPbj6SEMnfp+uitmvXLpo2bcqbN29o1qwZ06ZNw9HRUd7rpoOP/b1qtVqaN2/OhQsX2LBhA99++y1v3rxhy5YtDBo0iKxZs3Lw4EFsbGyU5+nu3buMHDmSYsWK0blzZ3LmzCnBHSGEEEIIIYQQQgiR6cnaCkJ8AYKDg9m9ezeGhoaEhoZiZ2enfLhtYmKCq6sr58+f5/bt25w7d+6DwR2An376CWtra7Zt20aWLFmU26WYIYTILN4v0L5fSOzZsyeJiYn0798ff39/DA0NadCggbJ0iqGhIbly5WLq1KkYGhpibW2dLkVf/aLnvXv3yJkzZ4p5+n26sVatWpVTp05RokSJNB2fEOLD8uXLR//+/bl58yZ58+ZNsc3NzY0xY8YAfHAJLRcXF2UJrcmTJ/Py5UsCAwOxsLAAUs9f4uN0HVuio6N58+YNpqam/PLLL5w9e5Y8efJgYGAgYag0dOTIEW7cuEGzZs0wMzNLse3x48ccPHiQpk2b8u2335KcnMz69evx8fHBwMCAAwcOYGNjA6D82yV//vxERUVhaGiIoaEhGo1GgjtCCCGEEEIIIYQQItOTir0QX4CdO3dSr149li5dyoABA3j06BEGBgao1WoAvvnmG27evMnAgQPp27cvhoaGHDt2LEVwJywsjIcPH9K4cWOlwCGEEJmJWq1WQjarVq3C29ubWrVqMWzYMNauXavs5+HhwZQpUzhz5gzDhw9n69atAEqAB8DW1hZra2u0Wm26Bnd27NjBkCFDWLJkCX/VHFFXxOzYsSNnzpzB3t5eGbsQIn1NmTKFFStWYG5uzsyZM9m9e7eyTRfgadWqFQsWLKB3794kJiaiUqmUAE9MTAy2trasX78ejUaTgWfy5Xv16hUDBgwgNDSUp0+f4uPjw5o1a5R5VprOfnpPnjyhS5cu9OrVi02bNvH69etU+xgYGPDs2TNevHhBTEyMEtw5ceKE8u8VrVaLp6cns2bNIjk5GRMTE+W1Tr5oIIQQQgghhBBCCCG+BrJslhCfueTkZIyM3jXJql27Nnv27KFt27aEh4eTK1cuAOLj46lVqxanTp3C2tqaQ4cOUbhwYeU+Vq5cyciRI7GxsWH9+vUpQj1CCJEZ6HfH8fb2Zvr06RgbG2Nra8vdu3d58+YNw4YNY+zYsUpQJiIiAk9PT0qVKsWECROoV69euo9bP7ize/dufH19OXXqFOfOnaNYsWIf3O9Dt0lHCSEy3q5du6hbty6VK1cmKCiIypUrK9v+1xJat2/fxtjYGHt7e7me/wX9x+zZs2fkyJGDlStX0rdvX+zs7Bg7diytWrVSAjzy+H46b9++ZePGjfj7+xMfH09ISAjff/892bJlU/Zp0qQJZ86cwdPTk6ioKIBUHULHjx9PcHAwUVFRtGvXLt3PQwghhBBCCCGEEEKIjCZfYRPiM6dfXNi1axfVq1dn5cqVeHp68vDhQwCyZs2Kv78/xYsX5+3bt8ycOZN9+/Zx7tw5vLy8GDx4MAkJCaxYsQJbW1v5VrcQItPRBXcmTJhAWFgYnTt3ZteuXVy5coUNGzbg5uamFAZ1+vXrx9SpUzl37hy9evVi79696Trm94M7fn5+XLp0iSNHjlCsWDFevnzJL7/8wps3bz5YaNa/TQrRQmS82rVrExAQwKFDhxg2bBgHDx5Utv2vDjyOjo5KBy25nv+399/Lvn37lrdv3wKQI0cOAJo1a8aMGTN49OgRI0eOlA48aSRLliw0bdqU8ePHY2pqipeXF+vXr+fVq1fKPm3btuXly5eMGDGCpKQkTp06lSK4s2LFCubNm0fZsmVp0KBBRpyGEEIIIYQQQgghhBAZTjrvCPEZU6vVSrv4efPm8eDBA86dO0d0dDTGxsa0aNGCKVOmkCtXLl6+fMmhQ4cICAjg+PHjGBoaotFoMDc3p2zZssyfP598+fKluE8hhMhMbt68Se3atXF1dSUyMpKCBQuSnJzM9u3b6datG2ZmZhw7dgwbG5sUx4WEhBAeHs7p06ext7dPl7F+KLhz8eJF9uzZQ4UKFXj+/Dnz589n+fLljBgxgqZNm6bLuIQQ/5x+5y+AMWPGMGrUKKpWrcq4ceOoUqWKsk2/A0/Lli1ZtmwZJiYmGTHsL5b+e9lFixaxb98+Tp48iY2NDS1btqR58+bkyZMHgDdv3rBhwwb69OkjHXjSiK5LaFJSErt27WLQoEG8efOGcePG0axZM8zNzXn+/DlDhgxhxYoVODk5sXTpUnLkyIG9vT1Tpkxh5syZaDQaDhw4gKOjY6prSgghhBBCCCGEEEKIr4GEd4T4Anh5eTFnzhzy589P8+bNOXXqFLGxscTGxuLu7k54eLhScFar1SxcuJAnT56QkJBAlSpVKFOmDNmzZ5fgjhAiUzt48CDVqlVjyZIltG/fnqSkJFavXo2fnx8GBgacOHECGxsb3r59y/3793F2dlaOffnyJebm5ukyT/6d4M6SJUvw8/OjaNGiHD16NE3HI4T4Z/5OsOCvAjy//fYbnp6enDlzhtjYWKytrdN6yJmG/vw5ZMgQIiIiMDMzw9bWlitXrgDQqFEj+vfvT/369YF3XXnWr19Pnz59yJMnDz4+PnTo0EGCO5+A/mvm4cOHOXHiBPv372ft2rV8++23DB06lCZNmmBhYcHjx48ZNWoUS5Ys4cWLF1hZWZGcnMybN28oVqwYa9aswcnJSf69IoQQQgghhBBCCCG+WkYZPQAhxF9btmwZYWFheHh4MHjwYFxcXHj9+jX37t2jR48erFq1CkDpwGNoaEi3bt1S3Y9Go5EPwoUQmVp8fDwA2bJlAyAmJkYJ7hw/flzpuJOQkEDPnj3x9/enatWqAJibm6PVaj+L4M7ixYsZOnQoZcuWZc+ePQBSzBTiM6F/LW7fvp1bt25x/fp1atWqhYuLC66urgD4+/sDMGrUKIYPH54iwFOkSBEiIyOxsrLC2tpauoz8A7r5MzQ0lMmTJ+Pp6UnXrl0pUaIE27dvZ/HixURHR/PkyRNMTEyoWbMmWbJkoUWLFqhUKtq2bcucOXNo1aoVWbNmzeCz+bLpv2Z6e3uzaNEizMzMKFeuHHnz5uXixYsMHz4cgMaNG2NjY8OECRNo06YNa9eu5e7du1haWlKjRg0aNmyIjY2NvNYJIYQQQgghhBBCiK+adN4R4jM3YMAApk2bxunTpylZsmSKwm9iYiK1atXi8OHDtGnThilTpmBvb6+0rxdCiK/JqVOnKFeuHIMHD6ZcuXL4+PgowR1bW1tlv169erFu3Tp+/vlnSpcunW7j+yfBndKlS7Nv3z4AmdOFyEAHDhzg1atXNGjQIEXIxs/Pj7CwMJKTkwEwNDSkWLFijBgxgpYtWyrH63fgGT9+PJUrV05x/xLc+We0Wi2PHz+mQYMGqFQq1q5di6Ojo7L91q1bzJs3j/Hjx9O6dWsWLFigLEuWmJjItm3bKFWqFHnz5s2oU8h0pk6dysCBAxk4cCB9+/alQIECPHz4kPnz5zNjxgy0Wi3BwcFKBx6d95ctk2tBCCGEEEIIIYQQQnzt5NMxIT5zDx8+xNDQMEXhGd5989vExISwsDDs7OzYtGkTgwYN4v79+1LkFUJkWhqN5qO3FStWjMaNGzNlyhQ8PDwwNDRMFdxZuHAh27dvp27duhQpUiTdxg2kCO74+Pjw66+/SnBHiM/YgwcPaN++PY0aNWLbtm1KsGDSpElMnjyZFi1asGnTJhYuXEjPnj05e/YsrVu3ZuHChcp9+Pv7M2rUKA4cOECfPn04fvx4it8hYYV/RqVS8eTJE86cOUOJEiVwdHRErVYrrwP58uWjY8eO1KxZkxUrVrBlyxblWBMTE5o2bUrevHlRq9UZdQqZhlarRa1Ws3nzZhwcHOjfvz8FChRArVaTK1cu+vfvT3BwMMnJyYwcOZKNGzfy8uVL5fj3ly2Ta0EIIYQQQgghhBBCfO3kEzIhPnO6AsPWrVvRaDTKB926lvKFCxfG2toaIyMjVq5cydixY5GGWkKIzEitVivFvd9++42LFy9y584d5TYTExPat2+Po6MjcXFxDBgwIEVwZ968eQQGBpIlSxaCg4PJli1bus+XT58+Zfbs2fz222/s2rVLgjtCfMbs7e0ZPnw4Tk5OtGnThs2bNwPvlstq1aoVISEhNGrUiE6dOjF9+nRmzpwJQPfu3VOERvz9/fHy8uLBgwc4OztnxKlkKmZmZlhYWPDixQvg3Xti/SBIgQIF6Ny5MwAXL1784H3I0kz/nVarJT4+nnPnzmFjY4OTk1OKZa/MzMxo3Lgx7du359q1awQHB7Np0yZevXqVwSMXQgghhBBCCCGEEOLzJOEdIT5TuoJy27ZtsbW1JSoqirt37yrbdd8Yzp49O3Z2dkyaNInOnTvj7e2d6pusQgjxJbp8+TK///678rOuIDh69Ghq1apF2bJlKVWqFMHBwfz2228AtGvXjgEDBmBvb4+Pjw/Vq1enT58+VKtWjSFDhqDVatm6dSt58uRBrVanyXz5V92BrK2t6dq1K0eOHKFChQo8e/aMJUuWSHBHiM+IfleW3r174+/vj6WlJR06dCA6Oppnz57RunVrnJyc0Gg0yv49e/ZkypQpaDQaZs+ezbNnz5RrPyQkhKtXr2JnZ/fBOUL8PVqtFiMjI+zt7VmzZg3r168H3nVx0Wq1yjJmZcqUAZCgSBoyMDAgR44clChRgocPH/Lo0SMMDQ2V60Gr1WJhYcFPP/2EhYUFV69epWvXruzcuTODRy6EEEIIIYQQQgghxOdJwjtCfKZ0BeVChQrRqlUrTp06RZcuXbh06RJv3rzB0NAQjUbDqlWruHTpEkWKFGHBggW4uLgohQshhPhSxcbG4ubmxqhRo1IEeIYPH87o0aPJlSsX33//PTly5GDYsGEEBgZy+vRpAAYMGMCMGTPo1q0bp0+fZtGiRcTFxdGlSxcOHDiAs7Nziu4An5J+d6Bbt25x/fp1Xr16lWI5kHr16lGsWDFevnzJ0qVL8fHxoUyZMhLcEeIzsHv3brp168Yff/yh3Na1a1cCAwOxsLCgY8eOnDlzhjdv3gDvAgy692QA/fv3p27duhw8eJC4uLgU176lpSVarVaWB/oPVCoVDg4ODB06FIDg4GBl7lSpVMrcuWnTJgwMDChdunSGjTWz02q1aDQaSpYsyaNHj/Dy8iI5OVkJ8Oi+iGBjY4OlpSUeHh589913lC1bNoNHLoQQQgghhBBCCCHE50k+ORYiHb3/Tev/9c1rrVZL9uzZGTFiBK1atWLv3r24u7vj7+/P7t27CQgIYMSIEdja2uLm5qYcJ0VfIcSXzsDAgLZt2xIdHa10rHjy5Anbtm2jT58+rF+/npUrV7J+/Xr69OnDsmXLCA4O5tSpUwA0a9aMGTNmcPXqVS5fvszp06cJDQ3FwcEhzYI7Go1Gud9x48ZRs2ZNihcvTpkyZZg3b16K7mnwbgmtxYsXU758efbu3QtIcEeIjDZq1CgWL17MihUrgD+78Pz444+MGjWKIkWKoFarOXjwoLJsE7ybs3T7urm58eTJE2JjY1Pdv3RH/G90gZAuXbowePBgjh49Sv/+/Vm2bBlJSUkkJSWxdOlS5syZQ9GiRalZs2YGjzjzUqlUGBgY4O3tTf78+Vm2bBnDhg1TAjy6kNq6devImjUrnTt3TtH5TgghhBBCCCGEEEIIkZJKq/sEVAiRbho2bMjgwYOpW7cuGo3mL7+Brdt+//59pk6dyurVq1N0oXBzc2Pz5s04Ozv/z/sSQogvydWrVwkKCmL+/Pn06dOH6tWr07lzZ3bu3EnlypWV/f744w/GjRvH1KlTadOmDX5+fpQqVQp4V+hN72L50KFDCQ4OpnDhwuTNm5fz58/z/PlzevfujaenJ66ursq+x48fp3z58oAEd4T4HGg0GgICAhg0aBDW1tYkJCSQNWtWZfucOXMIDg7m7t27LFiwAHd3dyDlXNO5c2c2b97M3r17KVasWIacx5fqn8zZT548ITQ0lKCgIACKFi1KYmIi9+7dw8bGhj179sj743/onz5WujDsuXPnaNKkCXfv3qVJkyb4+Phga2vLjh07mD59OpaWluzcuRNzc/M0HL0QQgghhBBCCCGEEF82Ce8Ikc4OHDhAo0aNSE5OZtu2bVSrVu1/flCuK2S8efOGFy9esGnTJpKSkrC1taV69erkzJkzzTpJCCFERrpy5QoTJkxg4cKF1KxZk5cvX3L06FEgZdjl8ePHBAYGKgGe4cOHU7x48XQZo/78e/v2bRo0aECtWrXw8fHB0dGR48ePExISQkxMDH369GHIkCEpAjzv34cQImMkJSVhbGys/DxgwACuX7/OggULsLa2Vm6fP38+AQEBxMXFMXXqVBo0aICDgwMA69evp2fPnhQpUoRNmzZhaWmZ7ufxpdIP7ty7d4/cuXP/rePWrl3L0qVLOX/+PLly5aJs2bL4+Pikaae1zG7s2LG0aNGCb7/99n/uq3vefvvtN9q1a8e5c+cwMjLC0NCQt2/f4uLiwq5du3B2ds6QQK0QQgghhBBCCCGEEF8KCe8IkQHWrFnD0KFDuXv3Lhs3bqRWrVr/6f7kG8VCiC/Z+3PY+wX0GzduMG7cOObOnQvAhg0baNKkSapjdQGeyMhIateuTVhYGN988026ncfevXu5d+8eP/30E3v27KFcuXLAu8LmrVu38PX1ZdWqVfTt25fBgwenCvAIIT4vRYsW5dKlS3Tp0oXQ0NBUAZ5Ro0bx6NEjypcvT40aNTh9+jRXr14lISGBffv24eTkJO/R/gUvLy/u3bvHkiVLAP7W46fVaklMTCRLlizKYy7BnX9n5syZ9OnTB3d3d2WpuP9F95g/f/6cNWvWcOrUKV6+fEnhwoXp1q0b9vb28nwIIYQQQgghhBBCCPE/SHhHiHSkX8CJiYnBw8ODhw8f8scff2BlZfWPizvy7VUhxJdOfx6LjY2lcOHCyrapU6dStGhRateuTWxsLJGRkURERNC6dWvGjh2r7Pt+gMfX15etW7dy/vx5cubMmS7nsWDBArp160bjxo15/fo1u3btAlJ21Ll16xY+Pj4S4BHiM/Sh91QajYZatWqxf/9+OnbsSHh4eIoAz7x585g4cSKxsbEUKFCAevXqkSdPHrp06ULu3LklrPAvvH79mpo1a3Lr1i0uXLiAjY3N3zpO//mT98f/nbe3N6GhobRt25aRI0f+rSDsX/29y7UghBBCCCGEEEIIIcT/ZpTRAxDia2JgYKAUFFq2bElycjI5cuT418VlKUwIIb50unmsQoUKPHjwgPXr11OyZEn69+9PZGQk06dPp3r16hQuXJh+/fqRkJDA3LlzsbKywsfHh/z582NgYKAEeGxsbJg4cSKhoaHkyJEj3bpeFC1alGbNmrFhwwYADh8+TKVKlTA0NFTm/Xz58hESEgLA7NmzefHiBWPHjsXJySnNxyeE+Dj9eeLRo0dYWVlhbGyMgYEBu3fvpnr16koXGP0AT7du3VCr1URERHDhwgWWLl2qdNySsMK/ky1bNlq2bMnQoUMJDw9nzJgxf2sO139PLO+P/z3d3+3EiRNRqVRMmjSJhIQElixZgrm5+V8eq/t7/1CQSq4FIYQQQgghhBBCCCH+NwnvCJHOVCqV8sG4u7u7crt8S1gI8TUrVaoUs2bNwsPDg7x58xIdHc2QIUNo1KgRRkbv3q4UKlQIHx8ftFotc+bMAfhggEdXWNdqtWke3NHN3eXKlWPkyJGYm5uzbNkylixZgqOjI46OjqhUqhQBnokTJ/L8+XP27NmDpaVlmo5PCPHX9EM2M2fOZMuWLXz77beMHj0aACMjI/bt2/fRAE/Pnj1JTExk3rx5ODg4KPcrYYV/r1evXkRFRbFnzx50TWLlfXL6MDQ0VK6JkJAQ3r59S5UqVf5ncEefBKmEEEIIIYQQQgghhPh3ZNksIdLYX33zOr06QgghxOdKfx4cN24cI0eOBKBDhw7MnDmTbNmyKcVbXRHwypUrBAcHM2/ePHr27KkEeNJ7vB9y6tQpxo4dy+bNm/Hz86NPnz7kzp0bSFl8vnfvHlmyZCFnzpxSlBYig+hfzz4+PsyYMQNnZ2fGjRtHs2bNAEhOTsbIyAiNRkP16tU5dOjQB5fQevXqFWZmZtJx5296fy7V/ax7/EaMGMH48eOZPXs23bt3z8CRfh30nw/d37wQQgghhBBCCCGEECJ9yadyQqQhjUajFHBmz57N8ePHefbsGfXq1aNx48bkzp1bAjxCiK+afrFWP8By/vx5fv/9d4oVKwak/PZ+wYIF8fX1BWDhwoW8ePGCoKCgNF9+Sr8of/nyZe7fv8+zZ88oUaIE9vb2mJqaUqZMGUaOHIlGoyE4OBhACfCoVCplztcFeuQ1QIiMo7v2Ro0axeTJk+nTpw+9evWiaNGiyj5GRkZKFy/9Djy6pYVsbGwAMDMzA6Tjzt+le+wvXbqEk5MT2bJlA/58/Jo2bcrEiRNZt24dbdu2xczMTEKOaUT/tW3Tpk1cvnyZxo0bU7hw4QwemRBCCCGEEEIIIYQQXxfpvCNEOvDy8iIsLEwpVOi+vT179mwKFCggxVshxFcvKSmJDRs2cPPmTa5evUpUVBRlypQhMjKS8uXLK/vpz5e///47vr6+HDt2jHPnzqXogvGp6f/ewMBA5syZw61btwAwNzenXbt2dOnShUqVKgHvOvCMHj2arVu34uvrS9++fVMsqSOE+Dzs27ePFi1a0KhRI8aOHYuLi4uy7caNG7x9+5bcuXNjYWEBvJsLateuzb59++jfvz/h4eESKvkH9DuNjR07loCAAOrWrcuwYcMoXLgw9vb2yr4//fQTCxcu5MCBAyleB8Sno//aFhAQQEREBNbW1kRERFC/fv0MHp0QQgghhBBCCCGEEF8XSQsIkQY0Go3y3ytWrGDWrFn07t2bPXv2cOHCBVq1asW+ffto3bo1ly9fxsDAIMUxQgiR2emyw7r/NzY2pnHjxgwYMIDp06fj4+PDqVOn8PDw4Pjx48CfRUatVsuzZ8/Inz8/4eHhSnAnLfPI+kvrjBo1ikKFChEZGcmgQYMoXrw4s2fPpl+/fuzatQuAMmXKMGrUKBo0aEBYWBghISE8fPgwzcYnhPh3YmNjefbsGd27d8fFxQW1Ws2TJ08YNWoUVatWpVixYpQrV46DBw8C7+aCXbt20bx5cwYNGiTBnb/w/nvbxMRE5fG6dOkSLi4u1K1bl/3791O7dm1atmxJVFQUjx8/BqBNmzYkJSUxceJEXr58me7jz+x0HaUAfH19GTduHE2bNmXJkiUS3BFCCCGEEEIIIYQQIgNIeEeIT0z/g/C3b9/y6tUrChcuzODBg6latSpubm5ER0fTt29fzp8/T5s2bSTAI4T4qqjV6hQF78TERABMTU2V+TMoKAg/Pz9OnTpF3759OXLkiBLc2bhxIwMGDODAgQPkzZsXa2trNBrNJymi/1UAaMOGDUybNo3u3bsza9Ys+vTpw6RJk1i6dCmDBg3i7NmzjBgxglOnTgFQunRpxowZQ5kyZVi7di2mpqb/eXxCiE/rxYsXwLsuO1qtlnnz5tG0aVOlC0+9evW4fPkyP/74I0+ePAHeBXhiYmJwdnYmOTk5I4f/WdPN5/7+/ly+fBkTExPg3VKC48ePp0mTJmzbto2tW7fi7e1NbGwsffv2pXr16vj4+FCsWDEqVKjAxYsXiY+PB1IHgsS/p3vNnD9/PhEREXh4eDBq1CgqVKiQYj/da7QQQgghhBBCCCGEECJtSXhHiE9M90F4v379qFChAlFRUVSuXJkCBQqg1WpJSkoCICIigr59+3LhwoUUAR61Wp2RwxdCiDSlVqsxNDQEYPHixfz444/Url0bd3d3jh07phRoAcaPH4+fnx+nT5+mZ8+ebN++nWXLluHn50d0dDQFChRQ9v1USw/q5ugPFeRPnz6NWq2mV69eODs7KyEkJycnJk6cSO/evTl27Bjbt29XjilZsiQzZszgxIkTZM+ePU27AwkhPk7/2tP/74oVK5IjRw66d++OtbU1vXr14v79+2zcuJGYmBg2bdpEp06duHbtGmfOnEl1v0ZGRuky/i/VmDFjCAwMxM/Pj1evXuHn58fMmTOxsbFR5tvq1aszYcIEdu/ezaxZs9BqtUyaNImSJUty8+ZNYmNjCQ8PBz7dXC/eSUxMZMeOHZibm9OzZ0+cnZ2VbbNmzaJDhw7UqFGD5cuXZ9wghRBCCCGEEEIIIYT4Sqi0UkUSIk20adOGNWvWYGVlRZcuXQgLC1OK1vrF6379+jF9+nRKlSrFkiVLcHNzy+CRCyFE2tAtewXg7e1NWFgY5ubm5M2bl3v37mFkZISXlxedO3fGwcFBOc7f35/AwEAATExMcHR0ZPv27bi4uKS4z/9q3759dOzYkX379uHq6qrcrpuzmzZtyubNmzl//jzffvstWq02Rbefs2fP0qhRI7Jly8apU6fInj37R89fCJF+9N93Qepr8eeff2bZsmUkJydTrFgx+vTpg5WVlbK9Xbt2HDt2jH379uHo6JiuY88M2rVrx8qVK3F2dubGjRv4+/vTrVs38uXLB5BqLo2Pj2fnzp2sWbOGdevWkZiYiJubGxs3blSOEZ/G69evqVGjBnFxcVy5cgWAHTt2MGPGDNatW4e1tTXPnj1Do9GwYMECOnfunMEjFkIIIYQQQgghhBAi85IKkhCfmC4PFx0dTa9evYiLi2P+/PlcuHABQ0NDtFqtEuCBdx14+vfvz5kzZ/D09EStVktnBiFEpqQrlo8ZM4YpU6bQq1cvDh48yC+//MKaNWt4+vQp4eHhREVF8eDBA+W4MWPGsGDBAry9vRk0aBD79+/HxcUFtVr9ScMw27dv5+7duwQHB6PVapX/6Yr+5cuXB+DSpUtA6uVbSpYsSbFixfjjjz94/PjxR89fCJF+NBqNcg3PnTuXHj160Lp1a+bNm8erV68AaNiwIVFRUSxfvhw/P78UwZ1Vq1Zx4MABypYtS86cOTPkHL4kJ0+eZNOmTbx9+1a5bfny5eTJk4e7d+/i6upKgwYNyJcvn/KeVz+4o1arsbCwoEWLFixZsoQNGzbQr18/Lly4wIEDBzLilDKNDy05ptVqKVKkCL///jtNmjShYcOGtGnThoMHDxIaGsr+/ftZs2YNhoaGhIWF8fz5c/l3ihBCCCGEEEIIIYQQaUQ67wjxH+mKDrpLSaVS8fbtW7JkyQKAh4cHM2bMoGrVqkRFReHm5qYco/9N8BEjRtCzZ0+cnJwy7FyEECKt7dy5kx49elC7dm2GDh1KgQIFSEhIoHTp0sTFxWFhYcG9e/fw8vKid+/eKTrw6Hu/k8anoNVqWb58OQ0bNsTKyornz5+TPXt2kpKSMDY2ZtOmTbRo0QJbW1sOHTqkBIhUKpUSzKlRowYPHz7k8OHDKQIAQoiM5e3tTWhoKMbGxspyTe3atcPLy4tSpUp98JjIyEjCw8PRarXs3buXvHnzpgqbiD89evSIkiVLEhcXx8GDBylTpgxqtZoTJ05QrVo1cufOza1bt2jWrBnh4eE4Ozt/tCOZ/u1Hjx6lXr16lChRgs2bN2NhYSHPwT+k/5p54MABnj9/TqVKlbC2tubatWv07duX48ePY2lpScWKFZk4cWKKLlMuLi4UL16c9evXZ9QpCCGEEEIIIYQQQgiR6clXwIX4D3RFW3hXZIiLiwNIUVCIjIyke/fuHDhwAA8PDy5duqSEfQwNDUlOTgYgMDAQJycn5WchhMhskpOTOXLkCGq1mp49e1KgQAFevnxJ2bJliYuLIyQkhJkzZ+Lk5ERUVBRRUVHcv3//g/f1qYM7uvm8ffv2WFlZ4e3tjZ2dHbdu3cLY2BiAJk2a0LVrVx48eECbNm24evUqhoaGSoF5zZo1nD9/nvLly2NmZvZJxyeE+Gf0v5+wdu1aZs2aRc+ePTlx4gR79+6le/fuREdH4+/vz+nTp5V9k5OT2bdvH3Xr1iUgIIBs2bKxa9cu8ubNm+J9n0jNzMyMESNG0LFjRwoWLAi86zhWunRpDh06xOnTp3F3d2fDhg3079+fmzdvYmBggEajSdUVxsDAQOlSWbFiRWrWrMm1a9d4+/atPAf/kH5wZ+zYsbRu3Zrhw4dz5coV1Go1rq6uLFu2jAMHDrB3714WLVqkBHfUajVz5szhjz/+oEyZMmg0Gum8I4QQQgghhBBCCCFEGjHK6AEI8aXS/yB89uzZbN68maNHj+Ls7EyxYsUYPHgwbm5uynatVsu8efPw8PAgMjJS6cBjZJTyMnz/ZyGEyCyMjIwoXLgwo0ePpmLFirx58wZ3d3cePHhASEgInTp1QqVSUbZsWZYsWcLs2bN5+fIlfn5+2NrapssYdV01Xr9+TVJSEtWqVWP//v3ky5cPgFmzZvH48WPWrVvHd999R9++fXFzc+PMmTNER0djZmbGuHHjMDExkQ4dQmQQ/WsvISGBM2fO4OLigre3NwUKFADedRJxcHAgKCgIrVbLmDFjKF26NEZGRty/f5/Lly/z448/4u3tTa5cudKk21dmY2ZmRvfu3TEwMMDY2JhJkybh4uJCs2bNKFeuHAArVqwgOTmZmJgY4N3ysfpdJ8+ePYuxsTFFixZVHu9nz57x5s0bjIyMePr0abq9HmQG+ks/ent7Ex4eTqtWrejTpw8VKlQA3n0BwdraGmtr61THL1u2jLCwMFxdXenVq5cs/yiEEEIIIYQQQgghRBqSZbOE+Bf0i0JDhgwhPDwcBwcHbG1tiYuL49atW1hYWLBq1Srq16+vHNejRw/mzZtHnTp1CA0NpVixYhl1CkIIkabeD67oL4GiW4YqJiaGrl278uOPPxIcHIypqSkAS5cuJTw8nJcvX/LmzRvOnj1L9uzZP9nYPrZMC7wrHJcsWRKtVou/vz/jxo0jT548HDp0SAnwwLu5Pzo6mjt37gDvitbFixdn2bJlODk5SaFfiM+Ar68vZ8+excLCguLFi+Pv709ycrISlL5//z5RUVFMmDCBevXqMXr0aMqUKQPAnTt3sLW1JUuWLH85Z3zt9uzZw4kTJ/Dx8QH+nF/PnDlD2bJlyZ8/P5MnT6Zu3bqYmJgox7Vu3ZqYmBgaN27M7Nmzsbe3Z+PGjQwePJhatWoxbdo0TExMSExMZPXq1XTs2JEePXowa9asjDrVL1pERAReXl707dsXT09PnJ2dP7ifrgvSvXv3CAkJYd26dZiYmLBnzx55bRNCCCGEEEIIIYQQIo3Jp9BC/Au6gvS0adOYOnUqAwcO5MCBA5w5c4YLFy4wZMgQ4uPjadGiBXv27FGOmzNnDr169WLnzp2MHTtWlsgSQmRKH1paRr/wrVuG6uLFi8THx9OmTRsluAOwevVqLCws2LRpE8ePHyd79uyfdJkO3Vg8PDw4ePCgcnu/fv2oUaMG586dQ6VSMWbMGIYOHcrdu3epXLkyt27dUvYNDQ1l8+bNrFq1ioiICNatW8fmzZuluCnEZyIuLo6HDx+yZ88eYmJiiI2NRa1WY2RkpMwnDg4O9O7dm6FDh7Jjxw7Gjh3LkSNHAMibNy9ZsmRBq9VKcOcDtFotz58/p2PHjvj5+TFp0iTgz/m1YMGCLFiwgISEBLy8vNi+fTuJiYnK8atXr6ZVq1Zs3ryZunXr0rt3bwYMGMDTp0/x9fVVgj4mJiY8e/aMvn37KsEd+e7JP/P06VNiYmJwdnamV69eKYI7K1asYOTIkfTu3Ztff/0VAwMD4uPjcXd3Z/bs2VSuXJkDBw7Ia5sQQgghhBBCCCGEEOlA1ucR4n/42LInCQkJrFu3DhcXF3r37o2LiwsajQYLCwsmTpyIvb093t7etG/fnkOHDuHq6grAjBkzsLCwoE+fPrJElhAi09FoNEpxLyIiguPHj/P8+XM6depEhQoVcHR0VObVLFmyAO+63ZQvXx4TExNWrVrFxYsX6dixI66urqhUqjTperFq1SpmzJjByZMnWbFiBZMnT2b69OkMHjwYOzs74F1QMzAwEIAJEyZQuXLlFB14ihUrlqqDmv75CyEyjpWVFSNHjsTGxoa5c+fy66+/cuHCBUqWLIlKpVLmIV2Ax9DQkFGjRpE9e3bKli2rhAxl6bsPU6lUZM+enYULF9KjRw98fHzQaDRKBx5zc3NatmyJSqXCx8cHb29vAOrVq6cEc6Kjo+nduzeLFy/mzp07FCpUSOnwot8hqW/fvsrvlS5I/9zbt2+5fPkypUqVonDhwgAcOXKEGTNmsGTJEqXD0dKlSzl06BDFixdn6dKlxMbGUrVqVSwsLCS4I4QQQgghhBBCCCFEOpBls4T4C8eOHePatWs0bdoUc3PzFNvu379PkSJFaNCgAStXrlS+BaxfuO3duzezZs0iNDSUQYMG8fbtW6VYDaQoTAghRGbi4+PDpEmTyJo1K0lJSahUKpo3b86IESOUwMu5c+fo3Lkz9+7do2XLlrx8+ZLdu3djamrKwYMHyZMnT5qN79GjRyxbtowxY8ZgYmLCo0eP8PLyYvDgwdjb2wMoxUqtVsuIESOYMGFCiiW0dMt/CSEy1seC1gBXrlwhIiKCyMhIvv/+e8LCwnByckp13N27d1mxYgXu7u44Ojqm29i/ZLrHb+/evXTo0IH79+8TFBSkBHgAXr16xdq1a/Hx8SF79uxMnDgxRYAH4Pjx4xgaGuLi4oK1tXWKoIj+c/RXz7P4uHv37lG7dm1iY2MZNmwYt2/fZvv27bx9+5Y+ffrQoEEDduzYQWBgIJ06dWLmzJkpuuFJYEoIIYQQQgghhBBCiPQh4R0hPuLp06dUrFiRq1evsn//fqpUqZJi+7179yhatCh2dnbs3LkzRaFHV3Q4c+YM3333HS1btmTZsmXpfQpCCJEhduzYQefOnWnVqhVdunTBxMSEsLAwFi9ezHfffUdERASlSpUiKSmJDRs2EBUVxa5du7CwsKBEiRIsWbKEfPnypcs3/atUqcLhw4ext7dn7ty5NGzYEPizSPyhAI+TkxO7d+/GxcUlTccmhPjf9OeJhw8f8vr1axISEvjmm2+Ufa5cucK0adOIjIykZcuWTJo06YMBHl1IQbqM/HP/NcCjI0GRf++vHrvt27fTvn17nj59Svbs2SlfvjxTp07FxcUFExMTXr9+Tc6cOenevTsRERHpPHIhhBBCCCGEEEIIIQTIsllCfJSZmRkBAQEcPXpU6RKh32Uhd+7c1KtXj/Xr17Njxw46deqUaokFOzs7jIyMpDODECJTe78bwr1797CyssLT05NChQoBsHDhQnLkyMG0adPo168fU6dOpUyZMjRv3pymTZty/PhxcuXKhZ2dHdmzZ0/z4rlGo+Hs2bPcvn2batWqcerUKQICArC1taV06dJKAdTQ0FAZS2BgIAYGBowbNw53d3eOHTuGSqWSThBCZBD9boeTJk0iOjqa3377DbVaTe3atWnVqhWdOnWiYMGCeHp6AhAZGans7+TklGIJLf3rXvw9useuRo0aLF26lA4dOuDn5wegBHjMzMxo0aKFcpu3tzcGBgbUqVMnVYBHgjv/jv5rZmxsLM+ePePZs2eUKFECKysr6tWrx65du7h//z7W1tYUL15c6a6jVquZPXs2Wq2W4sWLA9LlSAghhBBCCCGEEEKIjCCdd4T4C8nJyQAYGRkxfvx47O3t+eGHH8iWLRsAW7ZsoWfPnmTNmpVZs2bx3XffkTVrVuBdQWnGjBkMGjSI4OBgBg0aJB+ECyEyHf2CYUJCAsbGxoSFhXHo0CHWr1+PVqtFrVYrSwQOHDiQqVOnUrFiRSIiIihdujTw4e4X6eHMmTNky5aNn3/+mYCAAAoWLMjMmTMpU6ZMirHod+AJCgqiffv2SucOIUT6058zvL29CQ0NpWzZslSrVo03b96wYsUKDAwMcHd3Z9q0aahUKq5fv87kyZOJjIzE3d2dcePG4erqmsFn8mXT/6fk311Ca9iwYbx584Zly5ZRp06djBh2pqL/mhkYGMiiRYu4ceMGycnJFChQQFmy0tLSMtWxWq2WlStXMnbsWMzMzNiyZQs2NjbpfQpCCCGEEEIIIYQQQggkvCPER+kXhX799Ve+++47zMzMCAsLo2nTppiZmfHq1SvCwsIIDg7GxsaG3r178/3335M/f36WLFnCxIkTlUKGnZ1dBp+REEJ8WvoFw9DQULZt28azZ8+wtrYGYOvWrUphV79Dhi7AU6VKFUJDQylXrlyaj/X9Tj7vhymfPHnC3LlzGTduHAUKFGDmzJmUKlVKCezs3LkTtVpNgwYNlGOSk5OVUJIQImMsXryYnj170r17dwYOHEjBggUBmDx5MkOGDKF8+fLs2rULMzMzAK5fv87UqVOZMmUKPXv2ZPr06dJp5x/4WLhSf479XwGeZcuWMWPGDDZv3oyDg0O6jT2z8/X1ZeLEidStW5dGjRphYWFBeHg4Fy9epEKFCuzYsQNzc3Nl/6dPnzJp0iSWLl2KVqvlwIEDODk5ydJlQgghhBBCCCGEEEJkEAnvCEHqQsTbt2/JkiUL8K4YodFo2Lx5M0OHDiU+Pp6JEyfStGlTzM3NefHiBVFRUcydO5crV65gZGSEpaUlL168wMnJiR07duDs7CwfhAshMi0/Pz9CQkKwsbHBwsKC69evA7B8+XLatm0LvAvL6Ad4hgwZwuTJk2natCmrV69O0+UF9YvKMTExnD17lqdPn5I7d27atGmDg4MD5ubmPH78mPnz5xMYGEj+/PmJioqiWLFi7N+/nwEDBqBWqzl37hympqYynwuRwXT/hOncuTO7du1i586dfPPNN6jValauXElAQADJycmcPHmSnDlzpnhvd+XKFRYvXkyPHj3Ily9fRp7GF0V/Lj106BDXr1/nwYMH1KlTh+LFi6eYF/8qwPP69WsAsmXLluZLJH4t1q1bR4cOHejQoQM+Pj4UKFAAgKVLl9KpUydcXV05c+YMFhYWwLvlLatUqcLt27epX78+UVFR5M2bV54PIYQQQgghhBBCCCEykIR3hNCzbt06GjVqhImJCQBeXl7kz5+fPn36kJiYyM8//4y3tzevX79m4sSJNGnSBAsLCxISErh69SqLFy/m119/xdTUlHLlytGlSxfs7e3lg3AhRKaiv4TU6dOnad68OS1btuSnn37C1dWVmTNnMnjwYLJly8aSJUto3rw5kDrAM2rUKLp06YKLi0u6jNvLy4uwsLAUtzk7O/PDDz8waNAgbG1tefLkCfPnz2fChAlkz56dAgUKcOnSJdRqNQcPHpQldoT4jLx48YIyZcqQL18+du3axdu3b1m3bh0+Pj4YGBhw/PhxbG1tAYiNjeX169eUKlUK+HMek/dof49+CN3f35+IiAiePXsGgIGBAYMHD6ZLly4ULVpUOUY/wBMSEoKXl1dGDP2r4OvrS2RkJAcPHqRkyZKo1WpWrFiBv78/Go1GCbG9efMGExMTDAwM2LJlC48fP6ZZs2bkyJFDrgUhhBBCCCGEEEIIITKYhHeE+H9t27YlOjqayMhI+vTpg7e3N6GhoQwbNoyhQ4diZmbG27dv2bp1a4oAj64Dj05SUlKKDhLyQbgQIrM6ffo0ZmZmNGjQgA0bNlCsWDFl2+zZs+nXrx/ZsmVj/vz5Hw3wQNotP6W/NFZkZCS+vr506tSJrl27YmFhwZYtW5gzZw6xsbF0796dCRMmYGNjQ1xcHBs2bCA0NJT79+9TvHhx5s+fT758+WSpLCE+I8nJyZQrVw5LS0v27dtHdHQ0Xl5eqYI7ycnJlChRgoYNGxIcHCzvy/4h/bnUz89PCbD36tULa2trpk2bRkxMDO3bt8fT05MSJUoox+7du5cuXbpw+/ZtIiIi6Nu3b0adRqaVlJREw4YNuX79Or///juJiYmsXbv2gyG23377jYsXL9K8eXOMjIyU51Y6hAohhBBCCCGEEEIIkfGk+iTE//vpp584fPgwAQEBrFixggMHDjB8+HB69OiBmZkZWq2WLFmy0KBBAwC8vb3x9vYG4PvvvydbtmwAqZZ+kQKRECIzCgsLw8vLizJlylC4cGEluKMLt/Ts2RMADw8PunbtCkDz5s1RqVSpCoSfMgzzsUJkbGws5cqVw9fXF2dnZwAKFChAvXr16NatG/Pnz6dAgQIMGjQIKysrunTpQuvWrbl37x65c+fGzMwMtVotwR0hPhNqtRqtVkupUqVYsGAB/fv3Z8OGDRgYGHDkyBElrADv5quHDx9SuHBhCSj8C7rgzsKFC5kzZw4eHh54eHhQuHBhXr58yenTp0lOTmbBggW8efMGX19fihcvDkCNGjWYM2cOfn5+NGnSJCNPI9MyNjYme/bsvH79moSEBLZu3frB4I5Wq6Vz5844OjrSqFEjjIyMlOdWrgshhBBCCCGEEEIIITKefEonBO8+zK5duzY7d+7k5cuXHD58mJo1a9KzZ0+cnJxQq9XKh9u6AM/EiRPJli0bPj4+bNy4kVevXmXwWQghRPopWrQo33zzDefPn+fatWs8fPgQeFcA1Gg0APTs2ZPIyEhev37NTz/9xMqVK4E/C8Gf0osXL1Lct64QOXDgQNzd3Tl9+jQtW7bE2dkZrVaLVqvF2NiYYsWKMXXqVBwdHVm5ciXJycnAu9cFMzMzChYsqAQ4JYwpRPpTq9UfvN3Q0BBjY2Pat28PvOuulZSUxG+//Ya9vT3wbqmn6OhoZs6cSbFixWjZsmWazD9fgydPnrBu3TqcnJzo0aMHhQsX5sWLF5QrV45nz54xefJk2rVrx4oVKwgLC+PMmTPKsXXr1uXw4cPky5fvo8+n+N8+1DBX95pVq1YtHj58SLt27Rg0aBCGhoYcPnw4RYht6tSp3Lp1iwoVKpAlS5Z0G7cQQgghhBBCCCGEEOLvkfCOEPxZ7D127Bhv3rzB2NiY06dPs2XLFmXZK10xGlIGeCwsLOjSpQs7d+7MqOELIUS6q1+/PlOmTKFIkSJcvXqVyMhI4M/QjH6AZ/r06Tx+/JjRo0eTkJDwwQLkf3HgwAFatmzJtm3bUtz+5MkToqOjWbduHWfPnuXOnTvAu2KnfgG/WLFi1KlTh7Nnzypz+fsFfin4C5G+1Gp1iqVHY2JiGDt2LMHBwezYsUPZr06dOkyfPh2AV69esWbNGu7cucMff/zB6NGj8fb25u3btyxatIicOXOmeD8n/j61Ws2rV6+UrjqvX7+mfv36PHnyhODgYHr16oWnpyeWlpasWrWKadOmcerUKeV4ExMTQDpS/lv6XyTQp+sGV7t2bZycnNiwYQOvXr3i0qVLODg4AO9CP9HR0URERODq6krXrl3leRBCCCGEEEIIIYQQ4jMkaz8IoadQoUIsW7YMU1NT+vbty/Dhw3n79i39+/dXukmoVCpUKhVZsmShYcOGvH37lqlTp1K2bNmMHr4QQqQL3dJUtWvXJjw8nH79+hEYGIiZmRm+vr7KfKlbuqp79+5kzZqVqlWrkjVr1k86Fo1Gw+HDh9m9ezdVq1alTp06SlEyZ86c7Nq1i/bt23P27FmOHTsGvFtiRBcKUKvVmJub07BhQ+bOncubN28+6fiEEH/fjh072LVrF0FBQSnCBd7e3oSGhqbYd/To0YwcORKA3r17Y2hoiIeHBx07dsTc3JzExES0Wi2lS5dmxYoVODo6pggDiY/70ONkZ2fHggULyJUrFxqNhqCgIM6ePUtAQACtW7fG2NiYcuXK8e2333L79m0WLFhA1qxZKVGiRIrlmcQ/p9FolOdj/vz5HDp0iKSkJMqXL4+HhwcARYoUYcGCBdSrV48nT54wcuRImjRpQs6cOZk3bx7R0dFoNBpWrFiBra1tqqUlhRBCCCGEEEIIIYQQGU+l/dRffxfiC6ErPr/vzZs3mJqacvjwYVq1akViYiIBAQH069cvxYfcN2/eJG/evGi1WpKTkzE1NZWikBDiq6E/h+7Zs4d+/fpx6dIlJkyYgK+vL/Bn9x39uTMt5snnz5+ze/duatasSY4cObhx4wbOzs7K9itXrtCmTRvOnz9Ply5dmD9/PgBJSUkYGxuj1Wrx8fEhNDSUHTt2ULt27U86PiHE//by5Utq1arFyZMn8fPzY/z48QBERUUxePBg3N3d6dChAw8fPiQoKIhff/2VwYMHExISoswxx44d49ChQ5w/f56cOXNSqVIlatWqhZWVlbxH+xciIyNxcXGhUaNGqbbVr1+fa9eucenSJaX7S1JSEkWKFKFLly4YGRnRsWNH8uXLl97DzrS8vLwICwtLcVubNm2YMGECzs7OGBgYcPDgQfr27cvFixeVfUxNTfnuu++YP3++snSZXAtCCCGEEEIIIYQQQnx+JLwjvkr6H1q/ePGCN2/ekJycTO7cuVPsd+TIEVq2bEliYiL+/v4MGDAAgPXr1zNmzBg8PT3p0qVLuo9fCCE+B/oBnr179+Lh4fHBAE96frt/6NChbNu2jfDwcKpVq6bcfvXqVVq3bs358+dp27YtUVFRZM+eHYA1a9bg7e2NpaUle/bswcrKKt3GK4T409GjRxk8eDBHjx5lyJAhTJw4EQ8PD86ePcvixYtxdXUF3oV0AgIC2L59O4MHDyY4OPgvwwjSZeSfO3DgANWrV6dMmTIEBwdTq1Yt4F1A5+XLlxQtWpQcOXKwc+dO5f3zggUL8Pf3Z8GCBcr+EhT59/RfY1evXk2PHj1o164dvXv3JjExkSlTprBixQrq1avH5MmTKViwIAYGBty6dYvY2FhOnz5NlixZKF++PN9++y2WlpbyfAghhBBCCCGEEEII8RmT8I746uh/aB0REcHatWu5dOkSWbNmpXv37nTt2hUHBwdl/yNHjtCqVStevHhBnz59yJ07N3PmzOH69ev8+uuvKbo7CCHE1+ZjAZ4RI0YwZsyYNP/97xcihw4dSnBwMHXr1mXEiBFUrVpV2abfgcfNzY08efIAcPHiRSwsLNi2bRvOzs5S6BciA504cQIPDw9OnjzJwIEDefDgAbVq1aJHjx4prveTJ08yYsQIJcCj68Cj3/HrY10Wxd8zadIkfHx8KFeuHOPHj0/RlczX15dJkybh6+tLvXr1OHPmDDNmzMDU1JS9e/dibW2dgSP/8r3/txsYGMiKFStYu3YtBQsWBODWrVtERkYyefJk6tSpQ3h4OAULFvzo37y8tgkhhBBCCCGEEEII8XmT8I74quh/aO3l5UV4eDiOjo5UqFCBu3fvcujQITp37oynpyelS5dWjjt9+jQtW7bk1q1bGBoaUrBgQbZs2YKzs7N8g1UIkanoFwx1//2/CuD62/ft20fbtm159eoV9+7dw9zcPM2K5/pz+vLly6lfvz7W1tYEBwczfPhwatSoQUBAQIoAj34HHjs7Ozw8PPjmm2+oWLEiefLkkTldiM+AfoDH2NiYgIAAhg0bhkajQaVSKXPKyZMnGTlyJNu2bcPb25ugoCAJ63xioaGheHt7pwrwHDt2jHHjxrFp0yZl3yJFivDzzz/j5OQkQZFPZMiQIRw7dgxnZ2eKFCnCiBEjUKvVGBgYoFKpuHv3LtOmTSMsLIw6deowZcoUJdzzd1/DhRBCCCGEEEIIIYQQnwcJ74ivUkhICAEBAfTq1Yvu3btTrFgxLly4QLVq1Xj+/DktWrRg+PDhKQI8Dx8+ZO3atZibm1OvXj3s7OykyCuEyFT057SEhARevXqFjY2Nsv2vCoD6244cOYKzszMODg7pUjT08fEhLCyM0aNHM2zYMOLj45k+fTojRoz4YIDn8uXLtG7dml9++YVBgwYxadIkAN6+fUuWLFnSdKxCiL/n2LFjDBgwgOPHj9OmTRtWrlwJkCK4AO8CPKNGjWLLli2MHj2akSNHZuSwvzgfei/7fvBGP8ATGBhI3bp1gXdhyCNHjnDy5EkKFCiAu7s7uXLlkvfH/4H+Y//8+XN++uknYmJiUKvVtGvXjvnz52NiYpLiGP0AT/369Zk4cSJFihTJiOELIYQQQgghhBBCCCH+AwnviK/OiRMn6NatG25ubgQGBlKoUCFevnxJuXLleP78OaVLl+bnn3+mRYsWDB06lDJlynzwfuQbxUKIzER/Tps4cSIxMTFcvnyZxo0b4+7uToMGDTAyMvrLue/9oE5aFXD1x3D8+HFat25NixYt6N+/PwUKFAAgPj6eyMjIjwZ4YmNjcXd358KFC/Tp04fIyEgAkpOTMTIy+uRjFkJ82Pvzhv71ffToUQYMGMCJEycYMmQIEydOBFIHeI4ePUpERATjxo3Dyckp/U8iE5g3bx6urq7UqFED+HiAp3z58gQEBNCwYcMP3o8Ed/6+v3o9vXbtGq6urty8eZMJEyawYsUKXF1dWb16Na6urqn2v3v3LtOnT2fChAm0b9+ehQsXyvMghBBCCCGEEEIIIcQXRqpTItPT/2Bcq9Vy9uxZbt++zfTp0ylUqBCvXr2iUqVKxMXFMXnyZIoXL46BgQExMTFkzZqVQYMGpejAoyPBHSFEZqKb0/z8/AgJCcHR0ZGcOXMSHR3Nrl278PLyol+/fhgbG3+04Ph+h520KhzqfvfNmze5ePEir1+/pkePHhQoUEAJAlhYWODh4QHAiBEjAFIEeAoXLkx0dDRt2rRhxowZGBgYMG3aNAnuCJGO9IMeSUlJxMfHY2BgQI4cOQCoWLEiU6dOpX///oSGhmJgYEBwcDCGhoYpAjwVK1akTJkyGBsbSwDvX9i9ezc9evSgTp06mJiYUKlSJQwMDFLM9Z6enjx8+JBJkyYREhKCgYEB9evXT3VfEhj5+3SPbf/+/alRowatWrUCoFu3bmzevJmjR4/i4uKiLBk3Z84c+vXrx7Jly5RrRCdPnjz07t0bc3NzOnToIM+DEEIIIYQQQgghhBBfIEkfiExFo9Gk+Dk5OVn5YPzu3buoVCpatWpFZGQkVatW5e3bt3Tr1o1bt24xevRo2rRpQ9GiRWnSpAkAK1asYNCgQfzyyy/pfi5CCJEe9OfNc+fOsWjRIvr06cO+ffs4fvw40dHRGBkZMWrUKMLDw0lKSlKKuhkpODgYFxcXNmzYQK1atShWrFiqfXQBnsDAQPbu3cu4cePYtWsX8O68CxUqxOrVqylevDiRkZEsWrQovU9DiK+WfnAnMjKS5s2b8+2331K+fHmGDBnC+fPnUavVVKhQgWnTplG2bFkmTpyIr68v8C4koj8PGRsbA0hw53/Qb7qq++9SpUoxatQo9u7dy+jRozl06BBAirne2NhY6cpz5MgRPD09OXDgQPoOPhPatGkTkZGRhIaGKp2mFi5cSKtWrTA1NQUgX758jBw5kh49erB161Y6duzIs2fPUt2Xo6Mjvr6+5MuXD7Vanc5nIoQQQgghhBBCCCGE+K8kvCMyFV1QJzg4mPj4eKWAM3DgQH744Qdu376NtbU1HTp0AODSpUvs27ePJk2a8NNPPyn7Fy1aFFdXVzp06MCtW7ews7PLmBMSQog0pps3z549y9mzZ8mSJQv9+vXD2dmZHDly0KRJE9auXYuVlRVjx479LAI8Wq0WW1tb3Nzc2LBhAwcOHODq1atA6u4/ugDPhAkT2L59O7NnzyYxMVEZf8GCBVm2bBl+fn507tw5I05HiK+OVqtVgjtDhgxhwIABXLlyherVq2Nra8v06dPx8PBg8eLFSoAnMjJSCfAMGzYMkC4vf8f78/S9e/eIj48nLi5OmS+trKzo378//v7+7Ny5kzFjxqQI8CQmJgLw3XffUaFCBdq1a8fz588pWLBg+p5MJlS9enXmzJnD+fPnadmyJdOmTcPLy4vRo0fj4OAAvLteHB0d8ff3p0ePHmzZsuWjAR7da7pcG0IIIYQQQgghhBBCfHkkvCMynfHjxzN06FA8PT2Bd8GdqVOnUrlyZbJmzZpi34sXL/Lo0SOqV6+eouAbExODmZkZAQEBnD9/Hltb2wzvMiGEEGklODiY7777jpiYGEqVKoWbmxvJycnK9tKlS7N27Vqsra1TBXj0uzikFf35V61Wo1Kp6NSpE8OGDaNcuXLExcWxefNmEhISPni8hYUFvXr1Ytq0aYSEhGBiYgK8K3Kq1Wq++eYbxo8fn+p3CSHShu49V1RUFFOnTqVPnz6sW7eO5cuXs2rVKrp168ahQ4fYunUrSUlJAJQrV47IyEgqVqxIUFCQcs2Kj9Nf9mr27Nm0b9+esmXLUrJkSSpVqsTYsWPZsWMH8C7A07dvX0aPHq0EeHSddUxMTEhOTmbKlCk8ffqUyMhIrl+/jr29vcyZ/5GFhQXdunWjVKlSPHjwAAcHB4oVK4atrS3w7jlUqVRotVry5s2bIsDz448/EhcXl8FnIIQQQgghhBBCCCGE+FQkvCMynSZNmtChQwcWLlyIm5sbU6dOxcfHh379+mFjY5NiXzc3N7Jmzcr+/fuJj49Ho9GwcuVKNm7cSMmSJcmXLx8WFhZotVql+CGEEJmJVqulePHi2Nvbs3HjRi5evMjz588xMjJKEcwpVaoU69atI2fOnAQFBTFu3DiSkpJSdbr51NRqtTL/7tq1i+XLlxMbG4uxsTFt27ZlwIABuLi4EBgYyLZt21KEjvRZWlri4eGRajmR97sTyFwvRNrTarUkJCSwevVqChcuTP/+/fnmm29ITk7myJEjbNq0CWdnZyIiIjA1NVWu2XLlyjFp0iQaNmxI+/btM/gsPm/6wZ1mzZoxaNAgdu7ciYuLCw4ODty4cYOAgADc3d2ZOHEiANbW1ikCPEOHDmXJkiUkJyezYMECVq1aRdGiRTEwMCBr1qzy/vg/0H99PXXqFPHx8dSoUeP/2LvPuCjOr43jv12qgIIIKkqzl8QaezcxdmOLJVhi74qoFEFF7NgVQRS72BA1ahR7BQu22GKPvYEVCwJbnhd+dv6LJe0RUXO+b2KY2fWeXefeYc615+bhw4eEhoYSExMDoIRk3wzw9OzZkw0bNjBw4MCPEqIVQgghhBBCCCGEEEJkPJVe7vaJL9DDhw+pUqUKly9fplixYqxfv54CBQooN78N7ty5w6BBg4iKiqJKlSqoVCrOnj2Lra0t+/btw8XFJROPQgghPg6tVsu+ffsYOHAgp0+fZuzYsQwYMABra+u35s3ffvuNatWqkTdvXo4ePUrWrFkzbFzGxeeRI0cye/ZsbGxsmDVrFt9//z2mpqZotVqioqIYMWIESUlJhIeH06RJE2UZRCHEp8H4fAa4ceMGhQsXpnv37oSEhJCSksK6devw9fVFrVZz5MgRHBwc0Ov1XLp0icKFCyuPTUtLw8zMDI1GI+f6OxjP23Xr1iU+Ph5PT088PT2xt7cH4OjRo2zfvp2AgAAAhg8fTlBQEABPnjxh3rx5+Pj4AGBra8vTp09xdnZm//79uLm5vfXZIP4+43Ph+fPn2NjYcOHCBSwsLNi9ezd9+vShZMmSBAUFUb9+fQDl37rhdb927ZqyvJybm1tmHo4QQgghhBBCCCGEEOIDkfCO+OLo9XpiYmJo3LgxRYsW5fz583Ts2JHZs2e/tWwWwOnTp1myZAlLlizBwsKCUqVKERYWhouLC1qt9q2uDEII8bl6s3huLCUlhbi4OHr27Mnz588ZPXo07du3x9LS8q0i7dmzZ8mePTt58uT5KAVcX19fJk+eTIcOHejRowdVqlQB/leglgCPEJ824+upq1evki9fPh4/foy7uzvt2rUjLCyM1atXM2TIENRqNfHx8cqyQampqbi5ueHv70///v0z8zA+O6NHjyY4OJjAwEB69+6NjY0NqampytKBAOvWraNly5YAhISE0LdvX2Xb3r17CQ0NRa/X4+joyLBhw8iTJ49cH/8/GL928+fPZ8OGDZQqVYpRo0YBkJyczOLFi/Hy8norwKPVatm9ezf29vaULVtW+UyXEJsQQgghhBBCCCGEEF8GCe+IL9a2bduwtLRk3rx5REZG4uHhQUREhBLgebPw8PDhQ0xNTbGwsFCWaJDChBDiS2E8p/32228kJCTw8OFDSpYsSYECBbC0tCQlJYXY2Fi6d+9OSkoKI0eOpEOHDu8M8Lz5nBll7dq1dOzYkQ4dOjB06FBcXV3TbX9XgOfly5dMmTKFH3/8UQqaQnxCvL29+fXXX4mMjKRgwYJUrlyZp0+f0rNnT+bNm4eJiQmHDh0iV65cwOvzOygoiOnTpzNv3jx+/PHHTD6Cz8ezZ8+oV68ez549IzY2Fltb2/eGLZctW0aHDh3Ily8fUVFRfPPNN0ow5NWrV1haWirdjuT6+N8zDtD6+PgQHh5OoUKF6N+/P506dVL2S05OZsmSJQwcOJCSJUsycuRIGjRoQExMDN26dcPe3p6jR49iYWGRSUcihBBCCCGEEEIIIYTICFLREp+9NztJGP6/bt26ADg6OpKWlsby5csBlACPiYkJOp2O+Ph4smXLRvHixZXn0Ov1UpgQQnwxdDqdMqeNHDmSBQsWcOvWLQBy5MhBnTp1mD17NnZ2dlSrVo2IiAi6d+/OyJEjAd4b4PkY82RsbCxpaWl07979reAOgEqlUubs1q1bo1Kp6N27N+PHj6dp06YS3hEiExnPGYsXLyY0NJTOnTuTLVs2bG1t8fb2pmvXrowdO5acOXNy8uRJsmXLBryet6Kjo1m6dCmVK1emTp06mXkon53Tp09z6NAhBgwYgK2trRK+MWZ4f1q1asWePXtYsGABp06d4ptvvlHeN0NAxDCXyvXxv2f4fWXs2LFMnjyZ/v3706tXL4oVK5ZuvyxZstCxY0cABg0aRPfu3SlevDgXLlxApVKxbt06Ce4IIYQQQgghhBBCCPEFevfaGUJ8JrRarXIjfOfOncycOZMRI0YwZ84c7t27h0ajoVixYowYMYK2bduyfPlyunXrRnJyMvC6O0/Pnj0ZOnQoaWlpyvNm9BIwQgjxMRnmyaFDhzJq1Ci++eYbFixYwNatWylTpgyrVq2iWLFiPH36FAsLC2rXrk1ERAQWFhaMGTOGuXPnkpKS8tHnxuTkZOLj43FycqJs2bLA62KzMZ1Oh0ql4sWLF5iYmNCyZUsWLlxITEzMO5dKFEJ8HIZzE14vfXXgwAEqVKiAt7c3hQoVQq/X06xZM/r160daWhqFChXi3LlzpKSkKN2z/Pz80Gq1zJ07Fzs7O3Q6XSYf1edFpVIpIY83gzuG7QDm5uZUqVIFvV7P4sWL0el0ylxr2EeujT+M06dPM3fuXOrXr8/gwYPfCu4YZMmShS5durB06VJSU1M5c+YM7u7uHDhwgIIFC6LRaD7yyIUQQgghhBBCCCGEEBlNvo4uPlvGnST8/PyYNWsWL1++VLowzJ49m969e/Pzzz9TrFgxRo4ciUqlYsWKFTx48AAXFxd2795NUlISv/zyyzuLGkII8aXYsmULoaGhdOnSBX9/f/Lnzw+8XjJw165dpKamKsVatVpNrVq1mD9/Pk2bNmXx4sV07dr1o3/TX61WY2Jiwv379zl9+jQlSpRIt93QaS05OZnJkyfz888/4+7uTrNmzYCPs6yXEOLdDKFBPz8/nj9/zvnz52nWrBnu7u7KuZk9e3b69OkDwKxZs6hcuTJff/01T5484cGDBxQuXJj169fj4uIi5/N7GHc3Mu5GqdVq0ev1HDlyhGfPnmFlZfWnr1+1atWwsbEhJSUFtVr9VlBSfBjXrl3j5s2bjBs3DldX1/cuZQavA1ctWrSgZs2aJCcnY2dnh42NDVqtVrrKCSGEEEIIIYQQQgjxBZLOO+KzZShOBAUFMXHiRFq3bs2hQ4e4desWkZGR3L17l8GDB7No0SL0ej2FCxcmKCiIAQMGEB8fz/Lly3FwcODo0aPky5cPrVabyUckhBAZ5/jx47x48YKePXuSP39+tFoty5Ytw9/fH1dXVy5evIidnR3JyclKkbxatWps3ryZjRs3Ym1tnWFjM+6m8fLlS+XPFhYWNGnShJSUFNatWwf8r/uDcec1f39/li5dytOnT9M9rxT6hchct27dIi4ujrCwMPbv309iYiKAsnQpQNGiRZk2bRpr166lZcuWWFtbU6FCBYKDg9m+fTtubm4S3HkP4+DHy5cvUavVSkeWEiVKULFiRU6ePMnVq1cxMTF557Wu4X1ITU3l1atX5MmTB5BOOxnlzp07ANjZ2QFvd5MzvEeGcwVeL2/p7OyMjY2NLO0rhBBCCCGEEEIIIcQXTMI74rN25MgR5s2bR5MmTRg6dCgVKlTAyckJc3NzNBoNuXLlom3btkoBomDBggQFBXHw4EFiYmLYunWrFIWEEF88vV7P2bNnsbW1pXz58uh0OqKjo/H390ev13P48GFy5MgBwI0bNwgNDSUtLQ0zMzOqVatGnjx5MizgaBzCWbFiBSNHjmTDhg3K9ipVquDs7MzIkSOZN2+e8nPDnL127Vo2b95M0aJFlW5CQohPg7OzM5MmTcLDwwMTExMOHjzI+fPnAdJ1dzExMaFZs2asXr2agwcPEh0dTf/+/XF0dEzXaVGkZ7i+bdSoEdWrVycpKQlTU1O0Wi12dnbUqFGDx48f0759e+7fv/9WgEev1yvvw6JFi9BoNFhbW3PmzBni4uJ49uwZqampmXV4XyQbGxsANm7cyPPnz5XPPyBdMKd9+/ZMnTr1rcdLqEoIIYQQQgghhBBCiC+XhHfEZ+2PP/7g7t27dO7cmUKFCqHRaFi1ahWDBw/G1taWw4cPY2dnh0ajUToy2NraUrRoUWrWrImdnZ0UhYQQX5R3LXWiUqmwtrbm5cuXXLp0iV9++QUfHx/UajXx8fE4Ojoqj+3Vqxe//PILL168SPccGTFPGs+//v7+9OrVi+XLl2NhYaEcR5UqVZgwYQLm5ub06NGDgQMHsm7dOq5du0ZgYCDe3t4kJycze/ZssmbNKku9CJFJ3jz3DP9fqVIl+vbtS9OmTdm7dy8RERE8fvwYeDuIYNyFy/Bn43CDeFtqaip2dnZcuHCB1q1b8+zZM2VeHTduHNWqVePMmTP8+OOP3Lt3T9lm6Nqj0+lYt24d0dHRAERGRvLNN9/QvHlzFixYwPPnzzPt2D5X7/ocMvysSZMmlClTho0bN3Lw4EElTKXRaJSlf0NDQzl16hSWlpbymSaEEEIIIYQQQgghxH+I3A0Xn413dX04ffo0er2e4sWLAxAVFaUUpA8fPoyDgwMAly9fJjAwUCkWGZOikBDiS6HT6ZRi+JMnT5TlUwB++OEHUlNT6d69O97e3koXDENwByAsLIzz589To0YNpTtARjJe9mrixIm0a9eOjRs3Uq9ePVQqlTJ+Dw8PFi1aRMmSJZk5cyYtW7Ykf/78jBkzBgcHB/bv34+LiwtarVa6EgiRCYzPvRcvXvDs2TOePXumbK9cuTKDBw/mhx9+YNq0aUyaNOkvr8nk+uyv6fV6zM3NmT9/Pp06deLAgQM0atSIpKQk4HXocsWKFVSsWJG4uDiqVq3K6tWruXjxIlqtlhcvXhAcHIyfnx/JycmsXLmSsLAwRo8eTVBQEM2bN8fe3j6Tj/LzYnwuPHnyhCtXrvDgwQPlPTE3N+fnn3/myZMnDBw4kF9++YVHjx5hamoKvP5dJjQ0FGdnZ1q1aiWfaUIIIYQQQgghhBBC/Ieo9PJ1PvEZMHw7GGD48OF89dVXtG3blujoaFq3bk1wcDBfffUVvXv3fquTBECHDh3YtGkTR44coUCBApl1GEIIkWGMl/9bsGABe/fupVKlSnTv3h1TU1Nu3LhBx44d2bdvH1mzZuX69evY2dkpj1+1ahXDhg3D1taWzZs3kzNnzo8y7jVr1tChQwc6duyIn58f7u7uyrakpCQ0Go1SPD537hxXr17l4MGDWFpaUrZsWSpVqkT27Nll+UMhMolOp1OCNiEhIcTExHDjxg3Mzc3p2LEjVapUoUKFCgAcOnSI8ePHs3HjRvz8/PD29iZ79uyZOfzPnuH1f/XqFX379mXhwoXs37+fqlWrKtfPhqWzYmJiUKlU2NjYkDdvXhITE3n69CllypRh7dq15M2bN7MP57NmfC5MmjSJ5cuXc/LkSaytrSlYsCCTJk2iTp06PHv2jIkTJxIaGoper+ebb76hVq1aHDlyhAMHDmBpaUlsbCxubm7pnlMIIYQQQgghhBBCCPFlk/CO+KwEBQURFBRE27ZtmTdvHteuXaNixYqo1WpsbGywsLDg2LFj6QpB8+bNIygoiEaNGjF9+nQsLS0z8QiEEOLDMy7u+fr6Mnv2bOzt7QkLC6Nhw4bKfkePHsXDw4PLly/TsWNH6tSpQ8GCBVmyZAkbNmwAIC4u7qMWDL29vQkNDeXQoUOULFkSnU6HTqcjJCSE6OhoHj58SIUKFViyZMl7n0OKm0JkviFDhjB16lQcHBxwdXXl7NmzpKamUrp0aQYPHoyHhweQPsATEBCAl5eXdHf5fzIO8Fy4cIFSpUq9tU2n0xEZGcnhw4fZsWMHJiYmlChRgjp16tCiRQty5MhBamoq5ubmylJN0vXl7zP+osGQIUOYNm0alStXpn79+ty9e5e1a9eSlJREYGAgPj4+JCUlsXHjRhYtWsTOnTsBcHJyonr16kyePBlnZ2cJpQohhBBCCCGEEEII8R8j4R3xSTO+aZ2QkMAPP/xA6dKl8fb2VjroLFiwgG7dugGvgzpdunRRHr948WLGjRuHmZkZ27dvx8nJKd3NdSGE+JIMHz6cCRMm0KtXL3r27MnXX38NpA+3HD16lKCgIPbs2cOLFy8AsLKyokaNGsyZM0dZfupjFAx1Oh1t2rRhw4YNXLx4ETc3N9asWUNERATbtm3Dzc0NrVbLrVu3aNu2LcuXL8/wMQkh/h7jeWLjxo106tSJrl270rVrV4oUKUJ8fDzr169n/PjxuLi4MHnyZFq1agVAfHw8wcHBrFu3jvHjx+Pj4yPXZv9Pb87bxte7b2579eoVJiYmmJmZKT+TEOSHsXjxYnr37k3nzp0ZNGiQ8vuKv78/EyZMoESJEhw8eBArKyvlMb///jupqam4ubmRJUsWLC0tJbgjhBBCCCGEEEIIIcR/kGlmD0CIP2O4ab127VpevHjB8ePHGTduHAUKFFC+FdymTRsSEhLw9/fHy8uL2NhYChYsyKFDh9i7dy/29vZs3boVJycnuREuhPhibdmyhRkzZtCuXTsGDRpEvnz5lG13795Fr9dja2tLuXLlmDdvHg8fPiQ+Ph4TExPKli2Lm5sbNjY2H2WeNBSJ1Wo1FStWZM2aNVSrVg17e3vOnTuHg4MDixcvpnbt2tjZ2VG2bFn27NnDH3/8Qf78+TN0bEKIv8cwTyQmJqJWq8mRIwc9e/ZUwgoVKlSgbNmy5M2bl379+hEeHk758uVxd3enQoUKDB48GFtbW3766ScJ7vxLxgGdN+dt49fUsM2wv5mZmRLUMZ6Pxf/ftm3bsLe3p0ePHhQoUICUlBR+/fVXli1bRqFChdi5cydWVlZoNBpMTV//Kl6sWLF075der5ffV4QQQgghhBBCCCGE+A+Szjvik7dx40aaNm1KuXLlePXqFYcOHcLKyipdgTktLY21a9fi7+/Pw4cPSUpKomDBglSrVo3Ro0eTN29eCe4IIb5okydPxtfXl7i4OCpVqoRGoyE5OZlZs2axaNEikpKScHZ2ZvXq1bi7u7/zOTKqM9lfdXTw9/cnOjoae3t7ypQpw8iRI8mVK5eyvXLlyuh0Ovbs2UOWLFk++PiEEP+Or68vU6ZMoXz58uTJk4c1a9YA6eeSZ8+e4evrS3h4OOvWraNp06bK49PS0jAzM5NrtH/B+DU+f/48dnZ25M6dO5NH9d/y5mfbkydP+Oqrryhfvjy//PILaWlprFmzBl9fX9RqNfHx8Tg6OgIQGxtLrly5KFSoUGYNXwghhBBCCCGEEEII8YmRr1iKT95XX32Fp6cnZ8+e5cyZM0RFRQGvv0Ws0+kAMDMzo02bNuzbt4/jx4+za9cuDh48SFhYmAR3hBD/Cbdu3UKv15OQkADAsmXLaNKkCQEBAVhaWpIvXz6OHTvGDz/8wLNnz975HBkR3NFqtUpxc8uWLUydOpXu3buzZMkSjh07BsC4cePYtm0bu3fvZubMmemCO8uWLePixYtUqFBB6VIghPg0WFtbY2JiwunTp7l79y4vXrxQrs0MsmbNSr169YDXXUl0Oh1arRZAWbZJrtH+GePgzo4dO2jZsiWjRo0iNTU1k0f232Ec3ImPj+fFixfY2dlhbW1NQkIC9+/fZ8OGDe8M7gAMGDCAoKAgNBpNZh2CEEIIIYQQQgghhBDiEyPhHfHJMhR/8ufPT//+/RkwYACmpqYsXryYEydOAKBWqzFuHpU3b17y589PrVq1yJEjB5aWltJ6Xgjxn1C3bl0AWrZsiYuLC507d+bq1ausWbOGrVu3cuDAARo0aMDFixe5dOnSRxmTTqdT5l9fX19at26Nv78/y5cvp1OnTrRu3ZpZs2YB4O7uTpYsWZRiPsD8+fMZNWoUOXPmxN/fP902IUTmMVx7jRgxgvHjx/Py5UsOHTpETEwMarUalUqFXq9Xggk1a9ZEpVIpyzPJddm/Zxzc2bVrF8OHD+fSpUt06NABc3NzpKnqx2EI7nh6etKhQwe2bt0KvO4Ud+bMGSZPnszgwYPfGdyZOHEi165do3bt2hJKFUIIIYQQQgghhBBCKCS8Iz4Zb35T27gNff78+enWrRv9+vVj3759TJ48md9//x34604RGdFJQgghMsOb86Sxhg0bsmLFCipXrkzp0qXx8fHh+PHjNG/eXFlKRa1W4+bmhrOz80cZr2EeDwoKYtKkSbRs2ZI9e/Zw/fp1li9fTmJiIgMGDGDu3LnKY3Q6HcePH6dDhw4MGzYMlUpFTEwMTk5OSrcOIcTH9ebco1KplC4vgwYNYtq0aQDpQgw6nQ5TU1M0Gg2RkZHo9Xry5csHIAGTf+nN4I6fnx8nT54kNjaWypUr8+TJE/bs2cPNmzczeaRfLuNzYc2aNURGRvLtt99SqlQpALp06UKOHDmYMmUKr169Yv/+/emCOytXriQiIoISJUrQvHnzjz5+IYQQQgghhBBCCCHEp0u+6ic+CcbLWm3dupXLly9z7949KlSoQMmSJXFzc6NAgQL07dsXnU5HSEgIer2eYcOGUbx4cSB9QUMIIb40xvNkbGwsFy5c4M6dO+TPn58aNWrg4uJCmzZtqFOnDjly5HhrucBVq1Zx7Ngx6tSpQ7Zs2T7auE+ePMncuXNp3LgxQ4cOpXDhwqSlpeHg4ICJiQmFCxemZcuWyv5qtZrIyEhWrFhBx44dGTt2rBLckW4dQnx8xufe77//TlJSEiVKlEjXMcTT0xO9Xs+gQYNo0KAB8+bNo1KlShQvXpz58+czZ84c3N3d8fDwACRY/W+8K7hz5swZdu/eTYUKFXj69ClLlixh4MCBTJgwAR8fn0we8ZdHr9en+3LB48ePyZEjB56enhQoUACAcuXK0blzZ+bOnYupqSmHDx/m8ePH5MmTh1mzZrFgwQIAli5dir29fbrlt4QQQgghhBBCCCGEEP9tKr189VVkMuNihK+vL1OmTEn3rdZy5crRrVs3evToAcCVK1cICQkhJCSEtm3bMmzYMIoVK5YpYxdCiI/BuLgXEBDArFmzePbsmbK9TJkytGvXDi8vL2W5GuPi+OzZs5k6dSoAe/bsIW/evB808Hj//n1y5cr1zm3R0dG0bt2aX3/9lYYNG6LRaFi9ejV+fn6o1WqOHDmCg4MDqampPHr0SOkSdOLECQoXLoy1tbUEd4TIJMbzhL+/PyEhIbx48QIXFxfatm1Lr169lG46ADNmzMDLywuAXLly4ejoqAQXVq1ahbu7u5zP/8KfBXcqVqzI06dPWbp0KcOGDePrr78mNjY2k0f8Zevfvz9RUVFUrVqV/PnzM3nyZOB/QbenT58SHh7O3LlzuXr1Kubm5sDr0Frp0qVZuXIlbm5uci4IIYQQQgghhBBCCCHSkc47ItMZihFTpkxh5syZtG3bls6dO/PkyRMOHDjAjBkz6N27N0+fPsXb25sCBQrg6ekJQHh4OE+fPmXatGkUKlQoMw9DCCEyjCG4M3z4cCZMmEC7du3o0KEDGo2G7du3s2jRIvz8/Hj69ClBQUGoVCq0Wi2HDx9m9OjRnDhxAkdHRzZu3EjevHk/aMEwPj6eqlWrMmfOHLp06aL83BA4unLlCgAuLi6kpaURHR2tBHfi4+NxcHAA4MGDB0yYMIF+/fpRuHBhypQpA7wuWktxU4jMYbhGmzhxIsHBwdSuXZvChQtz8uRJJk2axOnTp5k8ebLSBdHQgcfb25vU1FTq1KlDYGAgOp2O7NmzS1jhX/i7wZ2hQ4dStmxZ9u7dC4BGo0nXHUn8e2/+u01JSSExMZENGzbQoEEDkpOTsbS0xMTEBL1ej62tLQMHDqRFixasXLmSu3fvYm5uTs2aNalZsyb29vZyLgghhBBCCCGEEEIIId4inXdEpnnzpnWDBg3IkiUL06ZNw83NTfn5hg0baN68ORYWFsydO5f27dsDcOPGDYKCgti+fTsnTpwgR44cH/0YhBDiYzlw4ABNmzalRo0aTJ06VZknk5OTiYuLo3379rx48YJp06bRrVs3ABYuXMigQYPw8PBg+PDh5M6d+4MXDNesWcOAAQN49OgRERERyhxtsH79epo3b868efNwcXGhW7duSnDH0dFR2e+nn35i37597N+/n/z583+w8Qkh/j29Xk9qaioNGzYkd+7cTJw4kbx585Kamkrv3r1ZuHAhNWvWJDQ0VAnwAEyaNIlhw4aRPXt2Vq1aRc2aNdFqtajValky61/avXs3vr6+Etz5yIzDU4GBgTg6OtK7d288PT1ZsGABOXLkICYmhq+//lp53f+qs50slSWEEEIIIYQQQgghhHgXuWsoMo2heDx48GBCQkIAaNeundJGHl7fMP/hhx+IjIwkLS2NdevWkZycDICrqytBQUGcOnWKHDlypFtqSwghvjTXr1/n0aNHyjxpmPOyZMlCnTp1mDNnDiYmJqxdu1bZ1rlzZ06cOMHkyZMzJLgD0LJlS2bNmoWbmxudOnUiMjIy3fbChQuTN29e+vXrR6dOnTAzM+PQoUPpgjsREREcPHiQxo0bkydPng86PiHEP2N8PaVSqUhOTubChQt06dKFvHnzotPpMDc3Z/78+XTv3p29e/fSt29ffv/9d+Vx3t7ejB8/noSEBNq0acPOnTsxMTGR4M6/FBMTw5AhQzh//rwEdz4yw7/ZMWPGMHbsWI4dO0ZaWhoTJ06kc+fO3L59m9atW/P06VNMTU3RaDTKY973HRkJ7gghhBBCCCGEEEIIId5F7hyKj864KPT7778zbdo0PD092blzJ/fv3wf+F+wx3Pxu3Lgx33//PevXr+fixYvK452dnbGzs0Ov18uNcCHEF8lQ/Ltw4QJ6vZ7Hjx8DvBVYrFq1KlWrVmXLli3Ex8crP3d3dydLliwZsvyUYQzNmzdnzJgxFCxYkE6dOrF06VJln2LFitG3b19evXrFvXv3mDRpErly5VK2R0ZGMmnSJLJly8bIkSOxtLR8b8FTCJGxDN1xAA4dOsSvv/7KjRs3cHJywsrKCr1ej16vV0LWc+bMeW+AZ9CgQUyePJkHDx7Qvn17Nm3alCnH9CW4du0aJ06cYPv27RLc+UgM/8bh9bKO27dvp2PHjgQFBWFpaYmVlRWTJ0+mT58+nD9/nqpVq6YL8AASVhNCCCGEEEIIIYQQQvwjknYQH5Vxm/jz589TvHhxVq1aRaFChdBoNJw+fVop2hr/N2vWrJQrVw6dTkdCQsJbzys3x4UQXyrD/Fa9enVMTU05cOAAAKampkp4Rq/X4+DgQK1atQB48eLFe5/nQzIO2fz444/06tWLggUL0qVLF6KiopRtfn5+DB48GL1ez08//UTv3r0ZPXo0TZo0YcCAAaSmprJhwwacnJzQarUypwuRCYwDfn5+flSvXp0ffviB0qVLc+zYMc6ePYtKpcLExAS1Wv3OAI+Hh0e6kPWgQYOYOHEi9+/fJyYmJlOO63Pwvu6RhhBI7969uXHjBhUrVuTJkycsW7ZMgjsZzHAuLF68mAMHDnD69GnatGmDq6sr8Pr1zpIlC5MmTaJPnz78/vvv7wzwCCGEEEIIIYQQQgghxN8ld3jFR2UI7vTr149du3YRHh5Oq1atSE1Nxc/Pjzlz5lC+fHm6dOmCSqVCr9crRdxbt25hbW2Nvb19Zh6CEEJkGOOA45vy5ctH8eLFWbhwIWXKlKFfv36o1ep0Bdtz586RLVs2nJycPspYDcXNUaNGERcXx7Fjx7C2tkar1dKuXTu0Wi0//fQTAJMmTcLV1ZUVK1awYMEC0tLSyJcvH82aNWP06NHkzZs3Q5b1EkL8PYbrrZkzZzJ9+nR++OEHKlasyI0bNwgLC6Nnz57kyZOHhg0bolKplACPiYkJc+bMISkpifXr12NnZwf8bz4bNGgQxYoVo0GDBpl4dJ8u43lv7969PH/+nLS0NJo2baqENNVqNXny5CElJYW5c+cycuRIKlWqxK5duwAJ7mSU7du307lzZ1xdXbG2tqZYsWLA63/bpqamaLVaJcADEBYWRs2aNdmzZ49yHgghhBBCCCGEEEIIIcTfJZ13xEdh/I3iFStWsGLFCipXroyLiwsA7dq1Y/LkyeTKlYtu3boRGhrK06dPlULS+vXriYmJoWTJkhQqVChTjkEIITKS8XI1Z8+e5cKFC+mWoMmfPz9jxowBYMCAAUyZMgVAKdj+8ssv7Ny5k4oVKyqdATKSYaz+/v6MHj0aJycn5s+fz/Lly/Hy8kKlUtGhQweWLVumPKZ///78+uuvnDhxgri4OA4fPkx4eLgEd4TIRG92fYmLi+Pbb79l+vTp+Pj4MGvWLKZNm4Zer6dTp05s2bIFIF2AB15f3926dYucOXMqgRPDNkNw530dZv6rjLsd+fv7U79+fZo0aUKLFi2oVq0av/32m3ItrFarsbCw4NWrVxQsWFCCOx/BV199RUBAAHq9ntu3b/PLL7+g0WiUzz8TE5N0AZ7+/ftz6tQpWrZsKcs/CiGEEEIIIYQQQggh/jGVXu4sigxm3D0HIDQ0lLlz57J69WoKFy6crmAbFRWFl5cXd+/epVatWhQrVoybN29y5swZ9Ho9e/bswc3N7U+7UwghxOfgwoUL5MuXD3Nz83TzYFBQEBERETx//hyAIUOG0L9/f2xtbQFYtWqV0s2mVq1aFClShEePHrFnzx5MTU05ePAgrq6ub829GeHIkSN8++231KlTh5kzZyqBTIAlS5YQGBjIzZs3iYyMpG3btu99no8xViHEnxswYAB2dnacOXOGn376iVatWpGWloaZmRkAISEhDBw4EHt7e5YuXUr9+vWB1+evcScuOZ//ueDgYPz9/alTpw6NGzdm69at7Nq1C2dnZ+bMmUPNmjXfed0rwZ2Md/fuXcLDw5k5cyb58uUjIiKCb775Jt0+hs/wly9fMm7cOLp164a7u3vmDFgIIYQQQgghhBBCCPHZkvCO+Gg8PT3Ztm0b9vb2VKlSRWkxD+mXilm1ahW+vr7cvHkTJycn+vTpg1qtpn379jg7O0t3BiHEZ+/s2bOUKFGC1q1bs2TJEszNzQEYPnw4Y8eOpWTJkpQqVYotW7aQmJhIx44dGTVqlNJRZ/v27cyaNYv4+Hju37+Ps7MzZcqUYdasWbi4uHy0eXLDhg00a9aMsLAwevXqhU6nU5YTAZg7dy69evXCxMSEyMhI2rRpk+FjEkL8c5cuXaJChQo8ffoUa2trpk+fTteuXZVOOYZrNOMAT2RkJPXq1cvMYX+2jOfoV69e0bBhQ/LkycPo0aPJly8fSUlJLFmyhLFjx2JlZcW8efPeCvBISOrD+asvBdy5c4c5c+YwadIkKlWqRGhoqLKElsGbn7sSrBJCCCGEEEIIIYQQQvxTckdRfBR6vZ6LFy9y4cIFbGxsKFq0KIDyjW61Wq3cOG/Tpg1arZbhw4dz9epVSpYsSePGjdPtL4QQnzO1Wk39+vWJiorCysqKsLAwEhMTWb9+Pb169WLIkCHkz5+fY8eOERoayqJFi9BqtYwdOxZXV1e+//57ypYty8uXL7l48SIFChTA0dERa2vrTAk4pqWlAa+X0TE1NVXm8x49erBr1y6ioqLo1KkTSUlJdO/e/aOOTQjx1woVKkRkZCQBAQGcOnWK3377DUC5PjOc0/379wfA29ubBg0asGvXLmrVqpV5A/9MGeboKVOmUKJECR49eoS3tzf58uVDo9GQLVs2unTpgrm5OYGBgXTr1u2tAI8Edz4M48/MgwcPcvnyZRITE8mdOzdNmjQhS5Ys5MmThx49egCvuyT17dv3rQDPm5+7EtwRQgghhBBCCCGEEEL8U9J5R2Q4wzeDtVotHTt2ZMWKFeTJk4eDBw++1SHC+JuvK1euZODAgSQkJLB06VLatWuX7vmEEOJzdv78eYYPH86aNWvo06cPnTt3plGjRqxbt47KlSsr+126dInJkycTERFBu3btGDduXLrlqYx97PnxwIEDVKtWjaJFi/LLL79QuHBhZVtKSgoWFhZMnDiRiIgIXr58iampKefOncPKyuqjjVEI8eeMr8M2b96Ml5cXly5dYsqUKXh5eQG81YEnODiY0NBQDhw4gLOzc+YM/DO3d+9eateujY2NDZaWlvz6669UqFAB+N9cnpyczNKlSwkMDMTa2pp58+ZRo0YNWTr2AzH+vSMgIICwsDCePn2qbK9atSodO3akffv2ZMmSRenAExwcTJUqVd7ZgUcIIYQQQgghhBBCCCH+LQnviA/uXcVjQ2FIp9PRsWNHli9fTtmyZdm0aRO5cuV6b4AnKioKT09P7t+/z/Lly2nbtu1HPx4hhMgo586dY8SIEaxZs4YSJUpgYmLC4cOHMTMzSzcvGgrpc+fOpV27dkyYMIG8efNm8uhf69atGwsWLMDLywsvLy+cnZ3TLRfSuXNnnj59So8ePShVqhROTk6ZPGIh/rveXB7oXcsFxcTEMGDAAK5cucKMGTOUbjtvBnieP3+OjY2NLGf6L6WkpDBlyhQWLVrE5cuXleUHDd4M8IwZM4akpCQ2b95MlSpVMnHkX57hw4czbtw4fvrpJ9q0aYNarWbNmjVs2LABlUrFkCFD8PT0xNLSkrt37xIeHs7UqVMpWLAgUVFRFCpUKLMPQQghhBBCCCGEEEII8QWQ8I74oIwLOElJSTx48ABra2scHBzShXPat2/PypUrqV69OlFRUX8a4Fm9ejWenp7cu3ePAwcOUKlSpcw5OCGEyABnz55l1KhRbN68Ga1Wy7Zt26hWrRqQPgxpCPAsWLCA+vXrM2fOnA8ehHkzfPlnnXwM8/T58+fp0aMHR44coWPHjvTv35+vv/4agDVr1uDn54eHhwdBQUEAUugXIpMYn3urV6/m6NGjHDlyhMKFC1O3bl1atGih7Pt3AzzSDfHfMbwXhgDP1KlTsbKyYt26dXzzzTfKfsYBnrlz57J48WJ+/fVX8uTJk4mj/7Ls27ePli1bUqtWLaZMmYKrqysAjx49Ys+ePQwcOBCNRsP06dNp1aoVKpWK+/fvM3HiRH755RcOHjxIzpw5M/kohBBCCCGEEEIIIYQQXwIJ74gPxjhwM3nyZFasWMGJEyewtramWLFiTJw4kTJlymBra4tWq6VDhw5KgGf16tXkzJnzvQGexYsXc+TIEWbNmpVpxyeEEP9fxvNaSkoK5ubmqFQqTp8+zZgxY1i9ejWtW7dm/vz5WFtbA+mL45cvX2bEiBHs27ePkydPkiNHjg82NuO/59q1a7i7u//tY4qNjWX06NHs3LkTBwcHmjRpwr179zh06BA2NjbExcXJ0jpCZCLjucfb25uwsDBUKhWWlpY8evQIgD59+tCrVy8lfLd161b69+/P5cuXmTlzJv369cu08X/O3tXdyFhKSgrTpk1j7NixODo6sm7dOkqVKqVsN8zNr169QqvVYm1tLSHIf+BdATPj92TJkiV07dqVlStX0rJlSwy/GqtUKtLS0lixYgX9+vWjdu3arF+/XnmOxMREzM3NsbW1/cv3WAghhBBCCCGEEEIIIf4OCe+ID8L4xviQIUOYNm0aFSpUoGHDhly/fp1t27aRlpaGl5cXnTp1ImfOnOk68NSuXZvIyMi3uki862a43CAXQnyO3ux6cfLkSSpWrEijRo1Qq9X8/vvvjBw5kujoaDp37szs2bMxNzcH0s+xV69exc7OjuzZs2fIfNigQQNevnxJaGioUsT/q+4aer2ehIQEpk6dyvLly7l9+zYODg6UKlWK+fPn4+rqKsVmIT4B48ePJyAggJ49e9KpUyfy58/P1q1bmTVrFvHx8bRu3ZpRo0ZRuHBhALZt28bAgQM5f/48ERERdO3aNZOP4PNiPO/t3LmTCxcucPPmTXLkyEGHDh2ws7PDwsLibwd43vyz+HPGn5GpqalcunQJNzc3bGxslG3+/v5MmDCBVatW0apVq3TLPgLcuXOHn376if3793P06FHKli2b7u+Q90MIIYQQQgghhBBCCPGhSAJCfBCGm9aLFy8mNDSUnj17smTJEoYPH86sWbPo2LEj9+/fZ8OGDem+oRoZGUm7du3YvXs3/fr1480smVqtfufPhBDic6LT6ZQCrp+fHz179iQ8PByVSoVGowGgePHiBAUF0bJlSxYuXEivXr1ITU0FXs+xhrkwX758ZM+eHb1e/8Hnw9TUVGrWrEl8fDyBgYGcPXtW+fv/jEqlIleuXAQHB3Ps2DEuXrzI77//zvr16yW4I8Qn4saNGyxatIhatWrh7+9PxYoVcXR0pH379oSFheHh4UFUVBSLFy9WHlO3bl0mTpxI1apV+e677zJx9J8f43l/6NChtGjRgn79+hEcHIyPjw/Vq1dn/vz53LlzBwsLC7y8vAgICCAxMZHmzZtz6tQp5bmM52AJivw9xsGdGTNmULduXb777jsGDRpESkqK8jpWrlwZgC1btgBgamqqLA+n1WrJkycPjRs3BuDVq1dv/T3yfgghhBBCCCGEEEIIIT4U07/eRYi/LyYmBkdHR3r37k2hQoVITU1l06ZNLF26lAIFCrBu3TosLCyUIrRarWbRokXY2toyZMiQd94Al5viQojPnaGAOGLECCZNmkSfPn3o2rUrpUuXTrdfsWLFGDVqFACLFi0CIDw8XFley1hGzI3m5uZ4enpiY2ODr68vL1++JDg4mJIlS/7lYw2FUkdHR3LmzKn8XK/XS3BHiE/AvXv3uHTpEm3atMHFxQWtVguAiYkJZcuWpW/fvhw4cIDx48fToEEDqlWrBkDjxo35/vvvsbCweKsriXg/w7wfGBhIcHAwnTp1olWrVuTKlYv58+ezceNG/Pz8SEhIoFevXuTOnRsvLy9UKhXBwcFUq1aNQ4cOUbx48Uw+ks+PcXCncePGxMXF4eTkxIgRIyhZsiQWFhbKvl999RVubm4sXLiQMmXK0K9fP9RqNampqUr3uzNnzmBra0vu3Lkz5XiEEEIIIYQQQgghhBD/DdLCRHwQOp2OpKQkdu/eTcWKFSlRogQpKSmsW7eOQYMGoVarOXDgAI6OjgAcOHCApKQk4HXRaNasWbi7uysdKIQQ4kuzf/9+QkNDadOmDUOGDFGCO292FzMEeFq2bMmiRYvw8PAgLS3to40zS5YsdO3aldGjR7N161aSk5P/1uMMhdKPETISQvxzhtDNixcvgNfXX8bBusqVK9O7d28Arl+/DvxvfjKEHSS4836GMJSxI0eOEBERwQ8//MDIkSNp0KABZcuWZerUqYSFhVG4cGFmzJjB7t27AZQOPP369cPJyQk7O7uPfBSfP+OudN9//z2xsbEMHjyY2NhY+vTpo4TS9Ho9er2e/PnzEx4eDkBAQACTJ08GUII769atU36/yZUrVyYckRBCCCGEEEIIIYQQ4r9CwjvibzG0jzd4M2SjVquxsrIie/bs3L9/nydPnrB582Z8fHxQq9XEx8crwR2A9u3b07t377eK1lIUEkJ8qU6fPs3jx4/p1asXbm5uys/fFW4pVqwYQUFB1KlTh7179/Ls2bMMHdubc3GWLFno2bMnly9fpmLFihn6dwshPizjazbjc9vKyoosWbKwaNEiDhw4kG4fQ0CwVKlSANy5cweQ8N3f8ccff5CWloaJiclbc+mtW7e4d+8ejRo1wtXVFXgd8rGwsKB+/fp4e3uj1+sJDg5W3gNzc3OGDx/O0aNHyZMnzztDQeL9DP9mBw4cyOHDhxk+fDgDBw7E3t4+3WupUqmUJSnr1avHsmXLePbsGT4+PtSrV49BgwbRrl07evfujU6nY+7cuVhbW7/1HgshhBBCCCGEEEIIIcSHIuEd8ZeMv8F68eJF0tLSlJCNr68v0dHRwOvgTfHixTl69CgjR47E09MTExMTDh8+nC64M378eJ49e0bt2rU//sEIIcRHZiikHz9+HAB7e/t37mcoKiYmJqLVailevDihoaGcO3cOe3v7t0KUH4pWq1WKnX/88Qe3b98GwNramvz58wNvh3uEEJ8mrVarXLNt2rSJ8PBwzp8/D0DRokXx9fXl8ePHhIeH8/vvvwOvQwxmZmYAxMXFYWFhQZkyZTLnAD4zR44coWDBgnTr1i3dXGpw//59AOX1NYR84PV1c7NmzahVqxanTp3i8OHDwOvPDDMzM7JmzSrLDv5Lf/zxB+vWraNy5cp07doVGxub976Whvfsp59+Yu/evVSpUoWTJ08yffp09u7dS7ly5Th48CCurq7vfI+FEEIIIYQQQgghhBDiQ5HwjvhLhpvUzZo1o3bt2kqxZ+DAgUyaNInTp08rSzAMHToUe3t7Zs6cSVpaGnv37iVnzpzKc61atYoFCxZQokQJWrRoITfAhRBfPEMhPV++fABcuXIFSN/BzFBU1Ol0jBw5ktjYWAAKFSqEg4MDOp1OeZ4PSavVKsXMadOm0bRpU4KCgrh79266/WSuFuLTp9PplPM5ICCAzp07M3jwYG7fvk1KSgoAnTt3pkWLFixbtoyAgAB27typPDYqKorIyEhKlChB2bJlM+04Pidubm6o1WoeP36cbnlDw/xeoEABABYvXkxycjJmZmZKEDM1NRULCwuaNm0KoCwnazzXy9z77xw8eJCbN2/i5eWFnZ0dOp3uT19LQ0C1evXqrF69mvj4eDZv3kxsbCwrV67ExcUl3eelEEIIIYQQQgghhBBCZAQJ74i/5cWLF5QsWZJXr17Ro0cP2rRpw8yZM/Hx8aF79+5YW1sDr5d6GTBgAE5OTmTNmpW9e/dy8eJF7t27R1BQEN7e3qSmprJkyZIM7SQhhBCfCkNRsGzZsqhUKnx9fXn48CGmpqZoNJp0RcXJkyezaNEinj59mu45MiK4Y1zo9/b2ZtiwYWTNmpUmTZrg5OT0wf8+IUTGMswT/v7+TJgwgZYtW7Jnzx6+++47LCwsAHBxcWHYsGG0bduW9evXU7duXRo0aECVKlXo3bs3aWlpREVFyTXa36DRaMiZMyePHj0iMjISS0tLNm3alK5DZc2aNalQoQJ79+4lODiY5ORk1Go1KSkpmJubAxAfH4+VlZUS9BH/nuHz9uLFiwDK7yd/9RmqUqk4c+YMT548wcnJCVdXV+rXr4+7u/ufdu0RQgghhBBCCCGEEEKID0nCO+Jvsba2xs/Pj/Hjx/Pbb78RHR1Nu3bt6Nu3L87Ozuj1evR6PTY2Nvz8888MGTKEV69e0bFjR8qXL0/hwoWZNGkSefPmZd++fco3WDOiIC2EEJ8SQzCnbt26tGjRgosXL9K6dWsSEhIwNTVV5sE1a9awaNEiypYtS7Vq1TJ8XIa/d+zYscycOZPu3buzdOlSmjRp8s79Dct6CSE+XZs2bSIkJIROnTrh5+dHhQoVlG2GYEPp0qUJDw9n5syZlChRgkOHDvH8+XOaNWvGwYMHcXd3l2u0v8HU1BStVku2bNnIli0bEydOpEmTJvj6+ipdeMzNzZkzZw5ubm5MmzaNESNGkJycrISp1qxZw5YtWyhfvjx58uTJzMP5Ihg+b62srID/dTP6M1qtFp1Ox+zZswkICEjXFe/N5xVCCCGEEEIIIYQQQoiMpNIb7uQL8Sf0ej0qlUr5Nre5uTkFCxZk5cqVFC9e/K0CT0pKCnfu3CE8PJyHDx9ibm5OrVq1qFOnDvb29tJ6Xgjxn2JY9iotLY06deqwf/9+3N3dad++PYULF2bv3r1s2bIFgNjYWNzc3DJsqSxjJ0+epFmzZhQuXJjQ0FAKFiyobDtw4ADJycmkpqbSoEEDAJm7hfjEDR8+nAkTJnD48OG/tfTVs2fPSEtLw9bWFr1erwRS5Dx/m+FaGF7P6W92Y7lz5w7VqlXj2rVreHl5MWHCBMzMzEhLS2P37t306dOHP/74g6+++orq1atz9+5dDhw4gJmZGXFxcbi5uaX7O8S/t3TpUn7++WdatGjB7NmzcXR0fOd+htc7NTWVAgUK0KBBA+bOnfuRRyuEEEIIIYQQQgghhBCvSXhH/COLFy/m2rVraLVaQkJCcHd3Z86cOZQvXz5dQePPCs4foyAthBCfGo1GoyyV1adPH7Zs2cKtW7cAyJo1K5UrV2bu3Lm4nRqKrwABAABJREFUurp+tOL59u3badiwIdOmTaNfv36kpaVx8+ZNQkNDCQ0NRafTodFo6N+/PzNmzMjw8Qgh/h2dTodOp6NOnTqcOHGCq1evkj179reCIIa55enTp9ja2mbSaD8/f3btumzZMsqWLUuxYsVITEykSpUqXLlyJV2AR6fTcfPmTfr168dvv/3G7du3cXFxoWzZssycOVPpSCmhqQ/jxYsXfPfdd1y5coVZs2bRokULzMzM0u1jCO7o9XpGjRrF5MmTWbx4MS1atJAQlRBCCCGEEEIIIYQQIlOYZvYAxKfrXYWKn3/+Ga1Wi0ajIVu2bIwZM4aePXsyd+5cypUrh0qlUh5z9+5dnJyclMcaboRLcEcI8V9kCO6YmpoSHh7OlStXOHfuHK9evaJIkSIUKFAAGxubDCvgvqsYmZiYiFarZcuWLVStWpWYmBhWrlzJlStXaNCgAeXLl2fmzJmEhIRQt25dGjVq9MHHJYT4/1Or1ajVavLnz8/BgwdJSEh4q9OhTqfDxMSEV69eMW3aNLp06YKrq2smj/zzYLh2rVixIl9//TXz588HoHfv3kRGRjJ//nzy5cuHo6MjcXFxVK1alWnTpgEoAR43NzfWrVvHo0ePuHbtGgUKFCBLlixYWVlJcOcDs7Cw4McffyQgIIDhw4dja2tLzZo1yZIlC3q9XjkXANauXUtkZCTly5fn22+/BWSZLCGEEEIIIYQQQgghROaQ8I54J+Miwrlz55T/L1asGCYmJpiYmNC1a1dUKhWjR4+mR48ezJkzhwoVKgDw66+/MnbsWAICAmjcuDEgN8KFEF+Wv+oi9q6wjKmpqfK4QoUKUahQobcekxEFXOOxJiYmki1bNiwsLPDw8GD16tWsX7+ebdu2odFoKFGiBFu3bqV48eLY29tTrFgxWrZsSVJS0gcflxDiwzDMN8WLFyctLQ0/Pz9WrVqFhYUFWq02XXh67NixTJ06lbp160p45x+4fPkyDx48YOHCheTJk4fU1FTmzJmDp6cn1atXx9LSEo1GQ86cOTlw4IAS4NHr9QQHB2NmZoZarSZnzpw4Ojoqnw8ZNe//l5mamtKjRw8uXbpEREQEffv2ZcCAATRu3JgCBQpgYmKCTqcjNDSUkJAQkpOTWbRoEXZ2dtIhVAghhBBCCCGEEEIIkWlk2SzxFuOb1iNHjmTu3Lk8ePCA7Nmz4+HhoXyTGODJkyfMnz+fMWPG4O7uztChQ3n48CFhYWHcuXOH48eP4+bmllmHIoQQGcI44Lh3717Onz/PgwcPcHd3p2HDhmTPnh34NJYJNB7r7NmzWblyJa1ateLnn38ma9asAIwePZpXr17h5uZGu3btsLa2Bl4XlYcMGcLcuXPZtWsX5cuXz7TjEOK/7u8s5ZOUlETVqlU5e/Ys/fv3Z+LEiVhYWCjbo6OjGTFiBM7OzqxevVqWzvqHzp49S58+fdi/fz8AI0aMoF+/fjg4OCj7GDqsJSYmUrVqVS5fvszAgQOVAM+n8LnwpTO8xk+ePCEwMJDly5fz5MkTXF1dqVevHqmpqfz2229cuHCBAgUKsH79etzc3KQDkhBCCCGEEEIIIYQQIlNJeEe814gRIxgzZgxlypShXLlyxMTEcOvWLZo1a8bChQuVgs+TJ09YvHgxkydP5vbt26jVatzc3NixYwf58uWTG+FCiC+KceF16NChzJo1ixcvXijbK1asSJ8+fWjfvn2mdxwzHqufnx+zZ88md+7cjB07lh9//PFPi8h6vZ41a9YQEBCAu7s7q1evJlu2bB9z+EL85x09epTU1FSqVKkC/HmAx3C9df78eRo2bMi1a9eoVasWPXv2JHfu3Pz6669ER0ej0+nYv38/rq6uEiT5Fxo0aMC2bdvQ6/V069aNuXPnAv8L7Rj/2TjA061bN8LCwpR9RMYy/Nt+/vw5W7duJSoqivXr16PT6dBoNHzzzTc0bNiQvn37kjNnTvl9RQghhBBCCCGEEEIIkekkvCMUhpvWer2ehIQEatasSa1atfD19SVfvnxcvHiR0aNHs2zZMpo0acKSJUuUAM+LFy+4ePEi69atw87ODg8PD3Lnzi03woUQX6zAwEBGjx5Nhw4d6NWrF7ly5WLXrl34+PgAMG3aNH7++edMHuVrgYGBjBs3jp49ezJgwAAKFy78zv2MC/mTJk0iPDyctLQ04uLicHFxkUK/EB+JXq/n+vXr5M+fn3z58hEZGUnlypWVbe8L8Bi2Xbt2jXbt2nHw4EFlm4WFBeXKlSMyMlK6jPwLhte2bdu2vHr1ips3b3LixAkGDhzI1KlTgfSdzgx/TkxMpHDhwpiYmHD58mXs7Owy8Sj+W948Vy5cuIBGoyE1NZVSpUqhUqlQqVTy2SaEEEIIIYQQQgghhPgkSHhHvHVje9WqVVSoUIGGDRuyaNEiKlasqNzUvn79OqNHj2bBggVvBXjefD4pCgkhvlSHDh2iZcuWVKxYkeDgYAoVKgS8XpKmW7duODg4cPTo0U+iSLt3717atGlDjRo1GD9+PAUKFFC2nTt3TilYFilSBIBTp07RunVr7t+/T7FixVi5ciWurq4ypwuRCXx9fZk0aRKlSpUiNDT0b3XgMVyzvXz5kr1793Lx4kXS0tIoVaoU5cuXx87OTs7nv8n4dTb+s1ar5dKlS3Tu3JnDhw+nC/CkpqZibm4OvF7GLFu2bDx48IDU1FTy5Mnzt5Y/E+9meO3e9Rr+nXPifc8nhBBCCCGEEEIIIYQQnwLp2/4f9OYNbONvnC5dupSff/4ZV1dXsmbNStGiRZV99Ho9bm5ujBgxAoAFCxbQsWNHli5dSrZs2ZQb4Iab4FIUEkJ8qS5dusT9+/fp2rUrhQoVQqPRsGbNGnx8fLC3t+fQoUPY2dmRlpbGq1evyJo1a6aN9cqVKzx8+BAPDw8KFCiARqPh8ePHzJo1izlz5vDq1SucnJzw9fWlU6dO2NnZUb58eQoWLEifPn1wdHSUQr8QH5nhnAsODsbS0pLRo0fTp08fQkNDqVq16nsDDABqtRqdToeVlRUNGjSgQYMG6bbrdDo5n/8G43lPp9Px8uVLzMzMsLCwwMTEhKJFizJz5kwGDBjA9OnTAZg6daoS3Nm2bRt79+6lU6dOSsBT5tJ/z/i1u3fvHi9evCAtLQ13d3eyZMnyp18ceF9XHQnuCCGEEEIIIYQQQgghPiXSH/w/yHADu2fPnqxYsSLdz2rWrEmTJk148OABt27d4sqVK8DrooWhUOTq6sqIESPo0qULGzdupEmTJjx79kxugAshvniGZnUnT55ErVZTokQJAKKiovDx8UGtVnP48GEcHBwAuHz5MuPHj+fZs2eZNuYrV66g1Wo5c+YMd+7cISIigh9++IHx48eTP39+mjRpwoULFxgxYgQXL17E1dWV+fPnExAQgKOjoxT6hcgEJiYmpKamAhAUFMTEiRM5deoUAwYMYN++fcD/gtXvYriuM95u+LMsD/TXjOe9kJAQmjZtSsmSJalTpw7jx4/nzp076HQ6ypcvT0hICBUrVmT69OkMGjSIly9fsnr1agYMGMCqVavSdWCTufTfMX4/Jk2aROPGjSldujTly5enWbNmREREAK9fX51Ol5lDFUIIIYQQQgghhBBCiH9Nls36j4qOjqZ169aUKlWKESNG0Lx5c2XbrVu36N+/P+vXr6dOnTps27YNAI1Gg6mpqfJN75s3bzJ48GD27dvH6dOncXR0zKzDEUKIj8rQpSwsLAw3Nzd69eqFWq0mPj4+3VzYtm1bdu7cydGjR3Fzc8uUsd68eZN69epx/vx5HBwcePjwIQUKFGD69OmUKVMGJycnRowYwZgxY9i/fz9Vq1bNlHEKIf7HuIPIyZMnMTc3p127dpw+fZpSpUoxY8YM5VyVpX8yjre3N1OmTCFnzpy4urpy5swZXr16xbfffounpycNGjTA1NSUY8eO4eXlRWxsLI6Ojjx//pwcOXKwZ88e8ufP/95lm8Q/Y3g/KlWqRLVq1QAICwtDp9PRsWNHwsPDM3mEQgghhBBCCCGEEEII8e/Jsln/UXXr1mXy5MkMHTqUwMBAACXA4+zszKxZs1Cr1axbt44mTZqwceNGTE1N0wV4XFxcmD59OpaWltjb20thQgjxRfmzgvhXX32FiYkJw4cPx9zcHAsLC44ePYq9vb2yT0REBHFxcbRu3ZrcuXNn6FjfXCokLS0NtVqNiYkJLi4uREdHExAQgIWFBcWLF2fgwIFky5ZN2f+PP/7AwcEBV1fXDB2nEOKv6fV65Xz29fVl+fLlqFQqnJycyJYtG8ePH6dfv36EhoZSpUqVP11CS/wzxteymzZtIiIigoEDB9K9e3eKFSvG4cOHmT9/PsuXL+fRo0dYWlpSp04dvvnmG8LDw1myZAmnT5/GwcGBcePGkTdvXuXaWfz/REZGEhISQu/evRk0aBAFChQAIGfOnPj4+LBnzx5evnyJlZVVJo9UCCGEEEIIIYQQQggh/h3pvPMf9vz5c+bMmYOfnx/FihUjKCgoXQee27dvM2DAANatW0fDhg359ddfAd5ZhJDgjhDiS2CYy4zDMA8fPkSv15MlSxasra2VfQ1LpMDroqKHh4eybcmSJYwdOxYLCwu2bdtG7ty5M6y4bjzWyMhIYmNjuXz5MjY2NnTr1o3SpUvj7Oys7G88Dr1ez5o1a/Dx8aFs2bIsWbJECp9CfCKCg4MZOnQo3t7edOjQga+//pojR44wf/585s6dS6lSpZg1a5Z04MkA169fZ9euXYSEhLBu3bp0ndPu3LnD4sWLGTVqFPXr12fVqlWYm5sDr4OTZmZmpKSkYGFh8VawUvxzhn/XXbp0YdOmTezYsYMSJUqg0WiIiopixIgR6PV64uPjyZEjB6mpqcr7IYQQQgghhBBCCCGEEJ8TCe/8x/2TAE+jRo3YuHEj8HaXByGE+JzdvHkTFxcXgHSFvwkTJhAdHc2dO3coV64cHh4etG3bFoBHjx4xbdo0xo4di6OjI23atMHd3Z3Y2Fh2796Nra0te/bswd3dPcPmTONi/ZAhQ5gxYwa2trbkzJmThIQEnj17xk8//UT37t3fuRzWzJkzCQkJIS0tjdjYWJydnSUAIMQn4O7duzRo0IDk5GQ2b96sdBkx8Pf3Z8KECZQuXZqZM2cqSwjJ+fv33blzBzs7u7cCi76+vqxcuRInJydy5crF+vXr0Wq1qNVq5bW9fv06np6ebNiwgcWLF9OhQwdAXv//j127dnHy5Em8vLze2vbs2TOqVKmCnZ0d+/fvJy0tjbVr1+Lj4/PWkpW///47jx49Us4JIYQQQgghhBBCCCGE+FxIq5T/OBsbG3r27MmECRM4d+4cgYGBrFu3TtmeN29eZs6cSfPmzdm0aZNyI1yCO0KIL8WBAwdwc3Nj7NixAEpwx8/PD39/fxISEnB2dubXX3/Fw8ODKVOmAGBvb4+vry9z5szB1NSUBQsWMGTIEE6ePEmTJk2Ii4vL0OAOoBSJp0yZwrRp0+jVqxf79u3j999/Jy4ujpo1a7JkyRKWL1+OVqsFXndPu3jxIpUrV2bMmDFky5aNvXv34uzsjFarlcKzEJ8AjUbD7du3KVOmjBLc0el0GDL348aNo3379vz22294eXkRGxsLIOfv3/Tbb7+RP39+pk6dSmpqarptuXPn5ubNmxw9epTk5GTg9XWv8fcd3Nzc6N69OwDnz59Xfi6v/7/z5MkThg4dyuDBg5kxY8Zb201MTDAzMyMtLQ3gvcEdjUZD+/btiY6ORqPRfNRjEEIIIYQQQgghhBBCiP8vCe+IvxXgCQkJoXbt2vz2228kJiZm4miFEOLDevXqFZaWlgQFBTF58mTgdWF36dKl9OvXj+3btxMfH8/atWtxd3fH29ub4OBg4PX82b17d2JjY4mPjycmJoa4uDjmzJlD3rx5M7xLmV6v5+bNmyxbtoyKFSvSv39/ihcvjl6v57fffuPChQu4uroyevRopfis1+u5fPkyarWaDh06sHnzZtzc3KSjmhCfkNTUVDQaDb/99hs3btwASNf5Ra/X89133wFw6tQp2rVrx9GjRzNtvJ+bxMREHB0dOXPmzFvbvLy8mD9/Pjqdjh07djB37lzg9euv0+mUIOTXX38NvO7CJv5/7OzsGDlyJJUrV8bLy4upU6cq23Q6HVZWVtSqVYv4+HgGDhyIn58farWaQ4cOKcEdeL2c5fXr1ylevLh8ngkhhBBCCCGEEEIIIT47ppk9APFpMAR44HW3icDAQABlCa08efKwbNkyTE1NcXBwQKfToVZL9ksI8fn79ttv2bRpEx4eHvj4+JA1a1a++eYbrKys6NmzJ0WKFAGgWbNmZMuWjX79+jF06FBUKhU+Pj4AuLq6YmJiQvHixZXn1ev1GV48VKlUPHjwgDNnzjBy5EgKFy6MRqMhOjoaX19f1Go1R44cwd7eHp1Ox71798iTJw9169alYsWKZM2aFXNzc3Q6nRQ6hchkxksuFShQgJYtW7Js2TL2799Pu3btlP00Gg2mpqbUqVOHsmXLkidPHnbs2IGzs3NmDf2z8/3337Np0yby58+Pubk5Bw8epESJEtjY2ADQuXNnVCoVXbp0YeLEiTg6OtK8efN0174bNmwASDfvi3/O8O++QYMGmJqaEhAQwJAhQ9Dr9QwePFh5zb///ntCQ0OZOXMmuXPn5s6dO+meIzo6mtmzZ/P111/TsmVL6YIkhBBCCCGEEEIIIYT47Ej6Qije1YFn/fr1yvbcuXNLcEcI8UUxLINSu3Ztli1bRq5cuejbty99+/bF1dWVr776Cr1er3Ra+PbbbwkNDaVo0aL4+fkxceJE4PWSHjqdLt1zf6zCYVJSEhqNhly5cgEQFRWlBHfi4+NxcHAAXneaaN68OXFxcZiampIjRw5liTCZ04X4+AzzioFxVx2ABg0akDVrVry8vNi3b5+yn6mpKVqtlkWLFvHq1Ss2bNhAYmIiuXPnfus5xdsMc3XJkiWxsbEhNDSUqlWrsnDhQl6+fKns16lTJ+bMmcMff/xBnz59mDp1Kk+ePOHZs2csWLCA2bNnkz9/flq1apVZh/JFUKlUynJY33//PdOmTaNixYp4e3sTEhKi7NegQQOl6929e/eIjo7m4sWLPHjwgMDAQHx8fEhNTSUyMpIcOXK89ZkshBBCCCGEEEIIIYQQnzrpvCPSMe7AM3z4cPr06YOFhQX169dX9pEirxDic2Xc2QJeF89NTV9/FH777bcsXLiQrl27cvToUUqUKJFuKSnDY2vXrk1oaCh9+/bFz8+Ply9fMnLkyEybG3Pnzk327NlZtGgRJiYmBAYGKsEd4+VEpkyZwpkzZ5QiqRAi8xjPLcuWLePy5cvcvXuXHj16UKhQIbJmzUrLli05e/Yso0aNonnz5gQGBlKrVi1KlixJZGQkq1evxsXFheTkZKysrD5Kt68vgSEcZVCkSBHKlSuHn5+f0m3HysoKgO7du6NSqejRowdDhgxh4cKFJCYmYmdnh5WVFZs3b1ZCU/La/ztarRYzMzMA1q9fz71799BoNKjVajw9PVGr1fTt2xeAgQMHYmZmRkBAAK1bt8bExER57cuXL8+KFStwcXGR90MIIYQQQgghhBBCCPFZUunfvIMtvgjv647zZuH6fZ4/f87UqVNZsGABBw8exMnJKSOGKYQQH43x/PfixQusrKyU/w8PD6dDhw5YW1uzY8cOOnTowP379wkMDFSWEdTpdKhUKuUxe/bsoU2bNiQnJ3P79m1sbGwyrNvOX3U8a9WqFWvWrMHe3h5ra2suXLiApaWlctwrVqxg6NChlC9fnoULF5I1a9YMGacQ4p/x8fFh8uTJmJqaotFosLOzo2/fvvz8888ULFgQgAkTJhAREcHVq1dRq9U4ODiQkJCAs7Mz+/fvx83N7W9f3/3XGc+lM2bMwNHREQ8PD3bt2sXw4cM5duwYkydPThfgAVi8eDGdO3fGycmJJk2aMGzYMOzs7LCxsZGgyP+D8b/bIUOGEBERQdGiRXFzcyMlJYWNGzcCMHXqVAYOHKg87vDhw5w6dYrjx4+TPXt2qlSpQtWqVcmePbu8H0IIIYQQQgghhBBCiM+WhHe+QMY3rS9fvoxWqyV37tzY2tq+tf3PvHjxAp1OR9asWeVGuBDii1G3bl2KFi1KcHAwWbJkwdPTk5CQEObNm0fnzp1RqVTs2bOHtm3bkpCQwKRJkxg8eDDwdoDnwIED5MuXDycnpwwrnhvPv6dPn+bx48c4ODjg7u6uFJdv375NixYtOHLkCJ06dWLBggXK42fNmsWMGTPQ6/Xs3buXvHnzSqFfiExifO7Nnz+ffv360bJlSzp37sylS5eIjo5mz5499O7dm/79+1O4cGEADh06xI4dO9i1axdZs2alYMGCDB48mDx58sg12r8QFBREUFAQvr6+DB06lGzZsrF9+3YCAwM5fvz4OwM8ERER9OzZk5IlSzJ+/HgaNGgA/HW4Uvy18PBw+vTpw4ABAxg0aBCurq4AREZGMm7cOM6fP8+0adPw9PT80+eR90IIIYQQQgghhBBCCPE5k/DOF8a4KDR8+HAiIiJISEigTJkyNG/enGHDhgGg0WiUpWL+yXMKIcTn7Pz583Tq1Iljx44xduxYrly5QkREBN7e3vTv3x9nZ2dl3127dtGuXTvu37//pwEe+PuhyH/KeP4NCAhg1qxZPHv2jGzZslG9enUiIiLInTs3aWlpHD9+nH79+nHs2DFcXFwoVaoU169f5+LFi7i7uxMTE4O7u7sU+oXIJMbBguTkZAICAvj999+ZPXs2+fLlQ6fT8ccff+Dv7090dDR9+vTB09OTQoUKKc/x4sULrK2tlfNYzue/x/A66fV6EhMTqV27NhUqVCAwMBB3d3dlv78K8MyZM4fevXtTokQJxo0bR6NGjTLhaL487dq1IyYmhj179lCyZMl0/643b95M7969uXnzJrNmzaJPnz7A/5Y/U6lU8ruKEEIIIYQQQgghhBDiiyDhnS/UmDFjGDFiBOXLl6dYsWJs2bKFhIQE2rVrx9KlS4F/FuARQogvxfHjxxk3bhxr164FoH///vj5+SndcwClCPhnAZ6P+e1+w5xes2ZNKlWqxIEDB9i/fz/58uVj7969ODs7o9PpeP78Of7+/hw7dowrV67w1VdfUb16dfr27UuuXLmk0C/EJyAgIIDnz58TGxtLhw4dGDhwYLprshs3buDt7c3q1avp06cPXl5eFChQAMi4oOB/xdKlSylfvjzNmjVjwYIFVKlSBUg/p/9VgGfu3LlKgGfYsGH8+OOPmXIsX4qUlBTKly/PixcvOHfuHObm5m+FZA2hKYDp06czYMCAzByyEEIIIYQQQgghhBBCZAhJbnxhDN8o3rx5M126dGHYsGG4u7tz/vx5+vfvz7Jly0hJSSEqKgpTU1MJ8Agh/jMM38wvW7YslpaWys9VKhUODg7Kn433/fbbb1m2bBnt2rVj6NChvHjxghEjRmR4cMe4QJ+amsq6devo0KEDo0ePxtXVFZ1Ox+DBg5kxYwZVq1YlLi4OZ2dnsmXLxqxZs9BoNDx8+JBcuXIpRWkp+guR+e7cucPevXs5cOCA0gkGwNTUVJl3XF1dmTRpEgBhYWGYmJjQr18/ChUqJOfw/8OKFSv4+eefyZUrF1myZEnXcUetViuv//fffw9AYGAgQ4cO5eXLl/Tv358sWbIA0KNHDwB69erF8ePHJbzz/2RhYUHx4sWJiopix44dNGzYUPmMNXx+tW/fnrlz5/LkyRMGDhyIpaWl8j4IIYQQQgghhBBCCCHEl+LjtQ0QGUan0yl/VqlUaLVaEhISaN++Pe7u7mg0GooWLcrcuXNp2LAh0dHRtG7dGkAJ8AghxJfozflRp9ORnJyMRqOhU6dO1K5dm5kzZzJ69GgeP3781r6AEuDR6XTMnTuXZ8+eZfi4DQX6kJAQdu/ejV6vp0ePHri6upKWloZarWby5Mn4+Phw8+ZNqlatyu3bt4H/dVVzdHRUjsX4OYUQmSdPnjzMmDGDdu3aodVqiYmJ4fLly8D/lv8BlABP27ZtCQkJYfHixWi12swc+mevVq1atGzZkhcvXpCQkMDZs2cBlNfV+PX//vvvGTVqFM7OzoSHh7/12vfo0YOdO3cybty4j3sQX6i6desCMGPGDM6dO6f83PA5rFaruXfvHjVq1KBRo0bUq1cvU8YphBBCCCGEEEIIIYQQGUmWzfrMGXdSOHr0KCkpKSQmJuLr68vmzZspUKBAuhvf165do1+/fmzevJkff/yRqKiot55HCCE+d7dv3yZv3rxv/dzQWeHVq1dotVouX75MQEAAmzdvZtiwYQwaNAg7O7t0++t0OkxMTNizZw+FChUib968yvNkpHXr1tGyZUtcXV3R6/Xs2LGDAgUKKAVmtVqNTqfD39+fiRMn4uLiwsGDB8mTJ4/M6UJ8goyXZjp69CgTJkzgl19+wcfHh/79++Pk5ASQbn65evUqEydOZOjQobi6umba2D83xq91SkoKarUaMzMz7t27h6enJ6tXr6ZSpUrs2rULS0vLdJ0ojV//ffv2UaRIEXLlyvXeef9jL6P4OXrzNXrX/3fs2JGVK1fSrVs3evfuTalSpZTtS5YsYezYscTExODq6irdQ4UQQgghhBBCCCGEEF8kCe98xoyLCP7+/oSEhPDixQty5MjB8+fPWbx4sdJhB/53o9w4wFOnTh22bduWWYcghBAf3NatW+nYsSMLFy6kYcOGb21/swB76NAhxowZowR4Bg4ciL29PQC//vormzZtYsqUKVhZWQEfLuz4ZvHyXYXIbt26sXjxYgC2bNnCd999p/z9hv8aB3iyZMnClStXyJ079/97fEKIf+/vBDqOHz/OyJEj2bJlC35+fvTu3fudAR5Z+u6fMX6dVq5cyYEDB2jUqBE1a9bE0tKShIQEPD09WbVqFd9++y07duwAeG+ABySg8/9h/H5ER0dz9OhRjh49SuHChalbty7NmjUD4NSpU/j6+rJ161ZKliyJp6cnJUqUYPfu3cybNw9LS0v27NlD9uzZM/FohBBCCCGEEEIIIYQQIuNIeOcLMGnSJPz9/alduza1atVi/fr1HDlyhBIlSrBmzRoKFiyo7GsoPly/fp127dpx4sQJrl+/joODQyYegRBCfBgHDhygXr16lC9fnvHjx1OxYsX37mtcnD18+DBjxoxh06ZNDB06lA4dOvD7778TGBjIzZs3uXDhArly5fpg4zT+u8+cOUPhwoUxNzcHYMiQIRQuXJgePXoA0K9fP8LCwnB2dmbnzp0UKlTorWK+TqdjwIABrF69mhMnTpAnT54PNlYhxD9jHFbYuHEjv/32G7du3eKrr77Cw8Mj3TXXiRMnCAwM/MsAj/h7jEM2fn5+REREoNPpWLRoEY0aNUKtVqNWq/9WgEf8/xm/H97e3oSFhQFgYWHBkydPAOjbty8DBgygUKFCnD9/npkzZxIeHp7ueQoWLMi2bdtwd3eXIJUQQgghhBBCCCGEEOKLJeGdz5DxTesXL17w008/kTVrVsaOHYu7uzsPHjxg/PjxzJw5k1KlSrFmzRrc3NzeevytW7ewsLDA0dFRboQLIT57ycnJtGrVikuXLjFv3jyqV68OwMOHD8mRI8c7H2NcHI+Pj1eWscmWLRtarRY7Ozv27dtHvnz5MmSebNWqFTt27GDLli1UrFiRgQMHMnPmTPz9/fHx8SFbtmwA9O/fn9DQUEqXLk1UVBQFCxZUxmP832fPnmFraysdOoTIJG+GR0JCQkhOTsbCwoKUlBTy58/PL7/8wtdff608xjjA4+/vT7du3XB2ds6sQ/giBAYGMnbsWHr16kWvXr3Svd6G9+h9AR6ZPz+8cePGMWzYMHr27EmnTp3Ily8fW7duZdasWRw5coTWrVszYcIE3N3dAYiJieHKlSvcunWLYsWK0aBBA3LmzCnvjRBCCCGEEEIIIYQQ4osm4Z3P2IQJE8iVKxeTJk1i/PjxNG3aVPnG8JMnTwgODmbSpEmULl36rQDPu5ZjEEKIz1lSUhLlypXDxsaGuLg4smTJwsCBA7GyssLHxwc7O7t3Ps54Pjx37hy//vormzdvpkiRIgwbNgxnZ+cMKRi+evWKadOmMXPmTBwcHChSpAhr167Fx8eHfv36vfX39unTh/DwcEqWLEl0dPQ7AzxvHo8Q4uMxPvdGjBjBmDFj6NSpE506daJGjRoMHz6csWPH4uLiwqpVq6hUqZLy2BMnTjBq1CjWr1/PxIkTGTRokFyb/UtxcXE0b96cGjVqMHnyZCUQYswwZyYmJuLp6cnKlSspXbo0x48f//gD/sJdv36d77//HhcXFxYtWoSLi4uy7dixY0ydOpUVK1bg7+/PmDFj3vs8EtwRQgghhBBCCCGEEEJ86SS885k6ceIE1atXR6/XY2Zmxi+//EKtWrXQ6/Xo9XrUajVPnz5lwoQJ7w3wCCHEl6Zdu3asX7+eiIgI9uzZQ0REBH5+fvj5+SldbN7lzcBLcnIypqammJmZZWjBMDU1lVWrVtGjRw9SU1Np06YNwcHB6Yqb7wrwlCpViujoaAoUKCABTCE+MWvWrMHLy4t69erh6+urBO2+/vprEhISePLkCblz52bt2rVUqFBBeVx8fDyzZ88mKCgIV1fXTDyCz1tERAQ9e/Zk27Zt1KlT5737GebWhIQEOnfuzL59+7hy5Qo5c+b8iKP9/L0vMGr4+eHDh6lcuTLDhw8nKCgIrVYLoHyuHThwAA8PD27cuEFsbCxVqlT5qOMXQgghhBBCCCGEEEKIT4VU+z5TX3/9NaGhoZQoUYKkpCR27tzJ8+fPUalUShcGW1tb/Pz88Pb25uzZs9SqVYtbt25l9tCFEOKDM+RQAwICKFGiBF27diUiIgJPT08GDhz4p8Ed4K3CY5YsWTAzMwPIsOCOXq/H3NycS5cukZKSgoWFBfHx8dy/fx+dTqfsZ2JiohQ7w8LC6N27NydPnqR27dpcu3ZNgjtCfEJevnxJVFQUFhYW9OvXj4IFC/L8+XO++uorHj9+zJQpU/Dz8+POnTu0adOG+Ph45bEVKlRg7ty5uLq6Kue8+PsMr9mhQ4cAsLGxAUg3nxrvl5SUxMuXL8mZMydLlizh2rVr5MyZ8639xfvpdDrl8/P+/fvExMQor7/h56ampsDrcwNef6YZf65WqVKF3r17A6+79AghhBBCCCGEEEIIIcR/lVT8PiOG4rSh246Hhwe9e/emSJEihIeHs337djQaDcBbAZ5u3bqh0+mUG+hCCPElMRQJixcvTo4cOdBqtVhaWpI7d26yZ8+eyaNLz1AYNoy5XLlyBAUFERAQwLNnz2jfvj1xcXHpCshqtVr5DAgNDaVt27a8fPkSS0vLj38AQoj3srKyomLFivTp04dSpUqRnJxM48aNSUxMZOzYsfz888+MGTOGcuXKcf36dVq1asW+ffuUx2d0aPBL8mbzUMNrVrp0aQAuXboEpA9n6vV6TExMSEtLw8vLixMnTqDT6ciRIwc5cuSQTmb/gPFrFRAQQO3atWnUqBFz5sxRXnsAa2trLC0tWbhwoRLsMUhLSwOgVKlSANy+ffsjjV4IIYQQQgghhBBCCCE+PXJ3+hNn/M1rlUpFamqqUoQwMzPjp59+YujQoWTLlo2+ffsSExPzzgDPuHHjOHXqFLlz55ZvFAshvkharZZDhw6xefNmGjVqRMGCBZk4cSILFizgyZMnmT084PUYDcXO/fv3s3HjRmrVqsXw4cPx8/NjxIgRPHr0iO7du6cL8KhUKlQqFdeuXQNg+fLlXLx4kdy5c0uHDiE+MYMGDcLLywuAJUuWEB8fT9++fWnfvr2yT4kSJShRogR37txh4MCBpKamZtZwP0tvdnx59eqVsq1QoUIA+Pr6cuLECVQqFXq9Hq1WqzwmPDycFStWcPv27XRhHQnu/D3GwZ169eoRHh5O3rx5iYmJwcfHR3kPAIoWLYqPjw+PHj1i9uzZnDt3DvjflxHg9dJZFhYWlClT5uMfjBBCCCGEEEIIIYQQQnwi5A71J0yr1SrfIl6yZAldunTh22+/pWnTpkRFRXHt2jXMzc1p06YNI0eOxNLSkp49e74zwJM1a1ZsbW3R6/VSmBBCfJFMTEyoVKkShw8fZsqUKcyePRs3NzeGDh3KqlWrePr0aaaOT6fTKXP6qFGjaNOmDUOGDOHIkSNoNBpMTEz4+eefCQwMVAI8sbGxyuM3bdpEq1atiIyMBMDe3l7pIiGE+DQdOnQItVpNr169MDc3V35++fJlypUrx7x589iwYUO6beLPGYcgQ0NDad68OSEhITx//hyA+vXr069fP+7du0f//v05cuQIKpVKmSujo6MJCwvjm2++oU6dOpl2HJ8r498lGjRowOHDh/Hz82PlypXUq1ePYsWKpdsXoGvXrjRv3pzIyEiGDRvGrl27UKlU6HQ6oqKiWLZsGSVKlJDwjhBCCCGEEEIIIYQQ4j9N1lD6RBkXZIcMGcL06dNxcHAgd+7cXL16lY0bN9K0aVN8fX2pVKkSP/30E2q1mhEjRtCjRw8iIiKoX78+pqam6cI6xksHCCHE5+x9y5uUL18eAGdnZ4KDg/Hz82Po0KEAtG3bFltb2486TgPDWL29vZk2bRqtW7emV69e1KhRA3g979vY2NCpUyfgdcCnR48e+Pv78/TpU+bOncvdu3epVq2a8pwypwvxaVOr1aSmpvLo0SOcnJzQ6/UsW7aMixcv0qtXL3766ScgfWBbvJ9xCNLX15fZs2fj6upKvnz5sLGxUV7H6dOn8+TJEyIjI/nuu+/o1asXefLk4fjx42zbtg1zc3O2bNmCvb29LJX1Dxk+d8aMGUNsbCy+vr707t0bGxsb9Hp9us8lw59dXFwYNmwY5ubmrFq1ivXr11O3bl0eP37MxYsXsba2JioqSt4PIYQQQgghhBBCCCHEf5pKb/hKpPgkTZs2DW9vb3r37k2/fv0oUqQIiYmJtGvXjh07duDh4cHChQsxMzNDo9GwcuVKRo8ezZUrV4iJieH777/P7EMQQogPzrjQffjwYW7cuEFiYiK5c+fmhx9+wNT0dTZVo9GwZ88efH19uXr1KuPHj8/UAM+KFSvo1q0bnTt3xtvbGzc3t3TbDYXPFy9esGzZMiZOnMgff/yBWq0mX758bNu2jXz58kmhX4hMYhwseDOo8C5hYWH069ePggULMm7cOI4ePUp0dDRmZmbs3r2b3Llzf4xhf3FGjhzJ2LFj6dmzJ56enumWaTLm5+fHkiVLSEhIQKfT4eDgQMWKFQkLC8PFxUXm0n/p6dOn1KtXj5cvX7Jr1y4cHBz+1vmQlJTEokWLmD9/PtevXydv3rxUqlSJUaNGkTdvXnk/hBBCCCGEEEIIIYQQ/2kS3vlEvOtm9Z07d2jWrBl6vZ6lS5dStGhRNBoN69evx8fHB4AjR45gb2+PRqPB1NSUtLQ0Fi9ezIIFC1i9ejV58+bNjMMRQogMY1w8HzFiBHPmzCExMVHZXrlyZcaMGUOFChWwtrZOF+C5du0aEyZMoFWrVtjZ2X30sXfv3p1Vq1axf/9+SpUq9c59DAXQV69eceXKFTZs2EDWrFlp1aoVuXLlkuKmEJnEeO5JSkoiW7Zsf6tLiJeXFxEREbx8+RKAEiVKsGHDBtzc3OR8/hdiY2Np3bo1VapUITg4mAIFCijbzpw5g16vR6vVUrp0aQBOnz7N48ePuXfvHl9//TWurq7puvSIf27Xrl3UqVOHESNGMHLkyL88D94M9iQlJZGWloadnR06nQ4zMzN5P4QQQgghhBBCCCGEEP95smxWJjt79iyFCxd+503rBw8ecPr0aYYOHUrRokXRarWsWbMGHx8f1Go18fHx2NvbA3Dr1i2yZ8+Ora0tnTp1wsPDAysrK7kRLoT44hgKhP7+/kyYMIEff/yRhg0bYmNjw9y5c9m5cyddunRh/PjxNG3alCxZslC7dm2Cg4MJCAigZ8+eZMmShfbt23/UcSclJXHo0CEKFiyoBHfeLGgaCqApKSlYWlry1Vdf8dVXXyn7yZwuROYxzD19+/YlPj6e/fv3Y2lp+d79DefrtGnTaNOmDdevX8fW1pYKFSpgb28v5/O/dPXqVRITE2ndujUFChRAo9Hw4MEDQkNDCQ8PJzk5mZw5czJw4EAGDBhAiRIl3noO4+VpxT/37NkzVCqVEoL9q447KpWKixcvcvHiRRo3bky2bNmUbYb3Qd4PIYQQQgghhBBCCCHEf92ff1VYZKjffvuNEiVK0LRpU9LS0jAxMUGr1SrbHz16REpKCg4ODgCsXLkyXXDH0dERgIcPH9KpUyfi4+MBMDU1xcrKCpAb4UKIL9Pu3bsJDw+nffv2TJ48mU6dOvHjjz+yevVqJk+eTEpKCsOHD+fSpUvA67mwdu3aBAYGUrduXWrVqvXRx6zX69Hr9dy+fZs//vgD4J3BnefPnzNr1iwePXqkbDPsJ3O6EJnvyJEjXL58mStXrgCvz913MTExUbZVqlSJNm3aUL9+fezt7dHpdHI+/0vXr19Hq9Vy8uRJrl+/Tnh4OE2bNmXixIkUL14cDw8Prl+/zrhx4zhz5sw7n+OvwibiNeMGrcb/zlNTU9Hr9Rw+fJiUlJQ/fT01Gg3w+veY8PBwEhISMm7AQgghhBBCCCGEEEII8RmT8E4mcnFxoXz58mzZsgUPDw8lwGO4yZ0/f37y5s3L4sWLWb58OQEBAW8FdwAmTZrEkSNHMDMzy6xDEUKIj+ry5cs8efKEFi1a4OrqCrwuENra2tK9e3d69+7NlStXGDp0qPIYExMT6tWrx/r163F2dk4XlswohmKnXq/H1taWBg0a8ODBA3bv3q38HF536DB09fDx8WHx4sU8ePAgw8cnhPj7DOfrsGHDePr0KUuXLgX40+WC3rftr5baEu/XvXt3ypQpw/jx4ylTpgyenp48ffqUDRs2EBUVxdy5cxk7diwJCQkyj/4/vNkZTq1WK7+j1KhRg3z58nH69Gnu3LkD8M7PVL1ej6np60ava9as4cmTJ+TMmfMjjF4IIYQQQgghhBBCCCE+P1I5yCRarZYcOXIQExND7dq1WbNmjRLgMTU1RafTkSdPHqpVq8aRI0cYMGAAAOfPn1eCO3q9nmXLlrFy5Urq1avHN998k5mHJIQQH82NGzcAlPlQo9FgamqKXq/HxsaGAQMGULJkSfbt28fVq1eVx5mYmGBhYaH8+UN7swOHoUBvKIBWqVIFa2trunfvzvbt29/qqLNmzRq2bdtGoUKFyJs37wcfnxDi3zOcryVKlKBQoUIsXLiQU6dOZfKovkzGQRC9Xk9qaiqpqakA5MqVi9WrV/PTTz/RqFEjRo8eTXx8PPXq1SNXrlwAXLhwAXt7e9zc3DJl/F8Cw7/36tWr8/PPPwOvu3tqNBqyZs1K7dq1+f333xk5ciSQvtMUvP48NDzHzJkzuXHjBq1atfq4ByGEEEIIIYQQQgghhBCfEQnvZBLDEln29vZERUW9FeBRq9WYmpoSGhpKsWLFePToEZUrV1aKzgBhYWGMHDkSU1NTQkJCyJo1a7r29kII8aVydnYGYMGCBco3+w1dAlJSUpQuNy9evCAxMfGjjMm4e866desYPnw4jRs3Jjg4mK1btwLQvHlz/P39AahXrx7BwcHs2bOHhw8fMm7cOPz8/EhLS2PGjBlYW1vLnC5EJnmzi4hxKCFfvnx4eXmRmJjIb7/9BiDn6gek1WqVQOPixYvp1q0b3333HU2aNCEqKopr166RP39+li1bxtKlS/H39ydbtmzA6/chOjqaffv2Ub16deny8v90+/ZtrKysWLp0KQMHDgT+tzzvgAEDyJkzJ0uXLqVXr17A/wKrqampyp/XrFlDSEgIBQoUwMPDI1OOQwghhBBCCCGEEEIIIT4HKr1UGzKVoUDx8OFDWrduze7du2nZsiXLly9XlsG6ePEiLVq04Pfff8fJyYlixYpx+/Ztrl69Sr58+YiJicHd3T1dsUMIIb5kCQkJVKxYkSdPnjBr1izatm2LiYkJKSkpSsixdevW7Nu3j5MnTyrdGDKKTqdLt+xVWFgYarWarFmzcv/+fSwtLRk0aBCjRo0CXnchmDp1qtJByKB06dKsXbtW5nQhPhEHDx6kcuXKyv8bunydO3eOOnXqYG1tzf79+zN8jvmvMF6qaciQIcyYMQN7e3vy5MlDQkIC9+/fp1WrVnTv3p1vv/32rcfPmDGDkJAQ0tLSiIuLw9nZ+a3ln8Q/c+nSJQIDA1m5ciXdu3dnzpw5yrYDBw5Qp04dXr16RfPmzenRowc1a9bE0tKStLQ0JkyYwOLFi3n16hVxcXG4ubml+7wUQgghhBBCCCGEEEII8T9y5zSTmZiYoNFoyJEjxzs78AAULlyYAwcOMGjQIIoUKcKlS5dwdnYmICCAPXv2SJFXCPGfotPpyJkzJ+PHjwdg3LhxzJ8/H0AJ7qxZs4bY2FjKly9P1qxZM3xMhkLk2LFjmTJlCh4eHuzevZubN28SExNDjhw5GDNmDGPHjgVgwIABrF27lvnz59OrVy+GDBnCqlWr2L59u8zpQmQi4w47vr6+VK1alfr16zN//nwSEhIwNTUFoFixYjRu3JjLly8THx8PvN2tR/xzhpDNtGnTmDZtGr169WL37t2cOHGCffv20aBBA1atWsXKlSuV11uj0XDhwgUqVKjAmDFjsLOzY9++fTg7O6PVaiW48/9UqFAhgoKC+OGHH4iIiODUqVPodDp0Oh1VqlRh9+7dFC9enHXr1tGgQQOKFy9OqVKlcHJyYvTo0eTKlYsDBw7g5uaWrkOdEEIIIYQQQgghhBBCiPSk885H9lffNv2zDjyGx967d4/cuXMr/y9FXiHEf9Hjx49ZsmQJo0aN4vHjxzRo0IC6dety+vRptm3bhlar5dChQ7i4uHyUzguXLl2ibt26FC1alJkzZ1KoUCFSU1PZs2cP7du3x87Ojri4OBwdHdM97s2xSVcCIT6ON881Q1cdgOvXr3P37l1mzZrF3r17uX37Ni4uLnh5eVG+fHmqVq3K+fPnqVWrFuXKlePXX3/NrMP4rL15DavX67l37x5NmzZFpVKxdOlSChcujF6vJyoqCl9fXwCOHz+Ovb09er0erVbLnj17GD9+PN988w2DBw8mV65ccn38gV24cIGkpCTKly+v/MxwDl25coWdO3eybt06rly5gk6n45tvvqFhw4Y0adIEe3t7eT+EEEIIIYQQQgghhBDiL0h45yPR6/XodDrlpvXhw4dJTEzk5cuX5M+fn3Llyin7vi/AYygqGW6UyzIAQogvzT8t7j179oxDhw4xZMgQTp8+DYCdnR3ffPMN8+fPx9XV9aMVDLdt20b9+vWJiorixx9/RKPRsHr1avz8/FCr1Rw5cgQHBwdSU1O5d+8erq6uwNvhHSHExzVkyBA8PT1xcXEBwNPTkx07drB7927s7e1JTExk+vTp7NixgxMnTmBtbU3Xrl2pXbs2YWFhHD58mKVLl9KkSZNMPpLPx7Fjx8iZMycuLi5vzdGnT5+mXLly+Pv7ExgYiEajITo6Gl9f33RzqU6n486dO+TNmxedTkdSUhLW1taYm5tLCDKDGX9uvfkZlpSUhEajwd7eXvmZvB9CCCGEEEIIIYQQQgjx1+QuagY6ceIEcXFxyk1tQ2EiICCA+vXr88MPP9C2bVuqV69O8+bNuXPnDjqd7r1LaBkHdwAp9gohvjiGeTIkJISDBw8CrwuD75M1a1a+//574uLiOHHiBJs2beLo0aOsXbs2Q4M7xsvjGJY4fPDgAfA6PKTVaomOjmbo0KGo1Wri4+NxcHAAXgeOvL29OXr0KCBzuRCZKSwsjKlTp9K5c2d0Oh1eXl6EhITQqFEj9Ho9pqamODk5ERwczK5du1iyZAlVqlQhNDSU3r17c/jwYZKSkjhx4kRmH8pn48KFC5QvX5569epx8+ZNTExM0s2pL1++JC0tTZkzDR133pxLHz58yA8//MDu3bsxMTEhe/bsmJubA0hQ5AP4s89e488tw58NS85ly5YNOzu7dM8h74cQQgghhBBCCCGEEEL8NbmTmkFu3rxJ9erV6d+/v1KABhg5ciTBwcFUrVqV6dOnM3XqVAoWLMj69etp0qQJJ0+eBHgrwNOoUSPS0tLk5rcQ4ou3Z88ePD09OXToEPDX4RadToeNjQ2lSpWiQYMG5M+fn6xZs6LX6zMkuGP8vMOHDycyMhKNRoO7uzsABw8eZOvWrfj6+qJSqYiPj0+3VJa3tzd79uwhS5YsH3xsQoh/5ocffsDLy4tdu3bh5ubGjBkz8PX1ZcCAAeTKlQv4XyjB1taW9u3bExUVxZEjR6hRowb58uUDYPz48co1nPhzRYoUoU2bNpw/f54ff/zxrQCPg4MDjo6OLFy4kHnz5qULQRrPpVOmTOHChQt/GjIR/45xN52rV69y7969v3yM8e8o8kUDIYQQQgghhBBCCCGE+OckCZJBrKys8Pf35+rVq/j5+REbG8ujR4/Yt28f3bt3JywsjAEDBjBw4EB27dpFr169OHHiBL169eL58+fA6wDP6tWrKV26NEePHuXp06eZfFRCCJHxcubMSe7cuZk9ezZXrlz5y/3fF2rMqKKh4XmDg4MZO3Yshw4dIikpiSJFilCpUiUCAwPp0qULZmZmHD58OF2xecGCBezatYv69esrRX8hxMcTFxdHWFiY8v/Ozs5MmjSJkiVLcvfuXVxcXGjTpg3Ozs5vdQ0x/L+trS2lS5dmyZIlbNq0iaFDh5KSksLevXuB/4V9xNsMAZ0VK1bQqVMnjhw5wo8//siNGzeUUGSBAgWoX78+x48fx8fHB4Dz588rc6ler2fFihWsWLGCevXqUaFChcw5mC+UcXBn27ZtdOzYkYiICKXLnBBCCCGEEEIIIYQQQoiMIeGdDJIjRw569uzJsGHDOH78OIGBgcTExHD48GEaN26Mq6srABqNBkdHR8aMGUP79u05cuQIgwYNAl4XOOzt7dm5cyfnz5/HwcFBCkJCiC9e8eLF6dy5M5cvX+bUqVNA+mWqMovxGHQ6HRs2bMDDw4MhQ4Zgb29Pjhw56NatG1ZWViQkJODv70/OnDmVxyxatIjx48djZWWl/Fc6Rgjxcej1epKSkqhduzb9+vVj69atwOvz+sCBA5w5c4ZSpUpx8+ZNBg8ezP37998KABr/v06nw9zcnDx58uDn50ehQoVYvHixdEn8CyYmJqSkpACvw4y9evXiyJEjtGzZkhs3bij7TZo0iRo1avDkyROqVauGqampsm3WrFmMGDECU1NTQkJClE5r4v/POLiza9cuAgMDiYuLo379+piZmcnrLIQQQgghhBBCCCGEEBlIpZe7sBnq4cOHLFq0iBEjRuDq6sqrV6/YuXMn+fPnR6vVYmJigk6nQ61Wc+/ePcqVK4e9vT0HDx7E2tpa2QdQ9hNCiC+VYZ47e/Ys3377LYULF2b37t3pCreZbdq0adjY2DBu3DjmzZvHd999l26unjhxIsOGDUOj0dC4cWPy58/PqVOnOHHihBLIdHd3T/cYIcTHsW3bNubPn8+cOXOws7NTfn7w4EGsrKxYunQpU6dOpUaNGkRFRSkBvHddgxkuoVUqFW3atOHXX3/l+PHjFClS5KMdz+fGeN67ceMG9+7do3Pnzpw7d44KFSoQHR2Ns7MzWq2WU6dO4enpSWxsLLly5aJkyZLcunWLK1eukD9/fjZv3ixz6Qf0ZnDHz8+PM2fOsGfPHipUqMDTp0+5cOECTk5OuLi4ZPJohRBCCCGEEEIIIYQQ4ssjSZAMliNHDjp16sTo0aN5/Pgx169fJyIiAr1ej4mJCXq9HrVaTUpKCrlz56ZGjRqcOXOG69evK/sYSHBHCPGlMO4iZrwUh6FwWLhwYSpVqkRcXBw7d+4EyLRv/BuPNTY2lsGDBzNp0iQ0Gg329vZv7efj48PSpUtp3bo1u3btYs6cOdy7d4927dqxf/9+KTYLkUl0Oh1169Zl+fLl2NnZMXz4cCZMmABA5cqVKVWqFN7e3vTr1499+/bRunVr7t+/D/zvGiw2NpYDBw4Ar+crlUrFq1evMDExwdLSUjok/gmdTqfMe/7+/lStWpX27dvz6tUr7O3tiY+Pp1WrVsoSWqVLl2br1q0MHjyYIkWKcObMGZycnPD392f37t0yl35A7wvu7N69WwnuLFiwgMaNG7N9+/ZMHq0QQgghhBBCCCGEEEJ8maTzTgYy/pZ2QkICy5cvZ8yYMWTPnp1Zs2bx/fffo1arSU1NxdzcHICGDRty+vRpTp06Rfbs2TNz+EIIkeGCgoJ48uQJjRs35rvvvku37dixY1SvXp0OHTowZ86cTBmf8TweGxtLtWrVmDhxIn5+fgDMnz+fzp07A6+Ln4ZApsHNmzd5+fIlzs7OWFhYYGpqKsVmITKJ4dzTarVcu3aNQoUKYW5uzrRp0+jdu7eyX0JCAmPHjiUkJIQaNWqwatUqHB0d2bp1Kz4+Pjg4OBATE4OlpSVarZaoqCjatWtHt27dmDt3biYe4edh7NixDB8+HC8vL7p06YKrqyuXLl1i6NChbN++nfLlyxMdHZ2uu4tOp+Px48fkyJFDmZdlLv0w/k7HnaVLlzJs2DCKFCnC4cOHM3nEQgghhBBCCCGEEEII8WWS8M4H9FfLWt2/f59ly5YRGBhIiRIlGDJkCC1atFC2R0dH07VrVypXrsyaNWuwtrb+GMMWQohMcezYMTw8PLh06RIA7du3p169enh4eKBSqXj69ClNmjQhPj6eHTt2UK1atUwbq6enJ6tWrWLatGn89NNPBAcHM3ToULJmzcq6dev49ttvgfTL6AghPh3G12hPnjzBzs6O3bt307BhQ9RqNZMmTaJPnz7K/gkJCYwb93/s3Xdgjef7x/F3ph1BkCBG7L1ae1OzZq1WqSL2lpCEyLB37L33HqVoidiz1GqN1qrae2Wec35/+J2nCdr6tiLF5/WPOM/I/ZzjuXPk+pzrHsrEiRMpWLAguXPn5sCBA4SHh3Pw4EGyZcsGPL/n9+zZw5o1axg3bpzxmOaAV7t27RpVqlQhceLErFmzBg8PjzjbW7RowdKlSylZsiQrVqzA3d39pWVm9fy+OX/VcadkyZJGcMfX15dixYqxc+dOAGJiYv5Ty1mKiIiIiIiIiIiIvA8U3nlDYn/6d8+ePZw9e5Zr167h4eFBpUqVyJgxI/A8wLNo0SKCgoJImjQplSpVokmTJmzcuJHDhw/z+PFjdu/eTebMmVWcEJH3SuzieezC35o1a1i6dCnbtm3j4cOHfPzxx3h6elK/fn2OHTtGzZo1GTlyJF5eXn8bkoyPsW7cuJG2bdvSsGFDvL29yZ49OwDDhw/Hz8+PAgUKMHnyZMqXLw+ocC/yX9ahQwdWrlzJzz//TPr06dm9ezdVq1bFwcHhpQDP7du3mT59OnPnzuXu3bsULFiQRYsWkSVLljjv+2LPF29rjnpXnT9/nnz58tG+fXsmT55sPF+xfyZUqVKFsLAwPvroI1auXEmWLFn0vMYzBXdEREREREREREREEp7CO29A7IJC//79mTRpEo8fPza2FytWjBYtWtCrVy/geYBnyZIlDB8+nNu3b1O4cGGSJUtGxYoV6dixY5xPGYuIvA9iz2lLly7l0KFDFC1alFatWgHw7Nkzrl27xsiRI9mzZw9nzpzBxcXFWDLL2dmZQ4cOkSFDhngfa+zwjdlsZt26dfTu3Ztvv/2WfPnyxbmWwYMHM3DgQPLnz8+UKVMU4BH5j4l9L86dO5c+ffpQuXJlhgwZQp48eQDYvXs3VapUwdHR8aUAT0REBPfu3ePGjRvkyJEDJycnvUf7F86fP0/+/PkpUqQImzZtIm3atMY2ayhk3rx5dOrUCTs7O9KnT8+uXbuMELy8edu2baN///5GcCf2UlkK7oiIiIiIiIiIiIi8PfoI6xtgDe74+/szfPhw6tevz9atW9m4cSM9e/bkwoUL+Pj4EBAQAED69On54osv8PPzI126dERERDB27FgGDx6s4I6IvHfMZrMxp/n4+NC5c2fWrFlDihQpiIqKAiBx4sTkyJGDSZMmcejQIUaNGkXevHmZOHEiz5494+bNm3zzzTfG+eKTtdDv5eVlFJJr1KhBvnz5AIzlWwAGDBhAcHAwp0+fpkuXLuzZsyfOOUQk4ZjNZuNefPr0KefOnaNo0aKMGzeOPHnyYLFYMJvNlC9fntDQUKKiovD29mbKlCnGORwdHcmQIQPFihXDyckpznwm/7ucOXNSpUoVzpw5w44dOzCZTMY26/vpwoULky5dOgoWLMi9e/cUFolHFouFzZs388MPPyi4IyIiIiIiIiIiIpLA1HnnDdm7dy8NGjSgQoUKjB07lixZsgAQHh7Onj17+PLLL3n27Bnjxo2jXbt2wPPlGKZNm8b8+fPZv39/nE8fi4i8bwICAhg0aBBdunShXbt2FC5c+C/3v3PnDqdOnWLy5MmEhoZSqFAhduzY8ZZGC71792batGlERUVRrlw5vvnmG5InTx6nK4+12GztwOPm5saaNWsoWbLkWxuniPw1Pz8/fv/9d/bv30/z5s0JDg5+5VJXsTvwjBkzho4dOybwyN8v1ud51apVdOzYkYwZMzJ9+nSKFSuGo6Ojsc+gQYMIDQ0lLCyMhw8f4uzsrGWz/oW/e+7Cw8P57bffyJUrlxHc8fPzo2jRogruiIiIiIiIiIiIiLxF+i34G3L58mXu3r1LixYtyJIli9GVIUmSJHzyySdMnz4dW1tb1q5da3zKOG3atHTt2pVjx46RNm3aeO8mISKSUA4cOMCUKVNo2rQp3t7eRnDnVflR62Np0qShUqVKTJs2jfr167Nz507WrVsX72O1fv+xY8fSp08fkidPzo8//sjRo0exsbEx5nBbW9s4HXj69evHo0ePyJw5c7yPUURez/3799m/fz8LFy7k2rVrJE2aFIjbHct6L1s78FgsFjp37sz8+fMTatjvjP/lvas1QPLJJ5/g6enJmTNn+Prrr5k5cya//fYb8HxZxdWrV5MiRQqio6NxdnbGYrEouPMPmUwm47k7deoUYWFhrF+/nvPnzxs/y5IkSUKuXLl49uwZ8+fPV3BHREREREREREREJIHoN+H/QOwW/1Znz54F4MGDB8DLxYxy5cpRtmxZNm/ezOHDh43HU6VKRYoUKVSYEJH32unTp7l79y7t2rWLE2551fJSNjY2WCwW4880adLQuXNnAI4fP/7Gx/ZigCh2QGfQoEF069aNR48e0bRpUy5cuICdnd0rAzzDhg3j999/x83N7ZU/J0Tk7UuVKhWzZ8+mZcuWRjjh1KlTL809sQM833zzDZkzZ6ZKlSoJNOp3h/W967Zt215rf4vFQsqUKenTpw++vr48ffqUbt26UbhwYbJmzUrLli25f/8+U6ZMMbrxaBnCfyb2Em9BQUHUrVuXKlWq0LBhQypVqkSHDh2IjIw09k2UKBF79uyhQIECCu6IiIiIiIiIiIiIJAClRf5HFovF+EV4jx49mDBhAgAVKlTA3t6evXv3AmBvb28UdC0WCy4uLlSuXBmAp0+fvnReFSZE5H125swZAFxcXF653Rp2uX37NvDynJgmTRqSJk3KlStX3mgwxmQyGd8rMjKS33//nSdPnsQJYA4aNIgBAwZw+/ZtypQpw6+//vqnAR5rGNP6c0JEEo41mOfh4UFAQACff/45Z8+eZdKkSVy6dOml/a33ctWqVTl37hzu7u4K4r0Gb29vqlevzqJFi/52X2so08XFBS8vL1atWkXbtm3JkycPWbNmpVOnThw4cIDMmTPruf+XrMEqHx8fgoKCKFiwINOmTWPt2rVkzZqVOXPmUKhQIaKiorC1tcXOzo6FCxeyb98+QMEdERERERERERERkbdNv5H9H1mLvCNHjmTixIk0atSIVq1a4eHhQb58+Zg7dy5Fixala9eu2NraxvnF988//4yTkxNubm4JeQkiIm9d2rRpATh69ChFihTBZDIZAZfYYRcfHx8aNGhA3bp1gedz7pMnT1i3bh3Pnj0jX758bywYE3sMEydOZM2aNezZs4cMGTKQPXt2RowYQd68eUmePDnBwcEADB48mLJly7J3716yZ89uzPHWIqmCmCIJw2w2v9TB0Ho/WiwWPDw8GDx4MJGRkcycOZPEiRPTs2dPsmbNGucY6zkcHBwAFMR7DTVr1uTYsWO0adMGGxsbWrRo8Zf7WwM8yZMnp0SJEpQoUYKnT5/i4OCAnZ2dEY7Uc//vbdq0icmTJ9OuXTt8fHzw8PAA4NGjR+zfv587d+4QHh6Oo6MjFouFRIkSAc/vJwV3RERERERERERERN4udd55TbE//WuxWAgNDeWLL75g2LBhODs7ky1bNoYMGQJA9+7dGTNmDIDxi+9169axfft2SpYsGWfJGBGR95m1I02lSpVIlSoVY8eO5d69e9jZ2WGxWDCbzXFCkatXryYqKirOOe7cucPixYtp1KgRffr0eSPjih0Y6t27N7169eLmzZvUqVOHtGnTEhYWRq1atZgzZw63bt0CIDg4GH9/f27dukWlSpU4d+6cipsi/wEmk8kI3WzYsIGAgADat2/PkCFDePTokTHHZMuWjVGjRtGgQQMmTpxISEjIKzvwgIJ4/4uqVasSFBREpUqVaNmyJfPmzfvbY2IHqwCSJUuGo6OjMS8ruPNmHDlyhMjISDp06ICHhwcmk4nFixcTEBBAtmzZOHfuHClTpiQ8PNw4Rkv5ioiIiIiIiIiIiCQMVR1fk7WIEBISQkxMDIcPH2bu3LnkzJnTKD7XqVOHZcuW0bx5c7y9vdm0aRO5c+fm3r17hIWFYW9vz8yZM0mePDkWi0WFIRF5b7yq6wX80cWiQIEC1K1blwULFlCrVi1WrFhBpkyZjLl19erVzJs3j/z58xtLDFplzZqVmTNnUrRo0b/8Xv8L6/w7Y8YMJk6cSNeuXY0uHDExMYwbN445c+bg7+9P4sSJadu2LXZ2dgQFBWFnZ0dgYCDNmjXjyJEj2Nraaj4XSSBms9mYR/r168eECROIjIwkSZIkhIeHs3jxYmbNmsXHH3+Mg4MD2bJlY/To0cDzjlt2dnZ07tyZ7NmzJ+RlvJNid8cpW7Ys/fr149mzZzg7O7/2OTR3xh+TycSJEydInTo1xYsXx2w2s2rVKvz8/LC1teXgwYOkSZMGgEuXLrF7927atWun4I6IiIiIiIiIiIhIArGxWD/yKsBfF4X3799P2bJlyZkzJ9HR0axevZqiRYsSExODnZ2dUYDYtm0bEydO5NChQ9y8eZNMmTJRtGhRJk2ahLu7u5YCEJH3Suw5bdOmTRw9epRbt26RJUsWunbtSuLEiQF48OABX375Jd9++y05cuSgVq1aFCtWjLCwML777jtsbW3Zs2cPWbJkMebiF4OObyK4E1uDBg348ccf+f7778mZM6exDFZMTAxr1qyhb9++REREsGfPHnLkyGEcN2bMGJo0aaJOaiL/EQMHDmTIkCF8/fXXtGvXjlKlSjFixAh8fX3JnTs348ePp0qVKka3rIsXL9KvXz9WrVqFv78/AwcO1Huzf2jXrl1UqFABgLt37xqBEEl4X375JevXr+fkyZMcOXKEPn36YGtry6FDh4zlLC0WC5UqVSJJkiSsXLmSFClSJPCoRURERERERERERD5MCu/EErsovGDBAo4fP86DBw8oXrw4LVu2JEWKFEydOpXu3btjMpkIDAxk4MCBwB9t/61F5vv37/PkyRPOnTtH9uzZSZs2LcmSJVNwR0TeK7HnTR8fH8aPH09kZCQODg5ER0dTuHBhZs6cScGCBUmUKBEPHz4kMDCQLVu2cPbsWQCcnJwoW7Ys06ZNe6MBxxeDP9ZgjnXbgwcPyJ49OwUKFGDXrl3GdutxJpOJAQMGMGLECNq3b8+0adOIjo7GwcHhlecUkYSxZs0aevbsSY0aNfDx8SF79uyYTCYKFCjA7du3CQ8PJ3369EyfPp2KFSvi6OgIwC+//MKIESPw9/dXEO8f8vX1ZcSIEfzwww9GdzR5u2L/rIv988vOzo6VK1fSrFkzatSowblz57BYLOzbtw9XV1fj+EmTJjFo0CC6deuGr6+v/p8iIiIiIiIiIiIikkAU3vl/sQvQjRs3ZsOGDcTExBjbrcu9pEyZkvnz5/P111/j5OTEzJkzadKkCfDqX57HpqWyROR9EntO8/f3N7petG3bljJlyjBo0CACAgIoVKgQ48ePp3Tp0jg6OhIVFcWjR4/44YcfMJlM5MiRg4wZM77RgGPsOX3v3r18//332NnZ0bhxY/LmzWvs8/HHH3Pr1i0OHjxIhgwZjGuyHn/9+nWKFStG0aJF+fbbb//1uETkn9m3bx8PHz7kk08+iROYCw8Pp23bthw9epRly5ZRpEgRnjx5QokSJbh//z6DBw/m1q1b9O/fn7x58xISEkKlSpWMEJ51zlG4+p8JCQmhd+/eLFmyhObNm+u97lsW+9+txWLBYrHE6U53/vx5WrZsyaFDh3BycuLq1askT57c2L58+XL69+9P6tSp2bhxI+nSpXvr1yAiIiIiIiIiIiIiz725tUfeYbGLvOXLl2fr1q18+eWXbNq0iUmTJpE3b16++eYbfH19iY6O5quvvmLOnDk8evSI4OBgNmzYADzvuvNiB57YVMwQkfeJdU5bvXo18+bNo127dvj5+VGmTBnMZjNLliwhVapUnDlzhg4dOrB//36ioqJwdHTExcWFGjVqULt2bXLlykWyZMmwWCxvPLjj6elJ48aNCQ4O5ujRo9y/f9/Yz9bWliJFivD7778zevRoHj58GGceB7C3t8dkMmn+FklAN2/epEWLFjRt2pTvv/8ek8lkbEuSJAlFihShbdu2FClShPDwcOrVq8etW7cYMmQIbdu2xdfXl+LFi/Pzzz/TtWtXtmzZYgS0rXOOgjv/TNWqVUmXLh3BwcHcvHlTc+VbZDabjX+348ePp2HDhjRo0IBx48YREREBQM6cORk2bBju7u48evQIX19fVq5cyY8//kj37t3x8vIiIiKCVatWkS5dOsxmc0JekoiIiIiIiIiIiMgH7YMP78Qu8laoUIFTp04xfPhwQkJCqFWrFp07d2batGm4uLhw4MABo2DUunVrZs6cyenTp+nfv3+cAI9+8S0iH4qnT5+yYsUKnJyc6Ny5M9mzZ+fJkyfkz5+fBw8eMGLECHx9fTl37hy9evVi//79cQrvsb2Jom/sOb1KlSqsXr2a2rVrc/HiRZYsWUKZMmWM/QCCgoLIly8fixcvZsGCBdy/fx8bGxvjHBs3buTp06eULFkSADWrE3n7UqdOTf/+/XF1daVdu3Zs3bo1zjzSt29fvL29AVi4cCEHDx6kY8eOtGjRwtinfPnyFC9enGvXrjFo0KA/nYfkf1OwYEHq16/PmTNnOHToEICe27ck9pKVvXr1YseOHXz33Xf06dOHFi1acPjwYcxmM5UrV2bx4sVUqVKFmTNn0qxZM4oVK8asWbPIly8f+/btI3PmzJhMpjhde0RERERERERERETk7dKyWf+vZMmSHD58mNGjR9O7d2/gj1b0T58+pXjx4ly4cIGLFy+SMWNG47jZs2fj6elJ/vz5GTZsGJ9++mlCXYKISIIYOHAgTk5OeHl5ER4eTu3atTl16hQjR47k66+/xmw2U6RIEU6dOkWBAgUYM2YMVatWjdciYZMmTfjuu+8IDg6mdevWpEyZ0pjTYy/rYjKZWLt2Ld7e3ty7d4+6devSp08fUqVKxebNmxk3bhwWi4Xdu3fj6uoab+MVkVez3q9RUVEsXbqUgIAAoqOjmTlzJjVq1HipY067du1YunQpx48fJ0eOHMbjFSpUwM3NjcaNG1O2bFkyZMjwti/lnfXikmLW1yQmJgZ7e3t++uknypUrR+XKlVm9enUCjvTDc+DAAZo0aUK9evXo1asXkZGRzJs3jylTplC8eHGGDBlC2bJljWUgr1+/zqFDh3BwcKB48eJkz56dFClSaNk4ERERERERERERkf8A+4QewH/ByZMnuXr1KgB37twxlnWxunLlCvfu3aNq1aqkTZs2TuG3bdu2AHTq1In27dszf/58Pvnkk7d/ESIi8Sx2V5vY82RwcLCxz9y5czl06BB9+vThiy++AJ53B6hQoQKJEyfm+PHjBAUFUbFixTjz7Ju0ePFiNm/ejKenJ23atCFFihRxluSK3eHHzs6OOnXqkDhxYgIDA1myZAlr1qwxOuxkzZqVzZs34+rqquKmyFsS+32WjY0NkZGRJEqUiObNmwMQEBCAp6fnKwM8tra2WCwWnj17Zjy2ePFiLl68iKenJ02aNAFeDqTIn7M+T0uXLiV37twUK1YMeL6soMViwc3NjeLFi7N27Vq2bt1KjRo1EnK47zXrvWH989KlS0RFRdGjRw8jrObt7Y2rqysBAQH079/fCPC4ubnh5uZmvH6xz6l7QURERERERERERCThfZC90V9sNpQvXz6WLVtGvnz5mDBhAoMGDeLBgwfY2dlx6dIlQkJCuHPnDp999hmOjo4vLe3Stm1bxo4di62tLQUKFHiblyIi8tZYgzvBwcHMmDGDx48fv7TPwYMHsbOzo02bNiRKlMh4/IcffiB79uzMmTOH5cuXx1twByA0NBQ7Ozv69etnBHf+akmuJEmS8OmnnxIWFkZwcDAtW7akSZMmjB49mp07d5I1a1YV+kXeErPZbNyve/bsITg4mIEDB3LmzBkSJUrEF198QXBwMA4ODnh6er60hFaZMmWIioqiUaNGzJ8/Hy8vLwYOHEjSpEnjhKt1P/9vZs+eTYsWLShVqhTdunVj1apVwPNwVapUqejbty92dnbs2rUrgUf6/jKZTMa9ERERQXh4OOnSpaNmzZrkypWL6OhoANKlS0fr1q0JDg7m6NGj9O/fn3379hn//3nx/0FvYslKEREREREREREREfn3Prhls/6siGs2m9m7dy8dO3bk0qVL+Pr60rBhQxYvXszw4cPx9vZmxIgRf3mOp0+fkixZMhV5ReS9df78eSpVqkRERASjR4+mcePGpEiRwtjesmVL1q9fz5EjR8iVKxcAixYtwtfXl9GjR9OsWTMgfrpeWCwWrl27RokSJciQIQOHDx9+qZPan7l//z6pUqV65bbY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HBxo2rQpu3fvtnbo0HgdkdRnMpmsIZtJkybx9ttvU7JkSSpVqkTnzp05c+aM9QzKmzcvq1evpmjRooSEhKQI8Ch892zkypWLVatWUa9ePe7cucOQIUOIiIhIEZJKCvDMmTOHyMhIFixYQGJiYhqvXEREREREREREREREJH0zWFTxEnll7d27l8qVKwMPAzxPC7D88ccfjBgxgi+++AKAatWq8eDBA06cOIGbmxthYWF4e3s/93UD7Nmzh99//x1fX182btxIiRIlrNcuXrxIixYtOHToEP369cPX15ds2bJZC/yJiYkYDAbs7OxShAdEJPUkP3P69+/PpEmTKFSoEG+++SYRERGsW7eON998E19fX6pXr279OT137hzNmzfnzJkzdOrUiREjRpAxY8a03Eq681fnfdJ5eO3aNVq3bs3OnTvp1asXgwcPJnv27JjNZmto02QysXfvXvLmzUvOnDlTewsiIiIiIiIiIiIiIiIvFYV3RF5RzZs3Z/fu3cybN4933nkH+GcBHoDZs2ezfPlyjhw5Qvbs2SlWrBiTJk3Cy8srVcIw06ZNo2fPnjRp0oTY2Fh++OEHTCYTNjY21vVfunSJ9957zxrg8fPzI2vWrP94jyKSOiZOnMjQoUPp2LEjHTp0oHjx4pw9e5aqVaty/fp1qlatyogRI1IEeH7//Xdq1KiBra0tR48eJXPmzGm8i/Qj+Rl97do1oqOjSUxMpGjRoinuu3btGi1btmT37t307NmTIUOGPBbgedI7RURERERERERERERE5N9TeEfkFbVp0ybatGmDh4cHwcHBNGrU6F89n5CQwL1798iYMSMWiwV7e/tUK+AeOHCAbt26cfjwYbJkycLhw4fx8fF5LJiTFOA5evQon3zyCcHBwSryi6SBO3fu4OLi8tj58PPPP9OuXTty587NmDFjKFSoEHfv3qVChQpERUVRq1Yt1qxZw+uvv86wYcN44403rO+4ePEi9vb2eHh4KJT3DyU/o8ePH8+iRYs4efIkRqORBg0aMHToUIoUKYKdnR3w1wEefb9FRERERERERERERESeLZun3yIiL6N69eqxcuVKrl+/To8ePVizZs0/ei4p72dnZ0eWLFkwGo3Y29tjsVhSrfNC+fLl+fLLL3nzzTe5ffs206ZN4+bNmxgMBpLnEb28vFi1ahVeXl6sX78+VdYmIimF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Fhsbi52dHV27dqVIkSLcv3+fN998k1u3bjF27Fjat29PmTJlaNiwIQsWLMBgMDB27FhrB54kCu6IiIiIiIiIiIiIiIiIvFjUeUfkHzp27Bh169alatWqTJo0CW9vb+u17777jj59+nD27Fm++OILOnbsaL32aFBHwR0R+S8sFgs///wzpUqVIjExkYEDBzJz5kxGjx7NZ599hr29Pb/99htVqlTh/v373L9/n2HDhhEQEJDWSxcRERERERERERERERGRv6F/fi/yD124cIHr16/z3nvv4e3tjdlsxmQyAdCoUSPGjh0LQHBwMGfOnCEpF/doUEfBHRH5O2az+YmfGwwGSpUqBcD9+/fZvXs3xYoVo3fv3tjb2wOQJ08ecufOzcSJE6lRowYff/xxqq1bRERERERERERERERERP4bhXdE/qG7d+8CcOfOHeDh6BlbW1trSKd58+Z8+umnREREcPv2bQwGQ1otVUTSKZPJZB1rderUKQ4cOMC5c+dITEwEsP56/fp1zp07h6urq/XZxMREpk2bxtmzZ3n77bfZsWMHPj4+1mdERERERERERERERERE5MWk8I7IP+Tl5QXA+vXruXTpkvVzg8FAQkICAAUKFCA2NpZTp06lyRpFJP0ym83WzlxDhw6lWrVqVKpUiapVq9KuXTtiYmKws7PDYrGQO3duChYsSFhYGJMnTyYyMpJ58+bxxRdfUKxYMTJmzGh9r52dXVptSURERERERERERERERET+AYV3RJJ50riapNFYlStXpkWLFmzevJk1a9Zw//596z1GoxGA8+fP4+bmRvHixVNnwSLy0kjquBMUFMSoUaMoVKgQ3bt3x8PDg8WLF/PGG29w7949DAYDRqORSZMm4enpSd++fcmbNy9dunQhPj6er7/+msyZM//l+C0RERERERERERERERERebHon+OLABaLJUXXi19++YV79+7h6emJh4cHtra22Nra8sEHH/DTTz/h5+dHTEwMTZs2pUiRIgCsWbOGdevWUbJkSfLly5eW2xGRdMRkMlnPnrt377Jy5Urat29PQEAAuXPn5t69e/Ts2ZMFCxZQtWpV9uzZQ8aMGSlXrhzbt29n+PDhmEwmcubMSc+ePfHw8EjxThERERERERERERERERF5sRksFoslrRchkhZOnDhBfHw8ZcqUSfF5YGAgM2bMIDIyEldXV9q3b0+bNm0oW7YsAF999RXjx4/n5MmT5M2bl3r16nH58mX27duHra0t4eHheHt7YzabrZ00RESeZs6cOdZuO19++SUVK1YkISEBo9HIvXv38PX1ZdasWZQoUYKwsDBcXFxSPG+xWDAYDAruiIiIiIiIiIiIiIiIiKQzCu/IK+ns2bMUKFCAt956i+DgYEqXLg1AQEAAI0eOpGLFihQpUoSTJ0+yf/9+6tatS0BAAFWrVgVg27ZtrF27lhkzZpCYmIinpyeVKlVi8uTJeHl5qXguIv/K0qVL+eCDD8ibNy8Wi4Vdu3bh6emJjY2NNQh4//59BgwYwMyZMylevDh79uzBxcXFGvBJCu+IiIiIiIiIiIiIiIiISPqi8I68kiIjIwkICGD27Nk0btyYQYMGUaxYMWrWrEnJkiUZOnQoXl5eXL9+nYkTJxISEkKtWrUICgqiWrVq1vecP3+e2NhYsmbNSubMmXF0dFRwR0T+NbPZTKtWrVi9ejUODg7s2LGD8uXLW8+TJwV4PD09OX36NBkzZkzr5YuIiIiIiIiIiIiIiIjI/8AurRcgkhbc3NwYMWIETk5OTJw4EVtbWxo1asQvv/xCSEgIXl5eALi7uzNmzBjs7OwYPXo0ACNGjKBKlSoA5M6dO0WnC4vFouCOiPwriYmJ2NnZsXTpUtq2bcuiRYto3749W7ZswcPDI0WAx9nZmfHjxxMdHc369eu5e/euwjsiIiIiIiIiIiIiIiIi6Zw678grLTIyklGjRjF58mRq1KhBdHQ0YWFhODs7WwvqSYYOHcqoUaOoVasWo0aNolKlSmm4chFJj5I66Dwq6bwxm83WAE/16tVZvnw5OXLkeKwDz4MHD4iNjSVLlizq9iUiIiIiIiIiIiIiIiKSzim8Iy+9R4vlCQkJGI1G6+9jYmIICAhg0qRJACxdupT3338feNhJJ3lnnaQAT+nSpZk9ezblypVLpV2ISHqXPGTzyy+/EBUVRVRUFG+88QaOjo7Ws8ZsNvPRRx+xZMkSqlWrxooVK54Y4IHHzygRERERERERERERERERSX8e/+f/Ii8Ri8ViLXKfPHkSwBrcmTBhAkuWLCFDhgwMGjQIf39/AGbOnMmhQ4cAMBgMJM+3jRgxgl69enH58mXraC0Rkacxm83W4M7o0aNp3Lgxb7zxBg0bNqROnTqEhoYSFxcHgI2NDaGhobRu3ZqwsDBatGhBRETEY8EdQMEdERERERERERERERERkZeAwjvyUksqbNeuXZsyZcpw+PBhALp3786AAQO4efMmcXFxZMuWjZ49e9K/f3927tzJ2LFjOXLkiPUdyQM8kyZN4syZM7i7u2M2m1N/UyKSriQPEfr6+uLv74+npyejR49m2rRpXLx4keHDhzNhwgQePHgAPB7gqV27Njdu3HjiyC0RERERERERERERERERSd/s0noBIqmhVKlSbN++nZYtW1K2bFlWrlxJ3759adq0KQ4ODgBky5aNgQMHYjabmThxIhaLhSFDhlCmTBkMBkOKjheZM2dOUZAXEUku+XmRFCKcPn06X375JT169KBr164ULlyYmJgYxo8fz7lz55g4cSIAffv2xdHR0RrgiYqKIjw8XGFBERERERERERERERERkZeUwZK8pYjISyZ5AX3SpEn0798fi8XCBx98wPz58zEajdauOkkF9sjISIKDg5k4cSLNmzfH39+f0qVLp9UWRCQduXDhAu7u7jg5OaU4f86fP0+bNm3IkCEDU6dOpXDhwty9e5fy5ctz7949unTpwuzZs4mOjqZ///7069cPR0dH4GHnnlu3buHm5vbY2CwRERERERERERERERERSf9UAZSXmo2NDSaTCYDo6GhrUGfnzp2cOHECeFgYTwruALi5uTFo0CD69u3L2rVrGTBgAMePH0/9xYtIunLgwAGKFCnCyJEjiY2NxcbGxtot5/bt28TFxdG9e3cKFy7M/fv3eeONN7h9+zajR4/G19eXcePGcf/+febPn8/EiROtI7QMBoOCOyIiIiIiIiIiIiIiIiIvMY3Nkpeera0tADVr1sTBwYGIiAimTJlC06ZNWbJkCZUrV7YGeJKK425ubvj7+xMVFcX333+Ph4dHGu9CRF50mTJlIkuWLEybNg1HR0f69++Pk5MTAGXKlGHatGlUqlSJxMREfH19OXXqFGPGjKFVq1bY29tTuHBhbG1tuXr1Kv7+/mTKlInu3btb36/gjoiIiIiIiIiIiIiIiMjLSWOz5KX0Vx0qkj4fNmwYw4cPx9vbm6VLl1oL6nZ2dlgsFiIiIsiRIwexsbHEx8fj6uqqrhci8lSnTp2iadOmXLhwgcGDB6cI8CS5desWb775JhkzZiQsLMz6eUxMDBUrVqRjx46sWbOG0NBQvLy8UnsLIiIiIiIiIiIiIiIiIpLKlESQl47JZLKGbP744w+OHDnCyZMniYiIsH4+bNgwAgMDuXjxIi1btmTPnj3Y2dlhNptZv349vXr14ptvvsHZ2RlXV1csFouCOyLyVIULF2bNmjXkzp2b0aNHM378eGJjYwGsI7QuXbrEL7/8Qo4cOazPmc1mvvzyS27cuEGTJk3YsWMHXl5e1rF/IiIiIiIiIiIiIiIiIvLyUucdeakk744zatQoFixYwNmzZwHInj07I0eOpFGjRnh6egIwfPhwhg0bhru7O3PnzuXatWuMHz+eP/74g5MnT5IzZ84024uIpF8nT56kWbNmT+zAk5CQQJUqVbh8+TITJ06kXr16fPvtt4wbNw4PDw/Wrl2Li4tLGu9ARERERERERERERERERFKLwjvyUvL19WX8+PHUqVOHFi1acOvWLdasWcPRo0dp3749/fv3J2/evACMGTOGoKAg4uLisLW1JW/evPzwww/kyZNHo7JE5F+zWCwYDIa/DfB888039OjRgytXrpA9e3Zu3bqFt7c327ZtI3fu3NZ3iIiIiIiIiIiIiIiIiMjLT+EdeeksX76cTz/9lA8++IABAwaQP39+AMaNG8fAgQMpWrQoBw4cwNnZ2frM6tWrOX36NHFxcXTp0gUPDw9MJhO2trZptQ0RSQeeFvB7NMDTr18/nJ2duXfvHidOnGDcuHHEx8eTJ08e/Pz88PT01NkjIiIiIiIiIiIiIiIi8opReEfSnad1pOjevTtLlixh06ZNlCtXjvj4eL799lv69++Pvb09e/fuJVu2bH9bIFfxXESeJvk5ceLECWJiYrh37x6lSpUiS5Ys1lDPXwV4kksKAensEREREREREREREREREXn1KLwj6UryLhd37tzB1dXVes1isZCQkEDZsmXJnDkze/bsISEhgVWrVjFw4EBsbGw4cOAA2bNnB2DHjh1kzpyZMmXKpMVWRCQdS34WBQUFMXv2bG7evEl8fDylS5embt26BAUF4ejoCDwe4EkaoZUU1tGYLBEREREREREREREREZFXl8I7km4kL25/+OGH5MqVi379+uHu7p7ieu3atTl//jybNm3i+PHj9OzZ87HgDkDp0qXJmDEjW7ZssRbYRUT+DX9/f0aPHk2lSpWoXbs2Fy5cYPv27Vy5coXatWvz7bffWrvsnDx5kubNm3PlyhU+++wzhg0bprNHRERERERERERERERERLBJ6wWI/FNJwZ3GjRuzePFili1bxpw5c7h582aK+ypWrMjvv/9OYGAgPXv2xNbW9rHgzsSJE/njjz949913sbe3T9V9iEj6ZTKZrF///vvvrFmzhl69erF48WJGjBjBwoUL2b9/P5UqVWLr1q20bNmSuLg4AIoUKcKaNWtwcHBgxYoVxMfHp9U2REREREREREREREREROQFovCOpCvnzp3jzJkzODs7ExcXx5gxY/jiiy+4efOmNdzTpUsXypcvz+LFi4mJieH7779PEdxZunQps2bNIn/+/Hz00UfW0TciIk9ja2sLwNq1a7lx4wY3b96kZcuW+Pj4AA/DPTlz5mTDhg2UKlWK9evX88UXXwAPR20VLlyYvXv3EhYWRqZMmVDzOxERERERERERERERERFRakHSlbx589K+fXvu379Pp06deP311xk9erQ1wAPg4eFBt27dKFGiBCaTieXLl7N9+3Z+/fVXBg4cSL9+/YiLi2Pp0qVky5YNs9mcxrsSkfRk6dKlNG3alI8//pjMmTNTrFgx4OHoPjs7O0wmE66uroSGhpIxY0Z27twJYA0K5s+fH09PT0wmkzV0KCIiIiIiIiIiIiIiIiKvLoNF/+xf0gmz2YyNjQ0XL16kXr16eHp60qdPH4KCgjhz5gwDBw6kY8eOuLu7c//+fb777jumTJnC3r17re/ImDEj5cqVY+HChXh7e2MymaydNEREniTp7Ely4sQJRo0axaZNm7h16xaLFi2iVatWKYI4iYmJPHjwgMqVKxMREcHhw4fJlSuXOn2JiIiIiIiIiIiIiIiIyGMU3pEXksVi+cuOFCaTiS5duvDtt9+ybt067t27R69evbh06RK+vr7WAI/JZOL+/fssXbqUGzduEB8fT/Xq1SlXrhyurq4K7ojIUyU/i/bv30+5cuWws7Pj9OnTjBgxgqVLl1K7dm1CQ0Nxd3cHHgZ37OzsAHj99dexWCyEh4fj4OCQZvsQERERERERERERERERkReXwjvywkne5SI2NhYnJyfrZ0mBm/Pnz1OsWDG6du3K+PHj2bp1K3369OHChQspAjz/5M8QEXmawYMHs2nTJg4dOmT97NdffyUoKIglS5bQokULgoOD8fb2to7OWrlyJR9++CEtWrRg3rx5ODo6puEORERERERE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4I6IiIiIiIiIiIiIiIiIpBolIuS5SUhI4Oeff+aDDz6gWLFixMTE8OabbxIZGcnEiROZNm0afn5+JCYm8sEHH7Bs2TLrsxkzZsTFxQWLxaLgjoj8Y8mDO7///jvXrl37R8/Z2NhQs2ZN1q5dS548eUhMTFRwR0RERERERERERERERERShVIR8kyYzebHPitcuDDHjh3j/fffJzExkb59+3L27Fn8/f1p0qQJAO+//z65c+fG3d2d1q1b880336R4x7/tniEir67kwZ1Nmzbx8ccfM3v2bBISEv7R87a2ttjb2wNgZ2f33NYpIiIiIiIiIiIiIiIiIpKcwjvyPzOZTNbuOIcOHWLjxo3WawUKFADg7t27bN++nXLlytGhQwecnZ0BMBqN3LlzhwYNGlC1alXKli2b+hsQkXQveXBn27ZtBAYGsmfPHurXr4/RaEQTIkVERERERERERERERETkRaXWAvI/MZvN1tEyo0ePZt68ecTExLBq1SqqVKliLaZHRETw22+/0aBBA5ycnKzPfvXVV7i7uzN27FgyZMiAvb09JpNJ42pE5B97NLjj5+fH8ePH2bdvH+XLlycqKorTp0/j6emJl5dXGq9WRERERERERERERERERCQldd6R/8xisVg77vTv359hw4ZRqlQpli1bRpUqVVLcmyVLFooUKcLcuXPZsmULCQkJfPXVV8yfPx9PT0+MRqN1XI2COyLyT/1VcGf79u1UqFCBqKgo5s2bR6NGjdi8eXMar1ZERERERERERERERERE5HEGi2aJyP9owYIFdO3alY4dO9KnTx/y5MnzxPumTZtGnz59MJlMZM2alVu3bpE7d2527NhB7ty5UxThRUSe5q+COzt27LAGd0JDQ/H396dQoULs378/jVcsIiIiIiIiIiIiIiIiIvI4hXfkX3lSwKZFixaEh4ezadMmihUr9tgzZrPZ2qFnyZIlbNu2jcuXL1O0aFH69etHzpw5NSpLRP6Vv+u4U7FiRWtwZ9CgQZQtW5adO3cCkJiYiJ2dJkaKiIiIiIiIiIiIiIiIyItD4R15qkOHDnHt2jXq1q2Lg4NDimuRkZEUKFCAqlWrsm7duhRBnb8SHx9vDerY2toquCMi/5mCOyIiIiIiIiIiIiIiIiKS3v19ykJeedevX6dx48a0aNGC48ePP3bdzs4Oo9HI77//zvXr1x8L7pjNZgCuXbvGzJkzAbC3t8fW1jZFgEdE5N/asmULgwYN4sSJE+zYsUPBHRERERERERERERERERFJlxTekb+VIUMG/P39+fDDDylQoADwZyAHIHPmzFSoUIHffvuNzZs3k5iYaL2WvAtPcHAwU6dO5ffff0/dDYjIS8lisfD9999z+PBhtm/fToUKFRTcEREREREREREREREREZF0SWOz5Kni4uKwsbHBaDQyYcIEfHx8eOedd7C3twfg22+/5dNPP8Xb25tZs2ZRqlSpFOO1li9fzsCBA6lcuTJz587FyckprbYiIunI08bwxcbGcunSJQoWLGgN7gwePJgyZcoouCMiIiIiIiIiIiIiIiIi6YbCO/K3LBYLBoMBgCNHjlCuXDkKFCjAxIkTqVu3Lvb29kRGRjJmzBimTJlCgQIF+OSTT3jnnXfImjUrX331FTNnzsRsNrNr1y5y5cqV4p0iIk9iMpmsI/WOHz/OzZs3iYqKomjRouTNmzfFuL379+8zZ84c/P39FdwRERERERERERERERERkXRH4R2xerTLRVxcXIoOOvfv32fVqlUMHjyYjBkzMm7cOOrUqYOjoyPXrl1jxowZLFy4kEuXLuHo6IitrS3x8fEUKlSItWvX4uPjk6IgLyLyJMnPoqCgIBYsWMCFCxcA8PT0pEGDBkyfPh0HBwfMZjMWi4XWrVtz+fJlwsPDAQV3RERERERERERERERERCT9UHhHHrN582bq1q1r/f2QIUMoXrw4rVu3JiYmhjVr1uDr60vmzJkJCQmhbt26ODo6Eh0dze+//868efO4du0aDg4OVK9enaZNm5I9e3YFd0TkX/Hz8yMkJIRGjRrRqFEjcuTIQUhICHv37qVAgQIcO3bMOr4vedhQwR0RERERERERERERERERSU8U3pEUGjduzPfff8/ChQv54IMP6N+/PxMnTmTYsGEMGDAAJyenJwZ46tWrl6JLz6Me7eojIvJ31q9fT6tWrWjdujV+fn7kzZsXgNDQUNq2bUuWLFk4d+4cmTNnTjGKT2eNiIiIiIiIiIiIiIiIiKQ3qnBKCm3atCFDhgz4+/tTt25dJk6cyODBg2nbti1OTk4AZMiQgWbNmhESEkJUVBS+vr5s3ryZ+Ph4AJ6UB1MxXUT+jUOHDhEXF0fnzp3JmzcvJpOJRYsWERgYSJ48efj111/JnDkzsbGx1mcsFovOGhERERERERERERERERFJd9R5R6ySuleEh4fzxhtvAFC3bl3mzZuHh4fHY2OvHu3AM2HCBOrUqWMdYyMi8l+YTCbef/999uzZw7Vr1zCbzaxYsQJfX19sbGw4cOAA2bNnB+DkyZPs3r2bDh06KLgjIiIiIiIiIiIiIiIiIumSKp1ilTR25vjx45hMJgwGA0ePHmXHjh0A2NraYjabrfcn78ATExNDu3bt2LVrV1osXUReIra2ttYRfefPn2f16tVPDO5YLBa6dOnC6tWriYmJSeNVi4iIiIiIiIiIiIiIiIj8NwrvyGMKFCjAvHnzmD17Nnfv3mXAgAHMnz8feDj+ymw2W0M8GTJkoHnz5gQEBPDaa69RrFixtFy6iKQjyRu/JX1tMpkAaNKkCTExMXTt2pWBAwdia2vL3r17rcEdgOnTp3Pq1CmqVauGs7Nz6i5eREREREREREREREREROQZ0disV1zSqKxHP4uLi8PR0ZH169fTqlUrXF1dGTFiBO3atUtx7x9//EHOnDmJi4vDZDLh7Oz82HgtEZFHJT8nLBYLFoslxdirM2fO8NFHH3HgwAEyZcrE5cuXyZgxo/X6smXLGDJkCFmzZuW7777D3d091fcgIiIiIiIiIiIiIiIiIvIsKLzzCktePL9//z5xcXGYzWbc3NxS3LdhwwZatmyJq6srw4cPp3379gB8++23jBs3jn79+tGsWbNUX7+IpE9ms9ka1JkyZQrbt2/HbDZTq1YtunbtiqOjIwDbt2+nXbt2XLp0iW7dulGjRg1rZ7A1a9ZgsVgIDw/H29s7xTtFRERERERERERERERERNIThXdeUcmDO7NmzWLdunWcPn2aDBky0L17d5o3b54ixLNhwwZatWqFo6MjvXr1IkuWLEyfPp3ffvuNM2fO4O3tnVZbEZF0ys/Pj5CQEFxcXIiLiyM+Pp5mzZrh5+dHuXLlsLGxISwsjGHDhhEWFkZ8fDwAjo6OVK9endmzZ+Pt7a1uXyIiIiIiIiIiIiIiIiKSrim88wpK3qGif//+TJ48GQ8PD0qWLMmVK1c4duwYPXr0oHPnzhQtWtT63JYtW2jRogVRUVHY2tqSL18+fvjhB3x8fFQ8F5F/Zd++fbRo0YLGjRvTp08f4uLiWLBgATNmzKBcuXKMGjWKqlWrYmNjw9WrV7l69SoHDhzAaDRSrlw58uXLh4uLi84eEREREREREREREREREUn3FN55hY0ZM4Zhw4bRuXNnOnToQIkSJTh48CBvvvkmMTExtGvXjgEDBlCkSBHrM7/++itr167FxcWFpk2bkiNHDhXPReSpLBYLBoPB+uvSpUvp1asXu3fvpmDBggBcv36d0NBQAgMDKVu2bIoAz9+9U0REREREREREREREREQkPVN45xW1d+9ePv30U0qVKsXw4cMpUKAAMTExvP7660RFRVGoUCF27txJu3bt6NevH8WKFXviexTcEZGnSX5OxMbGAg/PoIULF7Jw4UISEhIwGo0AREZGsnDhQgICAihbtiyjR4+matWqKYI/IiIiIiIiIiIiIiIiIiIvE7u0XoCkPovFwsGDB7l27RqzZ8+2BncqVarE7du3mThxIgULFmTAgAEsWLAAR0dHevTokaIDTxIFd0Tk75jNZus5MW7cONasWUNERARmsxkHBweio6PJlCmTdZyfm5sbbdu2BSAgIICAgACGDh1KzZo1FdwRERERERERERERERERkZeSwjuvIIPBQJMmTXBxcaFq1arExcXx8ccfc/nyZcaOHUvLli2xtbWlfv367Ny5kzlz5vDHH38wYcIE8uXLl9bLF5F0JGnk1eDBgxkzZgxeXl4YjUbu3r3LhQsXmDlzJj169MDZ2fmxAI+NjQ19+/bF1dWVqlWrYm9vn8a7ERERERERERERERERERF59jQ26yWXfMxM0teJiYnY2f2Z29q3bx/vvPMO77zzDnPmzLEW27ds2UL37t0pXrw4Bw4c4MiRI7i5uaXJPkQkfUk+Kuv8+fPUrFmTt99+m4EDB5IhQwY2bNhAYGAgDx48YMSIEbRp0wZHR0drgAfgxo0brFmzhgYNGuDl5ZWW2xEREREREREREREREREReW7Ueecllrx4npiYSHx8PM7OztbgTlKY5+jRo0RGRlK3bl1r0Rxg9erVZMiQgTFjxpA9e3YyZ86corAuIvJXks6e77//nqioKAC6du2Kt7c3AC1btiRHjhx07tyZwMBALBYLH374YYoAT/bs2enYsSMGgyHFeSYiIiIiIiIiIiIiIiIi8jJRCuMlZTabrYXusWPHUq9ePUqXLs3IkSM5cuQIgLUjT4ECBbC1tWXv3r3Ex8cDsGzZMjZv3kyFChXImzcvmTNnxmKxKLgjIv/YrFmz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FAqpq6vLMccck1//+tc57LDDcsstt6R169YrbKH1z/cEWJlisdgc7owfPz433XRT5syZk759++bLX/5yvvCFL6Rt27ZJ3pm8s88++6RTp065+eab07lz5/ziF7/IBRdckG233TZTpkxpnsYDAAAAAAAAwP8R76zl3v3l+ahRo3LllVfmjTfeaD6+7bbbZuLEidl3332z2Wab5bXXXssxxxyTe++9d6UBD8AHdc455+S73/1uttlmm7Rv3z5z585NQ0NDTjvttIwcOTIdOnTIkiVLMn78+Fx88cXp1q1bWrduneeeey6dO3dOTU1NevTokVKplEKh0NKvAwAAAAAAALBWsW3WWm5ZuDN27NhMnDgxxxxzTB544IEsXrw43//+91NXV5djjz02999/fxobG9OmTZvceuutGTBgQO66664MGDAgb731lnAHeN/eva3VnDlzcvPNN2fYsGH57W9/m+nTp+fuu+/O9ttvnwkTJmT8+PFZtGhR2rZtm9NPPz2XXHJJunfvnurq6gwcODAPPPBAevTokaamJuEOAAAAAAAAwEqYvLMOeOSRRzJw4MDsvffemThxYnr37p0kuemmm3LqqaemVatWmTlzZtq3b988Yee1117LwQcfnFmzZuWpp55Kp06dWvgtgHXNtGnTMm/evJx11ln51a9+lZ133rn52LPPPpujjz46jzzySEaMGJGzzjorm222WZb9k9LY2JhCoZCKigqTvwAAAAAAAAD+DZN31gHz5s3Liy++mK985Svp3bt3mpqacvPNN2f06NFp3759HnvssbRv3z719fXNX5C3adMmv/71r5vDnWKx2MJvAaxLJk2alL59+2bKlCnZZZddsvPOO6epqak5zunRo0duv/32fOITn8jFF1+ciRMn5tVXX22erlNZWZmKiookEe4AAAAAAAAA/BvinXVAbW1tkuTjH/94kuS2227LqFGjUigU8sc//jGbbbZZkuTpp5/OUUcd1RzqbLLJJs3hzrLttwDej7322it77LFHfvazn+Xhhx9ObW3tChFO9+7dmwOeyy+/POedd17q6upsjwUAAAAAAADwASg61gGbbrppkmTKlCm59dZbM2rUqJSVlWXGjBnLbYd10UUX5Te/+U3mzp273PXCHeCD2nPPPXP11VfnM5/5TBYvXpxJkybllVdeSaFQyLt3W+zevXumTJmS7t275+67727BFQMAAAAAAACsmwqld38LS4splUorTKtY9tmCBQuy5557prGxMYVCIZWVlXnsscfSrl275nN/9KMf5fzzz8/hhx+eSy+9NNXV1Wv6FYD1TKlUyqOPPppTTjkljz/+eM4///wMGTIkm2666Qq/s+bPn5+ysrJ06dJlpb/PAAAAAAAAAFg5I1la0LLtrZqampq/6K6rq8sLL7yQJM2fbbbZZjn77LPT2NiY+fPnZ8KECcuFO9dff33Gjx+f9u3b5/zzz091dXU0WcBHVSgUsvvuu+eqq67KjjvumHHjxuXqq6/Oq6++usIEnm7duqVLly4pFovCHQAAAAAAAIAPwOSdFvDMM8+kc+fOadWqVRoaGlJZWZkk+c53vpNbbrklTz75ZA455JB8/vOfz1e/+tWUl5dn3rx5ueaaazJp0qR07tw5++23X/bee+/cd999uf/++9O2bdvU1NSkZ8+eaWpqSnl5eQu/JbA+efTRRzN06NDMmjUr5557bk466aR06NChpZcFAAAAAAAAsM4T76xhM2bMSP/+/XPGGWfk3HPPTevWrZMko0ePzoQJE7Ltttumffv2mT17dt58881885vfzIUXXpiKioo8//zzqampyYQJE/K3v/0tSdKjR4/0798/48aNyxZbbCHcAVabZQHPnDlzcuqpp2b48OFp3759Sy8LAAAAAAAAYJ0m3lnDZs2alQMOOCCvv/56Ro4cmeHDh2fhwoXp169fPv/5z+fss89Ot27d8vjjj+foo49ObW1thg8fngkTJqSioiJJUl9fn7lz52bJkiXZcccdU1VVlY022ki4A6x2f/nLX3LUUUeloqIiDz/8cNq0adPSSwIAAAAAAABYp4l3WsCsWbNyxBFHpLa2NmPGjMn222+fESNG5K677sqOO+6YUqmUQqGQ+fPnZ7/99su8efNWCHj+2bJrAFa3J554Ip06dUqXLl387gEAAAAAAAD4iMQ7a9iyL7pnzZqVL3zhC3nxxRfzqU99KvX19ampqUlDQ0MqKyvT2NiYioqKLFiwIJ/61Kcyb968jBgxIuPHj09FRUWKxWLKyspa+nWADZjfQwAAAAAAAAAfnXhnNXr3RIpisZhSqbTctlZ/+9vfMnDgwMyZMyc9evTIX//617Rt27b5C/GVBTwnnXRSrrzySttjAQAAAAAAAACsB4xMWE3+eSuZsrKy5uDmRz/6UR577LHsuOOO+fnPf55dd901zz77bM4///y8/vrrKSsrS7FYTEVFRRobG9OlS5dMnz49bdq0yZQpU/Laa6+11GsBAAAAAAAAALAKiXdWk2Xhzt57753DDz+8+fNhw4bl61//ep544ok0NDSkT58+uemmm7LDDjvkhz/8YS699NK89dZbKw145syZk8ceeyzt27ePgUkAAAAAAAAAAOu+ipZewPrs+eefz9tvv5277rorX//617Pxxhvnhz/8Yb7xjW9k//33T2VlZZKkT58+uf3223PEEUfkO9/5TpJk5MiRadWqVXPA09TUlE6dOiVJmpqabJsFAAAAAAAAALAeKJSMcFmtamtrc+qpp+aee+5Jkpx77rkZPnx4OnTosMK5Tz75ZI444og888wzGT16dHPA889bcAEAAAAAAAAAsH6wbdZqVCwWs9VWWy03Jefpp59uDncaGhqWO3+HHXbInXfema222irf+973csEFF+Ttt98W7gAAAAAAAAAArKfEO6vRsuhmiy22yFFHHZV99903P/3pT3PCCSckSSorK9PU1LTcNcsCnqqqqvz0pz/N0qVL1/i6AQAAAAAAAABYM2ybtRo1NDSksrKy+c/PPfdcTjjhhPzud7/L8ccfnx/96EdJkqVLl6aqqipJ8tprr6VNmzaZPXt2Ntlkk3Tr1s22WQAAAAAAAAAA6ynxzipULBZTVvZ/w4xWFt3MmjUrp512Wn77298uF/Akyb333pv77rsvX/3qV/Oxj30sSdLU1LTctlsAAAAAAAAAAKw/xDuryLsjmzvuuCMPP/xw/vKXv+Tzn/98PvWpT2XXXXdtPvepp57Kqaee2hzwXHLJJfnVr36VsWPH5u23386MGTPSuXPnFnoTAAAAAAAAAADWFPHOKvDuiTtnn312rrjiipSVlWWTTTbJwoULs/POO+fMM8/Mcccd13zN008/nW9+85u5995707Zt29TX16dz586ZOnVqevXqtcIUHwAAAAAAAAAA1j/inVVozJgxGTduXI499ticfPLJ+eQnP5lrr702Q4cOTc+ePfOtb30rgwcPbj5/3rx5mTx5ch5//PG0a9cuF1xwQbbYYos0NjamoqKi5V4EAAAAAAAAAIA1QrzzAZRKpRQKhRV+TpKf/exnOfXUU3PooYdmxIgR2W677VJXV5d+/frl2WefTV1dXTp27JiLLrooxx9/fPN1y7bbWrp0aaqqqpbbfgsAAAAAAAAAgPWbfZnep2KxuFysUygUUiwWkyT19fX53e9+l8bGxgwdOjTbbbddXn/99Xzyk5/MwoULM3ny5Pz3f/93Fi1alNGjR+faa69tvs+ydqqqqipJhDsAAAAAAAAAABsQ8c77UCqVUlb2zl/Vl7/85Vx00UVJkrKysuaoZ+utt86ll16a3XffPW+//XYOP/zwvPLKKxk3blwOOuignHDCCTn44IPz0ksvZfz48bnqqquSxPZYAAAAAAAAAAAbMOXI+7Bs4s4+++yTGTNm5MEHH0ybNm0ydOjQlJWVpaqqKkOGDGmemnPDDTdk2rRpGTFiRP7f//t/zVN1evfunV69emXu3LmZNGlSBg8enNatW7fYewEAAAAAAAAA0LLEO+9TbW1tXnzxxVRVVeW5557Leeedl7KysgwZMiRJsvHGGzef+8QTT6SsrCxnnnnmcnHOzJkzc+KJJ2b//fdPly5dhDsAAAAAAAAAABs422a9T126dEm/fv2y5ZZb5vTTT8+iRYsycuTIXHvttc3nNDQ0pKGhIQsWLEh9fX3uv//+5mO33nprZs+enerq6uy1117p2bNnGhsbW+JVAAAAAAAAAABYSxRKpVKppRextiuVSikUCnnsscey++67Z8KECdl1111z8MEHp23btrnoooty4oknNp9/33335eCDD84uu+ySQYMG5eWXX85tt92W6urqPPDAA+natWsLvg0AAAAAAAAAAGsL8c6/sSzaSZJisZiGhoZ86UtfyvTp0zN9+vTMmDEjgwYNSrt27fK9732vOeBpaGjItddem+HDh6e+vj7V1dXZZZddctttt6Vnz54pFospKzP0CAAAAAAAAABgQ1fR0gtYm/xzVLMs3CmVSikrK0t1dXWOPPLI3HnnnampqckJJ5yQ+vr6DB48OGeeeWaS5MQTT0xlZWWGDRuWvn375rHHHkvHjh2z1157ZdNNN01TU1PKy8tb5P0AAAAAAAAAAFi7mLzzv94d7gwePDg77bRTjjvuuHTq1Cnl5eVpbGxMRcU7rVP//v0zf/78zJgxI+3atcuNN96YL3/5y2nXrl0mTpyYIUOGvOczAAAAAAAAAABASfK/lkU1++yzT37yk5/krLPOyqGHHpphw4blhRdeSGNjY5J3ApzD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AAAAAAAAa4NC6d2jbdYixWIxZWXvDAb6/ve/n6lTp6ZYLGb//ffPsGHDstFGGyVJpk6dmsGDB+e5557LKaeckn79+mXbbbfNddddlzvvvDOlUinTp09Pjx49lrsnAAAAAAAAAAC0tLU23llm1KhRmThxYtq0aZP6+vosXbo0RxxxREaNGpU99tgjZWVlefDBBzN27Ng8+OCDWbp0aZJko402St++fXPNNdekR48ey03xAQAAAAAAAACAtcFaHe/84Q9/yNFHH53DDz88Z5xxRurr63P99dfnqquuyh577JFx48Zl3333TVlZWebPn5/58+dnxowZqayszB577JHevXunTZs2wh0AAAAAAAAAANZKa1W8UyqVUigUmv9/yy235PTTT88DDzyQ7bbbLkmycOHCTJ48OWPGjMnuu+++XMDz7+4JAAAAAAAAAABrm5UXLy2gqampObJ5++2389Zbb6Vz584ZMGBAtttuuzQ0NCRJOnfunMGDB+fb3/52/vznP+fcc8/N9OnTs6xB+ucWSbgDAAAAAAAAAMDaaq2YvFMsFpsn53zve9/LnXfemQULFqRYLKa6ujozZsxI27Ztlztv0aJF+fGPf5xvfetb2WuvvXL++eenf//+Yh0AAAAAAAAAANYZa8XknWVBzujRo3P22WfnhRdeSKFQyGuvvZann346P/jBD/Lmm2+mrKwsxWIxSdKxY8ccf/zxufDCC1NTU5MrrriieToPAAAAAAAAAACsC1p08k5TU1PKy8uTJLW1tenfv38+97nP5eyzz87GG2+ce+65J2PGjMnbb7+d//zP/8yxxx6bjTbaaLkJPC+//HLuvPPOHHLIIenevXtLvQoAAAAAAAAAAHxga8W2Wb/85S9TV1eXUaNG5a677spOO+2UJFm6dGlqamoydOjQNDQ0ZOzYsTnuuONWCHhKpVIKhcJyMRAAAAAAAAAAAKztWjze+a//+q98/etfz2c+85ksXbo0v//979PQ0JCKiormIGfq1KkZMmRIGhoacsEFF6x0Ag8AAAAAAAAAAKxrWjzeeeGFF/LFL34xDz30UNq3b59HH300PXv2XC7MeXfAUyqVMnLkyAwZMiTV1dUtuXQAAAAAAAAAAPhIWnxszRZbbJEpU6bkoIMOyj/+8Y+ce+65WbBgQcrKylIsFpMk5eXl2X///XPttddm0aJFuf7669PY2NjCKwcAAAAAAAAAgI9mjU3eKZVKKRQKK3ze1NSU8vLyvPTSSxk0aFDuv//+nH766Rk9enQ6deq0wgSehx56KL169crmm2++JpYNAAAAAAAAAACrzRqJd5YFOkny0ksvZcmSJWlsbMyOO+643HkvvfRSjjnmmDzwwAP5xje+kXPPPXeFgGdl9wQAAAAAAAAAgHXRat82692RzUUXXZRDDjkku+yyS/bee+988YtfzOOPP968BVbXrl1z6623pm/fvrn88sszbty4vPzyyykrK8s/N0bCHQAAAAAAAAAA1nWrdfLOu7fKGjlyZC699NLstNNOOeSQQ7Jw4cLcfvvt2XXXXXPOOefkgAMOSFVVVZL/m8Azffr0fOUrX8nEiRPTsWPH1bVMAAAAAAAAAABoEWtk26zLLrss5513Xk488cQMGTIkH/vYx/Lss8/mk5/8ZObPn59dd90148aNy2c/+9lUVlYmSRYsWJDPfvazefXVVzNz5sy0b99+dS8TAAAAAAAAAADWqNUe78ycOTODBw9Ot27dMnHixPTp0ydLlizJ3nvvnbq6uhx66KG5/fbb07t373z729/OgQce2DyB5+WXX06xWEyXLl2Wm+IDAAAAAAAAAADrg4rV/YDa2to8/fTTmTBhQvr06ZM33ngj/fr1y6uvvprLLrssn/70p7PxxhvniiuuyHe/+92UlZU1b6HVqVOnJEmxWExZWdnqXioAAAAAAAAAAKxRa2TbrHvvvTcDBgxIQ0NDhgwZkjvuuCPjx4/PiSeemOrq6kydOjUHHHBAqqur06FDh9x4443Zf//9V/eyAAAAAAAAAACgRa2ycTbFYnGFz5YuXZokGTBgQJJkwYIFqampSf/+/XPKKaekuro6SbL55punW7duOe2009K1a9f06dNnVS0LAAAAAAAAAADWWqsk3mlqamre1mrx4sV5/vnnkyRVVVVJkmXDfWbNmpVnn302u+2223LX//SnP02bNm1yyimnZNq0aenWrVuamppWxdIAAAAAAAAAAGCt9ZHjnWKxmPLy8iTJ+PHj069fv2y77bY56KCDMnny5CxZsiSFQiFJsvPOO6dnz5755S9/mblz5+a1117LTTfdlMmTJ2ebbbZJ165d06pVqyRpvicAAAAAAAAAAKyvCqVlY3E+onPOOSff/e53s80226R9+/aZO3duGhoactppp2XkyJHp0KFDlixZkvHjx+fiiy9Ot27d0rp16zz33HPp3Llzampq0qNHj5RKpebYBwAAAAAAAAAA1mcfevLOu7e1mjNnTm6++eYMGzYsv/3tbzN9+vTcfffd2X777TNhwoSMHz8+ixYtStu2bXP66afnkksuSffu3VNdXZ2BAwfmgQceSI8ePdLU1CTcAQAAAAAAAABgg/GRJ+9MmzYt8+bNy1lnnZVf/epX2XnnnZuPPfvsszn66KPzyCOPZMSIETnrrLOy2WabZdkjGxsbUygUUlFRkaamJltlAQAAAAAAAACwQfnQk3eSZNKkSenbt2+mTJmSXXbZJTvvvHOampqa45wePXrk9ttvzyc+8YlcfPHFmThxYl599dXm6TqVlZWpqKhIEuEOAAAAAAAAAAAbnI8U7+y1117ZY4898rOf/SwPP/xwamtrV4hwunfv3hzwXH755TnvvPNSV1dneywAAAAAAAAAADZ4Hyne2XPPPXP11VfnM5/5TBYvXpxJkybllVdeSaFQyLt34+revXumTJmS7t275+677/7IiwYAAAAAAAAAgPVBofTuyuZDKJVKefTRR3PKKafk8ccfz/nnn58hQ4Zk0003TalUWm7Czvz581NWVpYuXbqscAwAAAAAAAAAADY0HzneWebRRx/N0KFDM2vWrIwePTonnXTSSgOeJCkWiykr+0hDfwAAAAAAAAAAYJ23ygqa3XbbLT/84Q/Tp0+ffOc738k111yTxYsXr3S6jnAHAAAAAAAAAABW4eSdZZZN4JkzZ05OPfXUDB8+PO3bt1+VjwAAAAAAAAAAgPXCKo93kuQvf/lLjjrqqFRUVOThhx9OmzZtVvUjAAAAAAAAAABgnbda4p0keeKJJ9KpU6d06dIlpVJppdtnAQAAAAAAAADAhmy1xTvLFIvFlJWVrc5HAAAAAAAAAADAOmm1xzsAAAAAAAAAAMDKGYkDAAAAAAAAAAAtRLwDAAAAAAAAAAAtRLwDAAAAAAAAAAAtRLwDAAAAAAAAAAAtRLwDAAAAAAAAAAAtRLwDAAAAAAAAAAAtRLwDAAAAAAAAAAAtRLwDAAAA8AHdd999OeGEE7Lddtulbdu2qa6uTrdu3XLggQfm0ksvzcsvv/yB7ldbW5tCoZCtttpq9Sz4Q6qpqUmhUEj//v1beikAAAAA6y3xDgAAAMD79Morr+TAAw/MQQcdlOuvvz4NDQ3Zf//9c+SRR2aHHXbI9OnTM3z48PTq1St//OMfV8kzt9pqqxQKhdTW1q6S+wEAAACwdqlo6QUAAAAArAvq6uqy33775amnnkqfPn1y9dVXp2/fvsudU19fnx//+McZM2ZM5s+f/77vvcUWW+TJJ59MZWXlql72R7LXXnvlySefTOvWrVt6KQAAAADrrUKpVCq19CIAAAAA1nZf+cpXMnny5Gy11Vb505/+lE033fRfnrtgwYL84x//yPbbb/+Rn7vVVlvlmWeeybx589a6bbUAAAAA+OhsmwUAAADwHv7+97/npptuSpJccskl/zbcSZIuXbo0hztjx45NoVDI2LFj8+yzz+ZrX/taunfvnsrKygwePDhJUltbm0KhsFycc/3116dQKOSZZ55Jkmy99dYpFArN/9XU1Cz3zBdffDHDhw/PDjvskNatW6dNmzbZc889M2nSpDQ2Nq6wxsGDB6dQKOT666/PE088kWOOOSbdunVLeXl5xo4dmySpqalJoVBI//79V7j+N7/5TU477bTsuuuu2WyzzVJdXZ0tt9wyxxxzTB5++OH38bcKAAAAQGLbLAAAAID3dNddd6WpqSnt27fP4Ycf/qHuMXv27Oy2226pqqrKvvvum1KplM022+xfnr/NNtvk+OOPz+2335433ngjRx55ZDbZZJPm4127dm3++fe//30GDhyYxYsXZ6uttsqBBx6Y+vr6zJgxI6eddlp+8Ytf5K677lrptlzTp0/PySefnG7duqVfv35566230qZNm/d8n5NPPjnPPfdcPvaxj2XfffdNRUVFZs2aldtuuy133HFHbrnllhx55JEf8G8JAAAAYMMj3gEAAAB4D4888kiSZPfdd095efmHusdNN92U4447Ltdee22qq6vf8/z99tsv++23X2pqavLGG2/koosuWum2WS+99FL+4z/+I//4xz9y1VVXZejQoSkre2fY8qJFi/LFL34xv/71rzN+/Ph861vfWuH6a665JqNGjcq4ceOar3s/Lrroonz6059Ohw4dlvv8f/7nf3L00Udn6NChOfTQQ9OqVav3fU8AAACADZFtswAAAADew8svv5wk6dy584e+x6abbppJkya9r3Dng7jsssuyaNGinHLKKRk2bNhyAU7Hjh3zk5/8JJWVlZk0aVJKpdIK12+33Xa58MILP1C4kyQDBw5cIdxZ9vnRRx+dRYsWZerUqR/8hQAAAAA2MCbvAAAAAKwBn/3sZ9OuXbtVft+77747SXLMMces9PgWW2yRbbfdNn/7298ye/bsbLfddssdHzhw4IeeJvTiiy/m7rvvzqxZs1JXV5fGxsYkycyZM5MkTz31VA499NAPdW8AAACADYV4BwAAAOA9dOrUKUmycOHCD32PlW15tSr8/e9/T5L07dv3Pc99+eWXV4h3Puy6LrjggowbNy4NDQ3/8pwlS5Z8qHsDAAAAbEjEOwAAAADvYY899sjkyZPz5z//OU1NTR9qUk2rVq1Ww8qSYrGYJDnqqKOy8cYb/9tzO3bsuErWdccdd2Ts2LHZZJNNMmnSpHzmM5/J5ptvnlatWqVQKGT06NEZP378SrfpAgAAAGB54h0AAACA93DYYYdl+PDh+cc//pGf//znOeKII1p6Sc26d++e2bNn5+yzz84nPvGJNfLM2267LUkybty4nHTSSSscnz179hpZBwAAAMD6oKylFwAAAACwtuvdu3cGDRqUJBkxYkReffXVf3v+woUL89RTT62SZ1dVVSVJGhsbV3r8kEMOSfJ/Qc2asOz9e/bsucKxhQsX5r777ltjawEAAABY14l3AAAAAN6HK664Ittss03mzZuX/fbbLw8++OAK5yxdujTXXXdddttttzz55JOr5LlbbrllkmTmzJkrPX7mmWemffv2ueSSS3LxxRdn6dKlK5wzb9683HDDDatkPUmyww47JEmuvvrq5Z5XV1eX448/PnV1davsWQAAAADrO9tmAQAAALwPHTp0yLRp03LMMcekpqYmffv2zdZbb51ddtklrVu3zoIFCzJjxoy8/vrradu2bTbffPNV8twjjzwyU6dOzXHHHZeDDjooHTp0SPJOtLP99ttnyy23zM9+9rMceeSRGTlyZCZOnJiddtop3bp1S11dXZ588snMnTs3e++9d4477rhVsqZvfvOb+clPfpJ77rknvXr1yj777JOGhobcf//9ad26db761a/muuuuWyXPAgAAAFjfiXcAAAAA3qfOnTtn6tSpuffee3PzzTdn+vTp+e1vf5v6+vp07Ngxn/zkJ/O5z30uX/7yl7PpppuukmcOGzYsr732Wm644Ybcc889efvtt5Mkxx13XLbffvskSb9+/TJz5sxMmjQpd999dx5++OHU19enc+fO6dGjR4477rgceeSRq2Q9SbL11lvn0UcfzXnnnZcHHnggd911V7p27ZpBgwZl7Nix+cEPfrDKngUAAACwviuUSqVSSy8CAAAAAAAAAAA2RGUtvQAAAAAAAAAAANhQiXcAAAAAAAAAAKCFiHcAAAAAAAAAAKCFiHcAAAAAAAAAAKCFiHcAAAAAAAAAAKCFiHcAAAAAAAAAAKCFiHcAAAAAAAAAAKCFiHcAAAAAAAAAAKCFiHcAAAAAAAAAAKCFiHcAAAAAAAAAAKCFiHcAAAAAAAAAAKCFiHcAAAAAAAAAAKCFiHcAAAAAAAAAAKCF/H9N274RyqxjZwAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -3596,7 +2784,7 @@ "plt.title(\n", " \"Average Values of 3 different baselines cases with 95% Confidence Intervals - math problems \", fontsize=12, pad=10\n", ") # Adjust titlepad to move the title further above\n", - "plt.xticks(index + bar_width / 2, criteria, rotation=45, fontsize=14)\n", + "plt.xticks(index + bar_width / 2, [crit.name for crit in criteria], rotation=45, fontsize=14)\n", "plt.legend(loc=\"upper center\", fontsize=14, bbox_to_anchor=(0.5, 1), ncol=3) # Adjust legend placement and ncol\n", "plt.tight_layout() # Adjust subplot parameters to fit the labels\n", "plt.ylim(0, 5)\n", diff --git a/samples/apps/autogen-studio/.gitignore b/samples/apps/autogen-studio/.gitignore index e94e41454a8..e1e3c9942ec 100644 --- a/samples/apps/autogen-studio/.gitignore +++ b/samples/apps/autogen-studio/.gitignore @@ -1,6 +1,7 @@ database.sqlite .cache/* autogenstudio/web/files/user/* +autogenstudio/test autogenstudio/web/files/ui/* OAI_CONFIG_LIST scratch/ diff --git a/samples/apps/autogen-studio/README.md b/samples/apps/autogen-studio/README.md index 49f7e3d657b..1e60b5362db 100644 --- a/samples/apps/autogen-studio/README.md +++ b/samples/apps/autogen-studio/README.md @@ -15,6 +15,8 @@ Code for AutoGen Studio is on GitHub at [microsoft/autogen](https://github.com/m > AutoGen Studio is currently under active development and we are iterating quickly. Kindly consider that we may introduce breaking changes in the releases during the upcoming weeks, and also the `README` might be outdated. We'll update the `README` as soon as we stabilize the API. > [!NOTE] Updates +> April 17: AutoGen Studio database layer is now rewritten to use [SQLModel](https://sqlmodel.tiangolo.com/) (Pydantic + SQLAlchemy). This provides entity linking (skills, models, agents and workflows are linked via association tables) and supports multiple [database backend dialects](https://docs.sqlalchemy.org/en/20/dialects/) supported in SQLAlchemy (SQLite, PostgreSQL, MySQL, Oracle, Microsoft SQL Server). The backend database can be specified a `--database-uri` argument when running the application. For example, `autogenstudio ui --database-uri sqlite:///database.sqlite` for SQLite and `autogenstudio ui --database-uri postgresql+psycopg://user:password@localhost/dbname` for PostgreSQL. + > March 12: Default directory for AutoGen Studio is now /home//.autogenstudio. You can also specify this directory using the `--appdir` argument when running the application. For example, `autogenstudio ui --appdir /path/to/folder`. This will store the database and other files in the specified directory e.g. `/path/to/folder/database.sqlite`. `.env` files in that directory will be used to set environment variables for the app. ### Capabilities / Roadmap @@ -84,7 +86,14 @@ autogenstudio ui --port 8081 ``` This will start the application on the specified port. Open your web browser and go to `http://localhost:8081/` to begin using AutoGen Studio. -AutoGen Studio also takes a `--host ` argument to specify the host address. By default, it is set to `localhost`. You can also use the `--appdir ` argument to specify the directory where the app files (e.g., database and generated user files) are stored. By default, it is set to the directory where autogen pip package is installed. + +AutoGen Studio also takes several parameters to customize the application: + +- `--host ` argument to specify the host address. By default, it is set to `localhost`. Y +- `--appdir ` argument to specify the directory where the app files (e.g., database and generated user files) are stored. By default, it is set to the a `.autogenstudio` directory in the user's home directory. +- `--port ` argument to specify the port number. By default, it is set to `8080`. +- `--reload` argument to enable auto-reloading of the server when changes are made to the code. By default, it is set to `False`. +- `--database-uri` argument to specify the database URI. Example values include `sqlite:///database.sqlite` for SQLite and `postgresql+psycopg://user:password@localhost/dbname` for PostgreSQL. If this is not specified, the database URIL defaults to a `database.sqlite` file in the `--appdir` directory. Now that you have AutoGen Studio installed and running, you are ready to explore its capabilities, including defining and modifying agent workflows, interacting with agents and sessions, and expanding agent skills. @@ -98,8 +107,6 @@ AutoGen Studio proposes some high-level concepts. **Skills**: Skills are functions (e.g., Python functions) that describe how to solve a task. In general, a good skill has a descriptive name (e.g. `generate_images`), extensive docstrings and good defaults (e.g., writing out files to disk for persistence and reuse). You can add new skills AutoGen Studio app via the provided UI. At inference time, these skills are made available to the assistant agent as they address your tasks. -AutoGen Studio comes with 3 example skills: `fetch_profile`, `find_papers`, `generate_images`. The default skills, agents and workflows are based on the [dbdefaults.json](autogentstudio/utils/dbdefaults.json) file which is used to initialize the database. - ## Example Usage Consider the following query. @@ -116,8 +123,6 @@ The agent workflow responds by _writing and executing code_ to create a python p > Note: You can also view the debug console that generates useful information to see how the agents are interacting in the background. - - ## Contribution Guide We welcome contributions to AutoGen Studio. We recommend the following general steps to contribute to the project: @@ -134,7 +139,7 @@ We welcome contributions to AutoGen Studio. We recommend the following general s **Q: How do I specify the directory where files(e.g. database) are stored?** -A: You can specify the directory where files are stored by setting the `--appdir` argument when running the application. For example, `autogenstudio ui --appdir /path/to/folder`. This will store the database and other files in the specified directory e.g. `/path/to/folder/database.sqlite`. +A: You can specify the directory where files are stored by setting the `--appdir` argument when running the application. For example, `autogenstudio ui --appdir /path/to/folder`. This will store the database (default) and other files in the specified directory e.g. `/path/to/folder/database.sqlite`. **Q: Where can I adjust the default skills, agent and workflow configurations?** A: You can modify agent configurations directly from the UI or by editing the [dbdefaults.json](autogenstudio/utils/dbdefaults.json) file which is used to initialize the database. @@ -146,7 +151,7 @@ A: To reset your conversation history, you can delete the `database.sqlite` file A: Yes, you can view the generated messages in the debug console of the web UI, providing insights into the agent interactions. Alternatively, you can inspect the `database.sqlite` file for a comprehensive record of messages. **Q: Can I use other models with AutoGen Studio?** -Yes. AutoGen standardizes on the openai model api format, and you can use any api server that offers an openai compliant endpoint. In the AutoGen Studio UI, each agent has an `llm_config` field where you can input your model endpoint details including `model`, `api key`, `base url`, `model type` and `api version`. For Azure OpenAI models, you can find these details in the Azure portal. Note that for Azure OpenAI, the `model` is the deployment name or deployment id, and the `type` is "azure". +Yes. AutoGen standardizes on the openai model api format, and you can use any api server that offers an openai compliant endpoint. In the AutoGen Studio UI, each agent has an `llm_config` field where you can input your model endpoint details including `model`, `api key`, `base url`, `model type` and `api version`. For Azure OpenAI models, you can find these details in the Azure portal. Note that for Azure OpenAI, the `model name` is the deployment id or engine, and the `model type` is "azure". For other OSS models, we recommend using a server such as vllm to instantiate an openai compliant endpoint. **Q: The server starts but I can't access the UI** diff --git a/samples/apps/autogen-studio/autogenstudio/chatmanager.py b/samples/apps/autogen-studio/autogenstudio/chatmanager.py index 674ae3506a2..84b85673f07 100644 --- a/samples/apps/autogen-studio/autogenstudio/chatmanager.py +++ b/samples/apps/autogen-studio/autogenstudio/chatmanager.py @@ -4,14 +4,18 @@ import time from datetime import datetime from queue import Queue -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, Union import websockets from fastapi import WebSocket, WebSocketDisconnect -from .datamodel import AgentWorkFlowConfig, Message, SocketMessage -from .utils import extract_successful_code_blocks, get_modified_files, summarize_chat_history -from .workflowmanager import AutoGenWorkFlowManager +from .datamodel import Message, SocketMessage, Workflow +from .utils import ( + extract_successful_code_blocks, + get_modified_files, + summarize_chat_history, +) +from .workflowmanager import WorkflowManager class AutoGenChatManager: @@ -41,7 +45,7 @@ def chat( self, message: Message, history: List[Dict[str, Any]], - flow_config: Optional[AgentWorkFlowConfig] = None, + workflow: Any = None, connection_id: Optional[str] = None, user_dir: Optional[str] = None, **kwargs, @@ -59,78 +63,93 @@ def chat( """ # create a working director for workflow based on user_dir/session_id/time_hash - work_dir = os.path.join(user_dir, message.session_id, datetime.now().strftime("%Y%m%d_%H-%M-%S")) + work_dir = os.path.join( + user_dir, + str(message.session_id), + datetime.now().strftime("%Y%m%d_%H-%M-%S"), + ) os.makedirs(work_dir, exist_ok=True) # if no flow config is provided, use the default - if flow_config is None: - raise ValueError("flow_config must be specified") + if workflow is None: + raise ValueError("Workflow must be specified") - flow = AutoGenWorkFlowManager( - config=flow_config, + workflow_manager = WorkflowManager( + workflow=workflow, history=history, work_dir=work_dir, send_message_function=self.send, connection_id=connection_id, ) + workflow = Workflow.model_validate(workflow) + message_text = message.content.strip() start_time = time.time() - flow.run(message=f"{message_text}", clear_history=False) + workflow_manager.run(message=f"{message_text}", clear_history=False) end_time = time.time() metadata = { - "messages": flow.agent_history, - "summary_method": flow_config.summary_method, + "messages": workflow_manager.agent_history, + "summary_method": workflow.summary_method, "time": end_time - start_time, "files": get_modified_files(start_time, end_time, source_dir=work_dir), } - print("Modified files: ", len(metadata["files"])) - - output = self._generate_output(message_text, flow, flow_config) + output = self._generate_output(message_text, workflow_manager, workflow) output_message = Message( user_id=message.user_id, - root_msg_id=message.root_msg_id, role="assistant", content=output, - metadata=json.dumps(metadata), + meta=json.dumps(metadata), session_id=message.session_id, ) return output_message def _generate_output( - self, message_text: str, flow: AutoGenWorkFlowManager, flow_config: AgentWorkFlowConfig + self, + message_text: str, + workflow_manager: WorkflowManager, + workflow: Workflow, ) -> str: """ Generates the output response based on the workflow configuration and agent history. :param message_text: The text of the incoming message. - :param flow: An instance of `AutoGenWorkFlowManager`. + :param flow: An instance of `WorkflowManager`. :param flow_config: An instance of `AgentWorkFlowConfig`. :return: The output response as a string. """ output = "" - if flow_config.summary_method == "last": - successful_code_blocks = extract_successful_code_blocks(flow.agent_history) - last_message = flow.agent_history[-1]["message"]["content"] if flow.agent_history else "" + if workflow.summary_method == "last": + successful_code_blocks = extract_successful_code_blocks(workflow_manager.agent_history) + last_message = ( + workflow_manager.agent_history[-1]["message"]["content"] if workflow_manager.agent_history else "" + ) successful_code_blocks = "\n\n".join(successful_code_blocks) output = (last_message + "\n" + successful_code_blocks) if successful_code_blocks else last_message - elif flow_config.summary_method == "llm": - model = flow.config.receiver.config.llm_config.config_list[0] + elif workflow.summary_method == "llm": + client = workflow_manager.receiver.client status_message = SocketMessage( type="agent_status", - data={"status": "summarizing", "message": "Generating summary of agent dialogue"}, - connection_id=flow.connection_id, + data={ + "status": "summarizing", + "message": "Summarizing agent dialogue", + }, + connection_id=workflow_manager.connection_id, ) self.send(status_message.dict()) - output = summarize_chat_history(task=message_text, messages=flow.agent_history, model=model) + output = summarize_chat_history( + task=message_text, + messages=workflow_manager.agent_history, + client=client, + ) - elif flow_config.summary_method == "none": + elif workflow.summary_method == "none": output = "" return output @@ -141,7 +160,9 @@ class WebSocketConnectionManager: """ def __init__( - self, active_connections: List[Tuple[WebSocket, str]] = None, active_connections_lock: asyncio.Lock = None + self, + active_connections: List[Tuple[WebSocket, str]] = None, + active_connections_lock: asyncio.Lock = None, ) -> None: """ Initializes WebSocketConnectionManager with an optional list of active WebSocket connections. @@ -185,7 +206,7 @@ async def disconnect_all(self) -> None: for connection, _ in self.active_connections[:]: await self.disconnect(connection) - async def send_message(self, message: Dict, websocket: WebSocket) -> None: + async def send_message(self, message: Union[Dict, str], websocket: WebSocket) -> None: """ Sends a JSON message to a single WebSocket connection. @@ -202,7 +223,7 @@ async def send_message(self, message: Dict, websocket: WebSocket) -> None: print("Error: WebSocket connection closed normally") await self.disconnect(websocket) except Exception as e: - print(f"Error in sending message: {str(e)}") + print(f"Error in sending message: {str(e)}", message) await self.disconnect(websocket) async def broadcast(self, message: Dict) -> None: diff --git a/samples/apps/autogen-studio/autogenstudio/cli.py b/samples/apps/autogen-studio/autogenstudio/cli.py index aafb13317c8..42642bcd68a 100644 --- a/samples/apps/autogen-studio/autogenstudio/cli.py +++ b/samples/apps/autogen-studio/autogenstudio/cli.py @@ -1,10 +1,10 @@ import os +from typing import Optional import typer import uvicorn from typing_extensions import Annotated -from .utils.dbutils import DBManager from .version import VERSION app = typer.Typer() @@ -18,6 +18,7 @@ def ui( reload: Annotated[bool, typer.Option("--reload")] = False, docs: bool = False, appdir: str = None, + database_uri: Optional[str] = None, ): """ Run the AutoGen Studio UI. @@ -29,11 +30,14 @@ def ui( reload (bool, optional): Whether to reload the UI on code changes. Defaults to False. docs (bool, optional): Whether to generate API docs. Defaults to False. appdir (str, optional): Path to the AutoGen Studio app directory. Defaults to None. + database-uri (str, optional): Database URI to connect to. Defaults to None. Examples include sqlite:///autogenstudio.db, postgresql://user:password@localhost/autogenstudio. """ os.environ["AUTOGENSTUDIO_API_DOCS"] = str(docs) if appdir: os.environ["AUTOGENSTUDIO_APPDIR"] = appdir + if database_uri: + os.environ["AUTOGENSTUDIO_DATABASE_URI"] = database_uri uvicorn.run( "autogenstudio.web.app:app", diff --git a/samples/apps/autogen-studio/autogenstudio/database/__init__.py b/samples/apps/autogen-studio/autogenstudio/database/__init__.py new file mode 100644 index 00000000000..0518c24ba4f --- /dev/null +++ b/samples/apps/autogen-studio/autogenstudio/database/__init__.py @@ -0,0 +1,3 @@ +# from .dbmanager import * +from .dbmanager import * +from .utils import * diff --git a/samples/apps/autogen-studio/autogenstudio/database/alembic.ini b/samples/apps/autogen-studio/autogenstudio/database/alembic.ini new file mode 100644 index 00000000000..cd413a26066 --- /dev/null +++ b/samples/apps/autogen-studio/autogenstudio/database/alembic.ini @@ -0,0 +1,116 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = migrations + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to migrations/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/samples/apps/autogen-studio/autogenstudio/database/dbmanager.py b/samples/apps/autogen-studio/autogenstudio/database/dbmanager.py new file mode 100644 index 00000000000..00d3714b63f --- /dev/null +++ b/samples/apps/autogen-studio/autogenstudio/database/dbmanager.py @@ -0,0 +1,472 @@ +from datetime import datetime +from typing import Optional + +from loguru import logger +from sqlalchemy import exc +from sqlmodel import Session, SQLModel, and_, create_engine, select + +from ..datamodel import ( + Agent, + AgentLink, + AgentModelLink, + AgentSkillLink, + Model, + Response, + Skill, + Workflow, + WorkflowAgentLink, +) +from .utils import init_db_samples + +valid_link_types = ["agent_model", "agent_skill", "agent_agent", "workflow_agent"] + + +class DBManager: + """A class to manage database operations""" + + def __init__(self, engine_uri: str): + connection_args = {"check_same_thread": True} if "sqlite" in engine_uri else {} + self.engine = create_engine(engine_uri, connect_args=connection_args) + # run_migration(engine_uri=engine_uri) + + def create_db_and_tables(self): + """Create a new database and tables""" + try: + SQLModel.metadata.create_all(self.engine) + try: + init_db_samples(self) + except Exception as e: + logger.info("Error while initializing database samples: " + str(e)) + except Exception as e: + logger.info("Error while creating database tables:" + str(e)) + + def upsert(self, model: SQLModel): + """Create a new entity""" + # check if the model exists, update else add + status = True + model_class = type(model) + existing_model = None + + with Session(self.engine) as session: + try: + existing_model = session.exec(select(model_class).where(model_class.id == model.id)).first() + if existing_model: + model.updated_at = datetime.now() + for key, value in model.model_dump().items(): + setattr(existing_model, key, value) + model = existing_model + session.add(model) + else: + session.add(model) + session.commit() + session.refresh(model) + except Exception as e: + session.rollback() + logger.error("Error while upserting %s", e) + status = False + + response = Response( + message=( + f"{model_class.__name__} Updated Successfully " + if existing_model + else f"{model_class.__name__} Created Successfully" + ), + status=status, + data=model.model_dump(), + ) + + return response + + def _model_to_dict(self, model_obj): + return {col.name: getattr(model_obj, col.name) for col in model_obj.__table__.columns} + + def get_items( + self, + model_class: SQLModel, + session: Session, + filters: dict = None, + return_json: bool = False, + order: str = "desc", + ): + """List all entities""" + result = [] + status = True + status_message = "" + + try: + if filters: + conditions = [getattr(model_class, col) == value for col, value in filters.items()] + statement = select(model_class).where(and_(*conditions)) + + if hasattr(model_class, "created_at") and order: + if order == "desc": + statement = statement.order_by(model_class.created_at.desc()) + else: + statement = statement.order_by(model_class.created_at.asc()) + else: + statement = select(model_class) + + if return_json: + result = [self._model_to_dict(row) for row in session.exec(statement).all()] + else: + result = session.exec(statement).all() + status_message = f"{model_class.__name__} Retrieved Successfully" + except Exception as e: + session.rollback() + status = False + status_message = f"Error while fetching {model_class.__name__}" + logger.error("Error while getting %s: %s", model_class.__name__, e) + + response: Response = Response( + message=status_message, + status=status, + data=result, + ) + return response + + def get( + self, + model_class: SQLModel, + filters: dict = None, + return_json: bool = False, + order: str = "desc", + ): + """List all entities""" + + with Session(self.engine) as session: + response = self.get_items(model_class, session, filters, return_json, order) + return response + + def delete(self, model_class: SQLModel, filters: dict = None): + """Delete an entity""" + row = None + status_message = "" + status = True + + with Session(self.engine) as session: + try: + if filters: + conditions = [getattr(model_class, col) == value for col, value in filters.items()] + row = session.exec(select(model_class).where(and_(*conditions))).all() + else: + row = session.exec(select(model_class)).all() + if row: + for row in row: + session.delete(row) + session.commit() + status_message = f"{model_class.__name__} Deleted Successfully" + else: + print(f"Row with filters {filters} not found") + logger.info("Row with filters %s not found", filters) + status_message = "Row not found" + except exc.IntegrityError as e: + session.rollback() + logger.error("Integrity ... Error while deleting: %s", e) + status_message = f"The {model_class.__name__} is linked to another entity and cannot be deleted." + status = False + except Exception as e: + session.rollback() + logger.error("Error while deleting: %s", e) + status_message = f"Error while deleting: {e}" + status = False + response = Response( + message=status_message, + status=status, + data=None, + ) + return response + + def get_linked_entities( + self, + link_type: str, + primary_id: int, + return_json: bool = False, + agent_type: Optional[str] = None, + ): + """ + Get all entities linked to the primary entity. + + Args: + link_type (str): The type of link to retrieve, e.g., "agent_model". + primary_id (int): The identifier for the primary model. + return_json (bool): Whether to return the result as a JSON object. + + Returns: + List[SQLModel]: A list of linked entities. + """ + + linked_entities = [] + + if link_type not in valid_link_types: + return [] + + status = True + status_message = "" + + with Session(self.engine) as session: + try: + if link_type == "agent_model": + # get the agent + agent = self.get_items(Agent, filters={"id": primary_id}, session=session).data[0] + linked_entities = agent.models + elif link_type == "agent_skill": + agent = self.get_items(Agent, filters={"id": primary_id}, session=session).data[0] + linked_entities = agent.skills + elif link_type == "agent_agent": + agent = self.get_items(Agent, filters={"id": primary_id}, session=session).data[0] + linked_entities = agent.agents + elif link_type == "workflow_agent": + linked_entities = session.exec( + select(Agent) + .join(WorkflowAgentLink) + .where( + WorkflowAgentLink.workflow_id == primary_id, + WorkflowAgentLink.agent_type == agent_type, + ) + ).all() + except Exception as e: + logger.error("Error while getting linked entities: %s", e) + status_message = f"Error while getting linked entities: {e}" + status = False + if return_json: + linked_entities = [self._model_to_dict(row) for row in linked_entities] + + response = Response( + message=status_message, + status=status, + data=linked_entities, + ) + + return response + + def link( + self, + link_type: str, + primary_id: int, + secondary_id: int, + agent_type: Optional[str] = None, + ) -> Response: + """ + Link two entities together. + + Args: + link_type (str): The type of link to create, e.g., "agent_model". + primary_id (int): The identifier for the primary model. + secondary_id (int): The identifier for the secondary model. + agent_type (Optional[str]): The type of agent, e.g., "sender" or receiver. + + Returns: + Response: The response of the linking operation, including success status and message. + """ + + # TBD verify that is creator of the primary entity being linked + status = True + status_message = "" + primary_model = None + secondary_model = None + + if link_type not in valid_link_types: + status = False + status_message = f"Invalid link type: {link_type}. Valid link types are: {valid_link_types}" + else: + with Session(self.engine) as session: + try: + if link_type == "agent_model": + primary_model = session.exec(select(Agent).where(Agent.id == primary_id)).first() + secondary_model = session.exec(select(Model).where(Model.id == secondary_id)).first() + if primary_model is None or secondary_model is None: + status = False + status_message = "One or both entity records do not exist." + else: + # check if the link already exists + existing_link = session.exec( + select(AgentModelLink).where( + AgentModelLink.agent_id == primary_id, + AgentModelLink.model_id == secondary_id, + ) + ).first() + if existing_link: # link already exists + return Response( + message=( + f"{secondary_model.__class__.__name__} already linked " + f"to {primary_model.__class__.__name__}" + ), + status=False, + ) + else: + primary_model.models.append(secondary_model) + elif link_type == "agent_agent": + primary_model = session.exec(select(Agent).where(Agent.id == primary_id)).first() + secondary_model = session.exec(select(Agent).where(Agent.id == secondary_id)).first() + if primary_model is None or secondary_model is None: + status = False + status_message = "One or both entity records do not exist." + else: + # check if the link already exists + existing_link = session.exec( + select(AgentLink).where( + AgentLink.parent_id == primary_id, + AgentLink.agent_id == secondary_id, + ) + ).first() + if existing_link: + return Response( + message=( + f"{secondary_model.__class__.__name__} already linked " + f"to {primary_model.__class__.__name__}" + ), + status=False, + ) + else: + primary_model.agents.append(secondary_model) + + elif link_type == "agent_skill": + primary_model = session.exec(select(Agent).where(Agent.id == primary_id)).first() + secondary_model = session.exec(select(Skill).where(Skill.id == secondary_id)).first() + if primary_model is None or secondary_model is None: + status = False + status_message = "One or both entity records do not exist." + else: + # check if the link already exists + existing_link = session.exec( + select(AgentSkillLink).where( + AgentSkillLink.agent_id == primary_id, + AgentSkillLink.skill_id == secondary_id, + ) + ).first() + if existing_link: + return Response( + message=( + f"{secondary_model.__class__.__name__} already linked " + f"to {primary_model.__class__.__name__}" + ), + status=False, + ) + else: + primary_model.skills.append(secondary_model) + elif link_type == "workflow_agent": + primary_model = session.exec(select(Workflow).where(Workflow.id == primary_id)).first() + secondary_model = session.exec(select(Agent).where(Agent.id == secondary_id)).first() + if primary_model is None or secondary_model is None: + status = False + status_message = "One or both entity records do not exist." + else: + # check if the link already exists + existing_link = session.exec( + select(WorkflowAgentLink).where( + WorkflowAgentLink.workflow_id == primary_id, + WorkflowAgentLink.agent_id == secondary_id, + WorkflowAgentLink.agent_type == agent_type, + ) + ).first() + if existing_link: + return Response( + message=( + f"{secondary_model.__class__.__name__} already linked " + f"to {primary_model.__class__.__name__}" + ), + status=False, + ) + else: + # primary_model.agents.append(secondary_model) + workflow_agent_link = WorkflowAgentLink( + workflow_id=primary_id, + agent_id=secondary_id, + agent_type=agent_type, + ) + session.add(workflow_agent_link) + # add and commit the link + session.add(primary_model) + session.commit() + status_message = ( + f"{secondary_model.__class__.__name__} successfully linked " + f"to {primary_model.__class__.__name__}" + ) + + except Exception as e: + session.rollback() + logger.error("Error while linking: %s", e) + status = False + status_message = f"Error while linking due to an exception: {e}" + + response = Response( + message=status_message, + status=status, + ) + + return response + + def unlink( + self, + link_type: str, + primary_id: int, + secondary_id: int, + agent_type: Optional[str] = None, + ) -> Response: + """ + Unlink two entities. + + Args: + link_type (str): The type of link to remove, e.g., "agent_model". + primary_id (int): The identifier for the primary model. + secondary_id (int): The identifier for the secondary model. + agent_type (Optional[str]): The type of agent, e.g., "sender" or receiver. + + Returns: + Response: The response of the unlinking operation, including success status and message. + """ + status = True + status_message = "" + + if link_type not in valid_link_types: + status = False + status_message = f"Invalid link type: {link_type}. Valid link types are: {valid_link_types}" + return Response(message=status_message, status=status) + + with Session(self.engine) as session: + try: + if link_type == "agent_model": + existing_link = session.exec( + select(AgentModelLink).where( + AgentModelLink.agent_id == primary_id, + AgentModelLink.model_id == secondary_id, + ) + ).first() + elif link_type == "agent_skill": + existing_link = session.exec( + select(AgentSkillLink).where( + AgentSkillLink.agent_id == primary_id, + AgentSkillLink.skill_id == secondary_id, + ) + ).first() + elif link_type == "agent_agent": + existing_link = session.exec( + select(AgentLink).where( + AgentLink.parent_id == primary_id, + AgentLink.agent_id == secondary_id, + ) + ).first() + elif link_type == "workflow_agent": + existing_link = session.exec( + select(WorkflowAgentLink).where( + WorkflowAgentLink.workflow_id == primary_id, + WorkflowAgentLink.agent_id == secondary_id, + WorkflowAgentLink.agent_type == agent_type, + ) + ).first() + + if existing_link: + session.delete(existing_link) + session.commit() + status_message = "Link removed successfully." + else: + status = False + status_message = "Link does not exist." + + except Exception as e: + session.rollback() + logger.error("Error while unlinking: %s", e) + status = False + status_message = f"Error while unlinking due to an exception: {e}" + + return Response(message=status_message, status=status) diff --git a/samples/apps/autogen-studio/autogenstudio/database/migrations/README b/samples/apps/autogen-studio/autogenstudio/database/migrations/README new file mode 100644 index 00000000000..2500aa1bcf7 --- /dev/null +++ b/samples/apps/autogen-studio/autogenstudio/database/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. diff --git a/samples/apps/autogen-studio/autogenstudio/database/migrations/__init__.py b/samples/apps/autogen-studio/autogenstudio/database/migrations/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/samples/apps/autogen-studio/autogenstudio/database/migrations/env.py b/samples/apps/autogen-studio/autogenstudio/database/migrations/env.py new file mode 100644 index 00000000000..1431492ad91 --- /dev/null +++ b/samples/apps/autogen-studio/autogenstudio/database/migrations/env.py @@ -0,0 +1,80 @@ +import os +from logging.config import fileConfig + +from alembic import context +from sqlalchemy import engine_from_config, pool +from sqlmodel import SQLModel + +from autogenstudio.datamodel import * +from autogenstudio.utils import get_db_uri + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config +config.set_main_option("sqlalchemy.url", get_db_uri()) + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = SQLModel.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/samples/apps/autogen-studio/autogenstudio/database/migrations/script.py.mako b/samples/apps/autogen-studio/autogenstudio/database/migrations/script.py.mako new file mode 100644 index 00000000000..6ce3351093c --- /dev/null +++ b/samples/apps/autogen-studio/autogenstudio/database/migrations/script.py.mako @@ -0,0 +1,27 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} diff --git a/samples/apps/autogen-studio/autogenstudio/database/utils.py b/samples/apps/autogen-studio/autogenstudio/database/utils.py new file mode 100644 index 00000000000..c14003b414c --- /dev/null +++ b/samples/apps/autogen-studio/autogenstudio/database/utils.py @@ -0,0 +1,323 @@ +# from .util import get_app_root +import os +import time +from datetime import datetime +from pathlib import Path +from typing import Any + +from alembic import command, util +from alembic.config import Config +from loguru import logger + +# from ..utils.db_utils import get_db_uri +from sqlmodel import Session, create_engine, text + +from autogen.agentchat import AssistantAgent + +from ..datamodel import ( + Agent, + AgentConfig, + AgentType, + CodeExecutionConfigTypes, + Model, + Skill, + Workflow, + WorkflowAgentLink, +) + + +def workflow_from_id(workflow_id: int, dbmanager: Any): + workflow = dbmanager.get(Workflow, filters={"id": workflow_id}).data + if not workflow or len(workflow) == 0: + raise ValueError("The specified workflow does not exist.") + workflow = workflow[0].model_dump(mode="json") + workflow_agent_links = dbmanager.get(WorkflowAgentLink, filters={"workflow_id": workflow_id}).data + + def dump_agent(agent: Agent): + exclude = [] + if agent.type != AgentType.groupchat: + exclude = [ + "admin_name", + "messages", + "max_round", + "admin_name", + "speaker_selection_method", + "allow_repeat_speaker", + ] + return agent.model_dump(warnings=False, mode="json", exclude=exclude) + + def get_agent(agent_id): + with Session(dbmanager.engine) as session: + agent: Agent = dbmanager.get_items(Agent, filters={"id": agent_id}, session=session).data[0] + agent_dict = dump_agent(agent) + agent_dict["skills"] = [Skill.model_validate(skill.model_dump(mode="json")) for skill in agent.skills] + model_exclude = [ + "id", + "agent_id", + "created_at", + "updated_at", + "user_id", + "description", + ] + models = [model.model_dump(mode="json", exclude=model_exclude) for model in agent.models] + agent_dict["models"] = [model.model_dump(mode="json") for model in agent.models] + + if len(models) > 0: + agent_dict["config"]["llm_config"] = agent_dict.get("config", {}).get("llm_config", {}) + llm_config = agent_dict["config"]["llm_config"] + if llm_config: + llm_config["config_list"] = models + agent_dict["config"]["llm_config"] = llm_config + agent_dict["agents"] = [get_agent(agent.id) for agent in agent.agents] + return agent_dict + + for link in workflow_agent_links: + agent_dict = get_agent(link.agent_id) + workflow[str(link.agent_type.value)] = agent_dict + return workflow + + +def run_migration(engine_uri: str): + database_dir = Path(__file__).parent + script_location = database_dir / "migrations" + + engine = create_engine(engine_uri) + buffer = open(script_location / "alembic.log", "w") + alembic_cfg = Config(stdout=buffer) + alembic_cfg.set_main_option("script_location", str(script_location)) + alembic_cfg.set_main_option("sqlalchemy.url", engine_uri) + + print(f"Running migrations with engine_uri: {engine_uri}") + + should_initialize_alembic = False + with Session(engine) as session: + try: + session.exec(text("SELECT * FROM alembic_version")) + except Exception: + logger.info("Alembic not initialized") + should_initialize_alembic = True + else: + logger.info("Alembic already initialized") + + if should_initialize_alembic: + try: + logger.info("Initializing alembic") + command.ensure_version(alembic_cfg) + command.upgrade(alembic_cfg, "head") + logger.info("Alembic initialized") + except Exception as exc: + logger.error(f"Error initializing alembic: {exc}") + raise RuntimeError("Error initializing alembic") from exc + + logger.info(f"Running DB migrations in {script_location}") + + try: + buffer.write(f"{datetime.now().isoformat()}: Checking migrations\n") + command.check(alembic_cfg) + except Exception as exc: + if isinstance(exc, (util.exc.CommandError, util.exc.AutogenerateDiffsDetected)): + try: + command.upgrade(alembic_cfg, "head") + time.sleep(3) + except Exception as exc: + logger.error(f"Error running migrations: {exc}") + + try: + buffer.write(f"{datetime.now().isoformat()}: Checking migrations\n") + command.check(alembic_cfg) + except util.exc.AutogenerateDiffsDetected as exc: + logger.info(f"AutogenerateDiffsDetected: {exc}") + # raise RuntimeError( + # f"There's a mismatch between the models and the database.\n{exc}") + except util.exc.CommandError as exc: + logger.error(f"CommandError: {exc}") + # raise RuntimeError(f"Error running migrations: {exc}") + + +def init_db_samples(dbmanager: Any): + workflows = dbmanager.get(Workflow).data + workflow_names = [w.name for w in workflows] + if "Default Workflow" in workflow_names and "Travel Planning Workflow" in workflow_names: + logger.info("Database already initialized with Default and Travel Planning Workflows") + return + logger.info("Initializing database with Default and Travel Planning Workflows") + # models + gpt_4_model = Model( + model="gpt-4-1106-preview", description="OpenAI GPT-4 model", user_id="guestuser@gmail.com", api_type="open_ai" + ) + azure_model = Model( + model="gpt4-turbo", + description="Azure OpenAI model", + user_id="guestuser@gmail.com", + api_type="azure", + base_url="https://api.your azureendpoint.com/v1", + ) + zephyr_model = Model( + model="zephyr", + description="Local Huggingface Zephyr model via vLLM, LMStudio or Ollama", + base_url="http://localhost:1234/v1", + user_id="guestuser@gmail.com", + api_type="open_ai", + ) + + google_gemini_model = Model( + model="gemini-1.5-pro-latest", + description="Google's Gemini model", + user_id="guestuser@gmail.com", + api_type="google", + ) + + # skills + + generate_image_skill = Skill( + name="generate_images", + description="Generate and save images based on a user's query.", + content='\nfrom typing import List\nimport uuid\nimport requests # to perform HTTP requests\nfrom pathlib import Path\n\nfrom openai import OpenAI\n\n\ndef generate_and_save_images(query: str, image_size: str = "1024x1024") -> List[str]:\n """\n Function to paint, draw or illustrate images based on the users query or request. Generates images from a given query using OpenAI\'s DALL-E model and saves them to disk. Use the code below anytime there is a request to create an image.\n\n :param query: A natural language description of the image to be generated.\n :param image_size: The size of the image to be generated. (default is "1024x1024")\n :return: A list of filenames for the saved images.\n """\n\n client = OpenAI() # Initialize the OpenAI client\n response = client.images.generate(model="dall-e-3", prompt=query, n=1, size=image_size) # Generate images\n\n # List to store the file names of saved images\n saved_files = []\n\n # Check if the response is successful\n if response.data:\n for image_data in response.data:\n # Generate a random UUID as the file name\n file_name = str(uuid.uuid4()) + ".png" # Assuming the image is a PNG\n file_path = Path(file_name)\n\n img_url = image_data.url\n img_response = requests.get(img_url)\n if img_response.status_code == 200:\n # Write the binary content to a file\n with open(file_path, "wb") as img_file:\n img_file.write(img_response.content)\n print(f"Image saved to {file_path}")\n saved_files.append(str(file_path))\n else:\n print(f"Failed to download the image from {img_url}")\n else:\n print("No image data found in the response!")\n\n # Return the list of saved files\n return saved_files\n\n\n# Example usage of the function:\n# generate_and_save_images("A cute baby sea otter")\n', + user_id="guestuser@gmail.com", + ) + + # agents + user_proxy_config = AgentConfig( + name="user_proxy", + description="User Proxy Agent Configuration", + human_input_mode="NEVER", + max_consecutive_auto_reply=25, + system_message="You are a helpful assistant", + code_execution_config=CodeExecutionConfigTypes.local, + default_auto_reply="TERMINATE", + llm_config=False, + ) + user_proxy = Agent( + user_id="guestuser@gmail.com", type=AgentType.userproxy, config=user_proxy_config.model_dump(mode="json") + ) + + painter_assistant_config = AgentConfig( + name="default_assistant", + description="Assistant Agent", + human_input_mode="NEVER", + max_consecutive_auto_reply=25, + system_message=AssistantAgent.DEFAULT_SYSTEM_MESSAGE, + code_execution_config=CodeExecutionConfigTypes.none, + llm_config={}, + ) + painter_assistant = Agent( + user_id="guestuser@gmail.com", type=AgentType.assistant, config=painter_assistant_config.model_dump(mode="json") + ) + + planner_assistant_config = AgentConfig( + name="planner_assistant", + description="Assistant Agent", + human_input_mode="NEVER", + max_consecutive_auto_reply=25, + system_message="You are a helpful assistant that can suggest a travel plan for a user. You are the primary cordinator who will receive suggestions or advice from other agents (local_assistant, language_assistant). You must ensure that the finally plan integrates the suggestions from other agents or team members. YOUR FINAL RESPONSE MUST BE THE COMPLETE PLAN. When the plan is complete and all perspectives are integrated, you can respond with TERMINATE.", + code_execution_config=CodeExecutionConfigTypes.none, + llm_config={}, + ) + planner_assistant = Agent( + user_id="guestuser@gmail.com", type=AgentType.assistant, config=planner_assistant_config.model_dump(mode="json") + ) + + local_assistant_config = AgentConfig( + name="local_assistant", + description="Local Assistant Agent", + human_input_mode="NEVER", + max_consecutive_auto_reply=25, + system_message="You are a local assistant that can suggest local activities or places to visit for a user. You can suggest local activities, places to visit, restaurants to eat at, etc. You can also provide information about the weather, local events, etc. You can provide information about the local area, but you cannot suggest a complete travel plan. You can only provide information about the local area.", + code_execution_config=CodeExecutionConfigTypes.none, + llm_config={}, + ) + local_assistant = Agent( + user_id="guestuser@gmail.com", type=AgentType.assistant, config=local_assistant_config.model_dump(mode="json") + ) + + language_assistant_config = AgentConfig( + name="language_assistant", + description="Language Assistant Agent", + human_input_mode="NEVER", + max_consecutive_auto_reply=25, + system_message="You are a helpful assistant that can review travel plans, providing feedback on important/critical tips about how best to address language or communication challenges for the given destination. If the plan already includes language tips, you can mention that the plan is satisfactory, with rationale.", + code_execution_config=CodeExecutionConfigTypes.none, + llm_config={}, + ) + language_assistant = Agent( + user_id="guestuser@gmail.com", + type=AgentType.assistant, + config=language_assistant_config.model_dump(mode="json"), + ) + + # group chat + travel_groupchat_config = AgentConfig( + name="travel_groupchat", + admin_name="groupchat", + description="Group Chat Agent Configuration", + human_input_mode="NEVER", + max_consecutive_auto_reply=25, + system_message="You are a group chat manager", + code_execution_config=CodeExecutionConfigTypes.none, + default_auto_reply="TERMINATE", + llm_config={}, + speaker_selection_method="auto", + ) + travel_groupchat_agent = Agent( + user_id="guestuser@gmail.com", type=AgentType.groupchat, config=travel_groupchat_config.model_dump(mode="json") + ) + + # workflows + default_workflow = Workflow(name="Default Workflow", description="Default workflow", user_id="guestuser@gmail.com") + + travel_workflow = Workflow( + name="Travel Planning Workflow", description="Travel workflow", user_id="guestuser@gmail.com" + ) + + with Session(dbmanager.engine) as session: + session.add(zephyr_model) + session.add(google_gemini_model) + session.add(azure_model) + session.add(gpt_4_model) + session.add(generate_image_skill) + session.add(user_proxy) + session.add(painter_assistant) + session.add(travel_groupchat_agent) + session.add(planner_assistant) + session.add(local_assistant) + session.add(language_assistant) + + session.add(default_workflow) + session.add(travel_workflow) + session.commit() + + dbmanager.link(link_type="agent_model", primary_id=painter_assistant.id, secondary_id=gpt_4_model.id) + dbmanager.link(link_type="agent_skill", primary_id=painter_assistant.id, secondary_id=generate_image_skill.id) + dbmanager.link( + link_type="workflow_agent", primary_id=default_workflow.id, secondary_id=user_proxy.id, agent_type="sender" + ) + dbmanager.link( + link_type="workflow_agent", + primary_id=default_workflow.id, + secondary_id=painter_assistant.id, + agent_type="receiver", + ) + + # link agents to travel groupchat agent + + dbmanager.link(link_type="agent_agent", primary_id=travel_groupchat_agent.id, secondary_id=planner_assistant.id) + dbmanager.link(link_type="agent_agent", primary_id=travel_groupchat_agent.id, secondary_id=local_assistant.id) + dbmanager.link( + link_type="agent_agent", primary_id=travel_groupchat_agent.id, secondary_id=language_assistant.id + ) + dbmanager.link(link_type="agent_agent", primary_id=travel_groupchat_agent.id, secondary_id=user_proxy.id) + dbmanager.link(link_type="agent_model", primary_id=travel_groupchat_agent.id, secondary_id=gpt_4_model.id) + dbmanager.link(link_type="agent_model", primary_id=planner_assistant.id, secondary_id=gpt_4_model.id) + dbmanager.link(link_type="agent_model", primary_id=local_assistant.id, secondary_id=gpt_4_model.id) + dbmanager.link(link_type="agent_model", primary_id=language_assistant.id, secondary_id=gpt_4_model.id) + + dbmanager.link( + link_type="workflow_agent", primary_id=travel_workflow.id, secondary_id=user_proxy.id, agent_type="sender" + ) + dbmanager.link( + link_type="workflow_agent", + primary_id=travel_workflow.id, + secondary_id=travel_groupchat_agent.id, + agent_type="receiver", + ) + logger.info("Successfully initialized database with Default and Travel Planning Workflows") diff --git a/samples/apps/autogen-studio/autogenstudio/datamodel.py b/samples/apps/autogen-studio/autogenstudio/datamodel.py index 083bddccfcf..3dbd46c357e 100644 --- a/samples/apps/autogen-studio/autogenstudio/datamodel.py +++ b/samples/apps/autogen-studio/autogenstudio/datamodel.py @@ -1,318 +1,262 @@ -import uuid -from dataclasses import asdict, field from datetime import datetime +from enum import Enum from typing import Any, Callable, Dict, List, Literal, Optional, Union -from pydantic.dataclasses import dataclass - - -@dataclass -class Message(object): - user_id: str +from sqlalchemy import ForeignKey, Integer, orm +from sqlmodel import ( + JSON, + Column, + DateTime, + Field, + Relationship, + SQLModel, + func, +) +from sqlmodel import ( + Enum as SqlEnum, +) + +SQLModel.model_config["protected_namespaces"] = () +# pylint: disable=protected-access + + +class Message(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable + user_id: Optional[str] = None role: str content: str - root_msg_id: Optional[str] = None - msg_id: Optional[str] = None - timestamp: Optional[str] = None - personalize: Optional[bool] = False - ra: Optional[str] = None - code: Optional[str] = None - metadata: Optional[Any] = None - session_id: Optional[str] = None - - def __post_init__(self): - if self.msg_id is None: - self.msg_id = str(uuid.uuid4()) - if self.timestamp is None: - self.timestamp = datetime.now().isoformat() - - def dict(self): - result = asdict(self) - return result - - -@dataclass -class Skill(object): - title: str - content: str - file_name: Optional[str] = None - id: Optional[str] = None - description: Optional[str] = None - timestamp: Optional[str] = None + session_id: Optional[int] = Field( + default=None, sa_column=Column(Integer, ForeignKey("session.id", ondelete="CASCADE")) + ) + connection_id: Optional[str] = None + meta: Optional[Dict] = Field(default={}, sa_column=Column(JSON)) + + +class Session(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable user_id: Optional[str] = None + workflow_id: Optional[int] = Field(default=None, foreign_key="workflow.id") + name: Optional[str] = None + description: Optional[str] = None - def __post_init__(self): - if self.id is None: - self.id = str(uuid.uuid4()) - if self.timestamp is None: - self.timestamp = datetime.now().isoformat() - if self.user_id is None: - self.user_id = "default" - - def dict(self): - result = asdict(self) - return result +class AgentSkillLink(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + agent_id: int = Field(default=None, primary_key=True, foreign_key="agent.id") + skill_id: int = Field(default=None, primary_key=True, foreign_key="skill.id") -# web api data models +class AgentModelLink(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + agent_id: int = Field(default=None, primary_key=True, foreign_key="agent.id") + model_id: int = Field(default=None, primary_key=True, foreign_key="model.id") -# autogenflow data models -@dataclass -class Model: - """Data model for Model Config item in LLMConfig for AutoGen""" - model: str - api_key: Optional[str] = None - base_url: Optional[str] = None - api_type: Optional[str] = None - api_version: Optional[str] = None - id: Optional[str] = None - timestamp: Optional[str] = None +class Skill(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable user_id: Optional[str] = None + name: str + content: str description: Optional[str] = None + secrets: Optional[Dict] = Field(default={}, sa_column=Column(JSON)) + libraries: Optional[Dict] = Field(default={}, sa_column=Column(JSON)) + agents: List["Agent"] = Relationship(back_populates="skills", link_model=AgentSkillLink) - def dict(self): - result = asdict(self) - return result - def __post_init__(self): - if self.id is None: - self.id = str(uuid.uuid4()) - if self.timestamp is None: - self.timestamp = datetime.now().isoformat() - if self.user_id is None: - self.user_id = "default" - - -@dataclass -class LLMConfig: +class LLMConfig(SQLModel, table=False): """Data model for LLM Config for AutoGen""" - config_list: List[Any] = field(default_factory=list) + config_list: List[Any] = Field(default_factory=list) temperature: float = 0 cache_seed: Optional[Union[int, None]] = None timeout: Optional[int] = None - max_tokens: Optional[int] = None + max_tokens: Optional[int] = 1000 extra_body: Optional[dict] = None - def dict(self): - result = asdict(self) - result["config_list"] = [c.dict() for c in self.config_list] - return result +class ModelTypes(str, Enum): + openai = "open_ai" + google = "google" + azure = "azure" -@dataclass -class AgentConfig: - """Data model for Agent Config for AutoGen""" - name: str - llm_config: Optional[Union[LLMConfig, bool]] = False +class Model(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable + user_id: Optional[str] = None + model: str + api_key: Optional[str] = None + base_url: Optional[str] = None + api_type: ModelTypes = Field(default=ModelTypes.openai, sa_column=Column(SqlEnum(ModelTypes))) + api_version: Optional[str] = None + description: Optional[str] = None + agents: List["Agent"] = Relationship(back_populates="models", link_model=AgentModelLink) + + +class CodeExecutionConfigTypes(str, Enum): + local = "local" + docker = "docker" + none = "none" + + +class AgentConfig(SQLModel, table=False): + name: Optional[str] = None human_input_mode: str = "NEVER" max_consecutive_auto_reply: int = 10 system_message: Optional[str] = None is_termination_msg: Optional[Union[bool, str, Callable]] = None - code_execution_config: Optional[Union[bool, str, Dict[str, Any]]] = None + code_execution_config: CodeExecutionConfigTypes = Field( + default=CodeExecutionConfigTypes.local, sa_column=Column(SqlEnum(CodeExecutionConfigTypes)) + ) default_auto_reply: Optional[str] = "" description: Optional[str] = None + llm_config: Optional[Union[LLMConfig, bool]] = Field(default=False, sa_column=Column(JSON)) - def dict(self): - result = asdict(self) - if isinstance(result["llm_config"], LLMConfig): - result["llm_config"] = result["llm_config"].dict() - return result - - -@dataclass -class AgentFlowSpec: - """Data model to help flow load agents from config""" - - type: Literal["assistant", "userproxy"] - config: AgentConfig - id: Optional[str] = None - timestamp: Optional[str] = None - user_id: Optional[str] = None - skills: Optional[Union[None, List[Skill]]] = None - - def __post_init__(self): - if self.timestamp is None: - self.timestamp = datetime.now().isoformat() - if self.id is None: - self.id = str(uuid.uuid4()) - if self.user_id is None: - self.user_id = "default" - - def dict(self): - result = asdict(self) - return result - - -@dataclass -class GroupChatConfig: - """Data model for GroupChat Config for AutoGen""" - - agents: List[AgentFlowSpec] = field(default_factory=list) - admin_name: str = "Admin" - messages: List[Dict] = field(default_factory=list) - max_round: Optional[int] = 10 admin_name: Optional[str] = "Admin" + messages: Optional[List[Dict]] = Field(default_factory=list) + max_round: Optional[int] = 100 speaker_selection_method: Optional[str] = "auto" - # TODO: match the new group chat default and support transition spec - allow_repeat_speaker: Optional[Union[bool, List[AgentConfig]]] = True + allow_repeat_speaker: Optional[Union[bool, List["AgentConfig"]]] = True - def dict(self): - result = asdict(self) - result["agents"] = [a.dict() for a in self.agents] - return result +class AgentType(str, Enum): + assistant = "assistant" + userproxy = "userproxy" + groupchat = "groupchat" -@dataclass -class GroupChatFlowSpec: - """Data model to help flow load agents from config""" - type: Literal["groupchat"] - config: AgentConfig = field(default_factory=AgentConfig) - groupchat_config: Optional[GroupChatConfig] = field(default_factory=GroupChatConfig) - id: Optional[str] = None - timestamp: Optional[str] = None - user_id: Optional[str] = None - skills: Optional[Union[None, List[Skill]]] = None +class WorkflowAgentType(str, Enum): + sender = "sender" + receiver = "receiver" + planner = "planner" - def __post_init__(self): - if self.timestamp is None: - self.timestamp = datetime.now().isoformat() - if self.id is None: - self.id = str(uuid.uuid4()) - if self.user_id is None: - self.user_id = "default" - def dict(self): - result = asdict(self) - # result["config"] = self.config.dict() - # result["groupchat_config"] = self.groupchat_config.dict() - return result +class WorkflowAgentLink(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + workflow_id: int = Field(default=None, primary_key=True, foreign_key="workflow.id") + agent_id: int = Field(default=None, primary_key=True, foreign_key="agent.id") + agent_type: WorkflowAgentType = Field( + default=WorkflowAgentType.sender, + sa_column=Column(SqlEnum(WorkflowAgentType), primary_key=True), + ) -@dataclass -class AgentWorkFlowConfig: - """Data model for Flow Config for AutoGen""" +class AgentLink(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + parent_id: Optional[int] = Field(default=None, foreign_key="agent.id", primary_key=True) + agent_id: Optional[int] = Field(default=None, foreign_key="agent.id", primary_key=True) + +class Agent(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable + user_id: Optional[str] = None + type: AgentType = Field(default=AgentType.assistant, sa_column=Column(SqlEnum(AgentType))) + config: AgentConfig = Field(default_factory=AgentConfig, sa_column=Column(JSON)) + skills: List[Skill] = Relationship(back_populates="agents", link_model=AgentSkillLink) + models: List[Model] = Relationship(back_populates="agents", link_model=AgentModelLink) + workflows: List["Workflow"] = Relationship(link_model=WorkflowAgentLink, back_populates="agents") + parents: List["Agent"] = Relationship( + back_populates="agents", + link_model=AgentLink, + sa_relationship_kwargs=dict( + primaryjoin="Agent.id==AgentLink.agent_id", + secondaryjoin="Agent.id==AgentLink.parent_id", + ), + ) + agents: List["Agent"] = Relationship( + back_populates="parents", + link_model=AgentLink, + sa_relationship_kwargs=dict( + primaryjoin="Agent.id==AgentLink.parent_id", + secondaryjoin="Agent.id==AgentLink.agent_id", + ), + ) + + +class WorkFlowType(str, Enum): + twoagents = "twoagents" + groupchat = "groupchat" + + +class WorkFlowSummaryMethod(str, Enum): + last = "last" + none = "none" + llm = "llm" + + +class Workflow(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable + user_id: Optional[str] = None name: str description: str - sender: AgentFlowSpec - receiver: Union[AgentFlowSpec, GroupChatFlowSpec] - type: Literal["twoagents", "groupchat"] = "twoagents" - id: Optional[str] = None - user_id: Optional[str] = None - timestamp: Optional[str] = None - # how the agent message summary is generated. last: only last message is used, none: no summary, llm: use llm to generate summary - summary_method: Optional[Literal["last", "none", "llm"]] = "last" - - def init_spec(self, spec: Dict): - """initialize the agent spec""" - if not isinstance(spec, dict): - spec = spec.dict() - if spec["type"] == "groupchat": - return GroupChatFlowSpec(**spec) - else: - return AgentFlowSpec(**spec) - - def __post_init__(self): - if self.id is None: - self.id = str(uuid.uuid4()) - self.sender = self.init_spec(self.sender) - self.receiver = self.init_spec(self.receiver) - if self.user_id is None: - self.user_id = "default" - if self.timestamp is None: - self.timestamp = datetime.now().isoformat() - - def dict(self): - result = asdict(self) - result["sender"] = self.sender.dict() - result["receiver"] = self.receiver.dict() - return result - - -@dataclass -class Session(object): - """Data model for AutoGen Chat Session""" - - user_id: str - id: Optional[str] = None - timestamp: Optional[str] = None - flow_config: AgentWorkFlowConfig = None - name: Optional[str] = None - description: Optional[str] = None + agents: List[Agent] = Relationship(back_populates="workflows", link_model=WorkflowAgentLink) + type: WorkFlowType = Field(default=WorkFlowType.twoagents, sa_column=Column(SqlEnum(WorkFlowType))) + summary_method: Optional[WorkFlowSummaryMethod] = Field( + default=WorkFlowSummaryMethod.last, + sa_column=Column(SqlEnum(WorkFlowSummaryMethod)), + ) - def __post_init__(self): - if self.timestamp is None: - self.timestamp = datetime.now().isoformat() - if self.id is None: - self.id = str(uuid.uuid4()) - - def dict(self): - result = asdict(self) - result["flow_config"] = self.flow_config.dict() - return result - - -@dataclass -class Gallery(object): - """Data model for Gallery Item""" - - session: Session - messages: List[Message] - tags: List[str] - id: Optional[str] = None - timestamp: Optional[str] = None - - def __post_init__(self): - if self.timestamp is None: - self.timestamp = datetime.now().isoformat() - if self.id is None: - self.id = str(uuid.uuid4()) - - def dict(self): - result = asdict(self) - return result - - -@dataclass -class ChatWebRequestModel(object): - """Data model for Chat Web Request for Web End""" - - message: Message - flow_config: AgentWorkFlowConfig - - -@dataclass -class DeleteMessageWebRequestModel(object): - user_id: str - msg_id: str - session_id: Optional[str] = None - - -@dataclass -class DBWebRequestModel(object): - user_id: str - msg_id: Optional[str] = None - session: Optional[Session] = None - skill: Optional[Skill] = None - tags: Optional[List[str]] = None - agent: Optional[AgentFlowSpec] = None - workflow: Optional[AgentWorkFlowConfig] = None - model: Optional[Model] = None - message: Optional[Message] = None - connection_id: Optional[str] = None + +class Response(SQLModel): + message: str + status: bool + data: Optional[Any] = None -@dataclass -class SocketMessage(object): +class SocketMessage(SQLModel, table=False): connection_id: str data: Dict[str, Any] type: str - - def dict(self): - result = asdict(self) - return result diff --git a/samples/apps/autogen-studio/autogenstudio/utils/__init__.py b/samples/apps/autogen-studio/autogenstudio/utils/__init__.py index f37b0b0486a..16281fe0b66 100644 --- a/samples/apps/autogen-studio/autogenstudio/utils/__init__.py +++ b/samples/apps/autogen-studio/autogenstudio/utils/__init__.py @@ -1,2 +1 @@ -from .dbutils import * from .utils import * diff --git a/samples/apps/autogen-studio/autogenstudio/utils/dbutils.py b/samples/apps/autogen-studio/autogenstudio/utils/dbutils.py deleted file mode 100644 index dca0fc6b0a6..00000000000 --- a/samples/apps/autogen-studio/autogenstudio/utils/dbutils.py +++ /dev/null @@ -1,860 +0,0 @@ -import json -import logging -import os -import sqlite3 -import threading -from typing import Any, Dict, List, Optional, Tuple - -from ..datamodel import AgentFlowSpec, AgentWorkFlowConfig, Gallery, Message, Model, Session, Skill -from ..version import __version__ as __db_version__ - -VERSION_TABLE_SQL = """ - CREATE TABLE IF NOT EXISTS version ( - - version TEXT NOT NULL, - UNIQUE (version) - ) - """ - -MODELS_TABLE_SQL = """ - CREATE TABLE IF NOT EXISTS models ( - id TEXT NOT NULL, - user_id TEXT NOT NULL, - timestamp DATETIME NOT NULL, - model TEXT, - api_key TEXT, - base_url TEXT, - api_type TEXT, - api_version TEXT, - description TEXT, - UNIQUE (id, user_id) - ) - """ - - -MESSAGES_TABLE_SQL = """ - CREATE TABLE IF NOT EXISTS messages ( - user_id TEXT NOT NULL, - session_id TEXT, - root_msg_id TEXT NOT NULL, - msg_id TEXT, - role TEXT NOT NULL, - content TEXT NOT NULL, - metadata TEXT, - timestamp DATETIME, - UNIQUE (user_id, root_msg_id, msg_id) - ) - """ - -SESSIONS_TABLE_SQL = """ - CREATE TABLE IF NOT EXISTS sessions ( - id TEXT NOT NULL, - user_id TEXT NOT NULL, - timestamp DATETIME NOT NULL, - name TEXT, - flow_config TEXT, - UNIQUE (user_id, id) - ) - """ - -SKILLS_TABLE_SQL = """ - CREATE TABLE IF NOT EXISTS skills ( - id TEXT NOT NULL, - user_id TEXT NOT NULL, - timestamp DATETIME NOT NULL, - content TEXT, - title TEXT, - file_name TEXT, - UNIQUE (id, user_id) - ) - """ -AGENTS_TABLE_SQL = """ - CREATE TABLE IF NOT EXISTS agents ( - - id TEXT NOT NULL, - user_id TEXT NOT NULL, - timestamp DATETIME NOT NULL, - config TEXT, - type TEXT, - skills TEXT, - UNIQUE (id, user_id) - ) - """ - -WORKFLOWS_TABLE_SQL = """ - CREATE TABLE IF NOT EXISTS workflows ( - id TEXT NOT NULL, - user_id TEXT NOT NULL, - timestamp DATETIME NOT NULL, - sender TEXT, - receiver TEXT, - type TEXT, - name TEXT, - description TEXT, - summary_method TEXT, - UNIQUE (id, user_id) - ) - """ - -GALLERY_TABLE_SQL = """ - CREATE TABLE IF NOT EXISTS gallery ( - id TEXT NOT NULL, - session TEXT, - messages TEXT, - tags TEXT, - timestamp DATETIME NOT NULL, - UNIQUE ( id) - ) - """ - - -lock = threading.Lock() -logger = logging.getLogger() - - -class DBManager: - """ - A database manager class that handles the creation and interaction with an SQLite database. - """ - - def __init__(self, path: str = "database.sqlite", **kwargs: Any) -> None: - """ - Initializes the DBManager object, creates a database if it does not exist, and establishes a connection. - - Args: - path (str): The file path to the SQLite database file. - **kwargs: Additional keyword arguments to pass to the sqlite3.connect method. - """ - - self.path = path - # check if the database exists, if not create it - # self.reset_db() - if not os.path.exists(self.path): - logger.info("Creating database") - self.init_db(path=self.path, **kwargs) - - try: - self.conn = sqlite3.connect(self.path, check_same_thread=False, **kwargs) - self.cursor = self.conn.cursor() - self.migrate() - except Exception as e: - logger.error("Error connecting to database: %s", e) - raise e - - def migrate(self): - """ - Run migrations to update the database schema. - """ - self.add_column_if_not_exists("sessions", "name", "TEXT") - self.add_column_if_not_exists("models", "description", "TEXT") - - def add_column_if_not_exists(self, table: str, column: str, column_type: str): - """ - Adds a new column to the specified table if it does not exist. - - Args: - table (str): The table name where the column should be added. - column (str): The column name that should be added. - column_type (str): The data type of the new column. - """ - try: - self.cursor.execute(f"PRAGMA table_info({table})") - column_names = [row[1] for row in self.cursor.fetchall()] - if column not in column_names: - self.cursor.execute(f"ALTER TABLE {table} ADD COLUMN {column} {column_type}") - self.conn.commit() - logger.info(f"Migration: New '{column}' column has been added to the '{table}' table.") - else: - logger.info(f"'{column}' column already exists in the '{table}' table.") - - except Exception as e: - print(f"Error while checking and updating '{table}' table: {e}") - - def reset_db(self): - """ - Reset the database by deleting the database file and creating a new one. - """ - print("resetting db") - if os.path.exists(self.path): - os.remove(self.path) - self.init_db(path=self.path) - - def init_db(self, path: str = "database.sqlite", **kwargs: Any) -> None: - """ - Initializes the database by creating necessary tables. - - Args: - path (str): The file path to the SQLite database file. - **kwargs: Additional keyword arguments to pass to the sqlite3.connect method. - """ - # Connect to the database (or create a new one if it doesn't exist) - self.conn = sqlite3.connect(path, check_same_thread=False, **kwargs) - self.cursor = self.conn.cursor() - - # Create the version table - self.cursor.execute(VERSION_TABLE_SQL) - self.cursor.execute("INSERT INTO version (version) VALUES (?)", (__db_version__,)) - - # Create the models table - self.cursor.execute(MODELS_TABLE_SQL) - - # Create the messages table - self.cursor.execute(MESSAGES_TABLE_SQL) - - # Create a sessions table - self.cursor.execute(SESSIONS_TABLE_SQL) - - # Create a skills - self.cursor.execute(SKILLS_TABLE_SQL) - - # Create a gallery table - self.cursor.execute(GALLERY_TABLE_SQL) - - # Create a agents table - self.cursor.execute(AGENTS_TABLE_SQL) - - # Create a workflows table - self.cursor.execute(WORKFLOWS_TABLE_SQL) - - # init skills table with content of defaultskills.json in current directory - current_dir = os.path.dirname(os.path.realpath(__file__)) - with open(os.path.join(current_dir, "dbdefaults.json"), "r", encoding="utf-8") as json_file: - data = json.load(json_file) - skills = data["skills"] - agents = data["agents"] - models = data["models"] - for model in models: - model = Model(**model) - self.cursor.execute( - "INSERT INTO models (id, user_id, timestamp, model, api_key, base_url, api_type, api_version, description) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", - ( - model.id, - "default", - model.timestamp, - model.model, - model.api_key, - model.base_url, - model.api_type, - model.api_version, - model.description, - ), - ) - - for skill in skills: - skill = Skill(**skill) - - self.cursor.execute( - "INSERT INTO skills (id, user_id, timestamp, content, title, file_name) VALUES (?, ?, ?, ?, ?, ?)", - (skill.id, "default", skill.timestamp, skill.content, skill.title, skill.file_name), - ) - for agent in agents: - agent = AgentFlowSpec(**agent) - agent.skills = [skill.dict() for skill in agent.skills] if agent.skills else None - self.cursor.execute( - "INSERT INTO agents (id, user_id, timestamp, config, type, skills) VALUES (?, ?, ?, ?, ?, ?)", - ( - agent.id, - "default", - agent.timestamp, - json.dumps(agent.config.dict()), - agent.type, - json.dumps(agent.skills), - ), - ) - - for workflow in data["workflows"]: - workflow = AgentWorkFlowConfig(**workflow) - self.cursor.execute( - "INSERT INTO workflows (id, user_id, timestamp, sender, receiver, type, name, description, summary_method) VALUES (?, ?, ?, ?, ?, ?, ?, ?,?)", - ( - workflow.id, - "default", - workflow.timestamp, - json.dumps(workflow.sender.dict()), - json.dumps(workflow.receiver.dict()), - workflow.type, - workflow.name, - workflow.description, - workflow.summary_method, - ), - ) - - # Commit the changes and close the connection - self.conn.commit() - - def query(self, query: str, args: Tuple = (), return_json: bool = False) -> List[Dict[str, Any]]: - """ - Executes a given SQL query and returns the results. - - Args: - query (str): The SQL query to execute. - args (Tuple): The arguments to pass to the SQL query. - return_json (bool): If True, the results will be returned as a list of dictionaries. - - Returns: - List[Dict[str, Any]]: The result of the SQL query. - """ - try: - with lock: - self.cursor.execute(query, args) - result = self.cursor.fetchall() - self.commit() - if return_json: - result = [dict(zip([key[0] for key in self.cursor.description], row)) for row in result] - return result - except Exception as e: - logger.error("Error running query with query %s and args %s: %s", query, args, e) - raise e - - def commit(self) -> None: - """ - Commits the current transaction Modelto the database. - """ - self.conn.commit() - - def close(self) -> None: - """ - Closes the database connection. - """ - self.conn.close() - - -def get_models(user_id: str, dbmanager: DBManager) -> List[dict]: - """ - Get all models for a given user from the database. - - Args: - user_id: The user id to get models for - dbmanager: The DBManager instance to interact with the database - - Returns: - A list of model configurations - """ - query = "SELECT * FROM models WHERE user_id = ? OR user_id = ?" - args = (user_id, "default") - results = dbmanager.query(query, args, return_json=True) - return results - - -def upsert_model(model: Model, dbmanager: DBManager) -> List[dict]: - """ - Insert or update a model configuration in the database. - - Args: - model: The Model object containing model configuration data - dbmanager: The DBManager instance to interact with the database - - Returns: - A list of model configurations - """ - - # Check if the model config with the provided id already exists in the database - existing_model = get_item_by_field("models", "id", model.id, dbmanager) - - if existing_model: - # If the model config exists, update it with the new data - updated_data = { - "model": model.model, - "api_key": model.api_key, - "base_url": model.base_url, - "api_type": model.api_type, - "api_version": model.api_version, - "user_id": model.user_id, - "timestamp": model.timestamp, - "description": model.description, - } - update_item("models", model.id, updated_data, dbmanager) - else: - # If the model config does not exist, insert a new one - query = """ - INSERT INTO models (id, user_id, timestamp, model, api_key, base_url, api_type, api_version, description) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - """ - args = ( - model.id, - model.user_id, - model.timestamp, - model.model, - model.api_key, - model.base_url, - model.api_type, - model.api_version, - model.description, - ) - dbmanager.query(query=query, args=args) - - # Return the inserted or updated model config - models = get_models(model.user_id, dbmanager) - return models - - -def delete_model(model: Model, dbmanager: DBManager) -> List[dict]: - """ - Delete a model configuration from the database where id = model.id and user_id = model.user_id. - - Args: - model: The Model object containing model configuration data - dbmanager: The DBManager instance to interact with the database - - Returns: - A list of model configurations - """ - - query = "DELETE FROM models WHERE id = ? AND user_id = ?" - args = (model.id, model.user_id) - dbmanager.query(query=query, args=args) - - # Return the remaining model configs - models = get_models(model.user_id, dbmanager) - return models - - -def create_message(message: Message, dbmanager: DBManager) -> List[dict]: - """ - Save a message in the database using the provided database manager. - - :param message: The Message object containing message data - :param dbmanager: The DBManager instance used to interact with the database - """ - query = "INSERT INTO messages (user_id, root_msg_id, msg_id, role, content, metadata, timestamp, session_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" - args = ( - message.user_id, - message.root_msg_id, - message.msg_id, - message.role, - message.content, - message.metadata, - message.timestamp, - message.session_id, - ) - dbmanager.query(query=query, args=args) - messages = get_messages(user_id=message.user_id, session_id=message.session_id, dbmanager=dbmanager) - return messages - - -def get_messages(user_id: str, session_id: str, dbmanager: DBManager) -> List[dict]: - """ - Load messages for a specific user and session from the database, sorted by timestamp. - - :param user_id: The ID of the user whose messages are to be loaded - :param session_id: The ID of the session whose messages are to be loaded - :param dbmanager: The DBManager instance to interact with the database - - :return: A list of dictionaries, each representing a message - """ - query = "SELECT * FROM messages WHERE user_id = ? AND session_id = ?" - args = (user_id, session_id) - result = dbmanager.query(query=query, args=args, return_json=True) - # Sort by timestamp ascending - result = sorted(result, key=lambda k: k["timestamp"], reverse=False) - return result - - -def get_sessions(user_id: str, dbmanager: DBManager) -> List[dict]: - """ - Load sessions for a specific user from the database, sorted by timestamp. - - :param user_id: The ID of the user whose sessions are to be loaded - :param dbmanager: The DBManager instance to interact with the database - :return: A list of dictionaries, each representing a session - """ - query = "SELECT * FROM sessions WHERE user_id = ?" - args = (user_id,) - result = dbmanager.query(query=query, args=args, return_json=True) - # Sort by timestamp ascending - result = sorted(result, key=lambda k: k["timestamp"], reverse=True) - for row in result: - row["flow_config"] = json.loads(row["flow_config"]) - return result - - -def create_session(user_id: str, session: Session, dbmanager: DBManager) -> List[dict]: - """ - Create a new session for a specific user in the database. - - :param user_id: The ID of the user whose session is to be created - :param dbmanager: The DBManager instance to interact with the database - :return: A list of dictionaries, each representing a session - """ - query = "INSERT INTO sessions (user_id, id, timestamp, flow_config) VALUES (?, ?, ?,?)" - args = (session.user_id, session.id, session.timestamp, json.dumps(session.flow_config.dict())) - dbmanager.query(query=query, args=args) - sessions = get_sessions(user_id=user_id, dbmanager=dbmanager) - - return sessions - - -def rename_session(name: str, session: Session, dbmanager: DBManager) -> List[dict]: - """ - Edit a session for a specific user in the database. - - :param name: The new name of the session - :param session: The Session object containing session data - :param dbmanager: The DBManager instance to interact with the database - :return: A list of dictionaries, each representing a session - """ - - query = "UPDATE sessions SET name = ? WHERE id = ?" - args = (name, session.id) - dbmanager.query(query=query, args=args) - sessions = get_sessions(user_id=session.user_id, dbmanager=dbmanager) - - return sessions - - -def delete_session(session: Session, dbmanager: DBManager) -> List[dict]: - """ - Delete a specific session and all messages for that session in the database. - - :param session: The Session object containing session data - :param dbmanager: The DBManager instance to interact with the database - :return: A list of the remaining sessions - """ - - query = "DELETE FROM sessions WHERE id = ?" - args = (session.id,) - dbmanager.query(query=query, args=args) - - query = "DELETE FROM messages WHERE session_id = ?" - args = (session.id,) - dbmanager.query(query=query, args=args) - - return get_sessions(user_id=session.user_id, dbmanager=dbmanager) - - -def create_gallery(session: Session, dbmanager: DBManager, tags: List[str] = []) -> Gallery: - """ - Publish a session to the gallery table in the database. Fetches the session messages first, then saves session and messages object to the gallery database table. - :param session: The Session object containing session data - :param dbmanager: The DBManager instance used to interact with the database - :param tags: A list of tags to associate with the session - :return: A gallery object containing the session and messages objects - """ - - messages = get_messages(user_id=session.user_id, session_id=session.id, dbmanager=dbmanager) - gallery_item = Gallery(session=session, messages=messages, tags=tags) - query = "INSERT INTO gallery (id, session, messages, tags, timestamp) VALUES (?, ?, ?, ?,?)" - args = ( - gallery_item.id, - json.dumps(gallery_item.session.dict()), - json.dumps([message.dict() for message in gallery_item.messages]), - json.dumps(gallery_item.tags), - gallery_item.timestamp, - ) - dbmanager.query(query=query, args=args) - return gallery_item - - -def get_gallery(gallery_id, dbmanager: DBManager) -> List[Gallery]: - """ - Load gallery items from the database, sorted by timestamp. If gallery_id is provided, only the gallery item with the matching gallery_id will be returned. - - :param gallery_id: The ID of the gallery item to be loaded - :param dbmanager: The DBManager instance to interact with the database - :return: A list of Gallery objects - """ - - if gallery_id: - query = "SELECT * FROM gallery WHERE id = ?" - args = (gallery_id,) - else: - query = "SELECT * FROM gallery" - args = () - result = dbmanager.query(query=query, args=args, return_json=True) - # Sort by timestamp ascending - result = sorted(result, key=lambda k: k["timestamp"], reverse=True) - gallery = [] - for row in result: - gallery_item = Gallery( - id=row["id"], - session=Session(**json.loads(row["session"])), - messages=[Message(**message) for message in json.loads(row["messages"])], - tags=json.loads(row["tags"]), - timestamp=row["timestamp"], - ) - gallery.append(gallery_item) - return gallery - - -def get_skills(user_id: str, dbmanager: DBManager) -> List[Skill]: - """ - Load skills from the database, sorted by timestamp. Load skills where id = user_id or user_id = default. - - :param user_id: The ID of the user whose skills are to be loaded - :param dbmanager: The DBManager instance to interact with the database - :return: A list of Skill objects - """ - - query = "SELECT * FROM skills WHERE user_id = ? OR user_id = ?" - args = (user_id, "default") - result = dbmanager.query(query=query, args=args, return_json=True) - # Sort by timestamp ascending - result = sorted(result, key=lambda k: k["timestamp"], reverse=True) - skills = [] - for row in result: - skill = Skill(**row) - skills.append(skill) - return skills - - -def upsert_skill(skill: Skill, dbmanager: DBManager) -> List[Skill]: - """ - Insert or update a skill for a specific user in the database. - - If the skill with the given ID already exists, it will be updated with the new data. - Otherwise, a new skill will be created. - - :param skill: The Skill object containing skill data - :param dbmanager: The DBManager instance to interact with the database - :return: A list of dictionaries, each representing a skill - """ - - existing_skill = get_item_by_field("skills", "id", skill.id, dbmanager) - - if existing_skill: - updated_data = { - "user_id": skill.user_id, - "timestamp": skill.timestamp, - "content": skill.content, - "title": skill.title, - "file_name": skill.file_name, - } - update_item("skills", skill.id, updated_data, dbmanager) - else: - query = "INSERT INTO skills (id, user_id, timestamp, content, title, file_name) VALUES (?, ?, ?, ?, ?, ?)" - args = (skill.id, skill.user_id, skill.timestamp, skill.content, skill.title, skill.file_name) - dbmanager.query(query=query, args=args) - - skills = get_skills(user_id=skill.user_id, dbmanager=dbmanager) - - return skills - - -def delete_skill(skill: Skill, dbmanager: DBManager) -> List[Skill]: - """ - Delete a skill for a specific user in the database. - - :param skill: The Skill object containing skill data - :param dbmanager: The DBManager instance to interact with the database - :return: A list of dictionaries, each representing a skill - """ - # delete where id = skill.id and user_id = skill.user_id - query = "DELETE FROM skills WHERE id = ? AND user_id = ?" - args = (skill.id, skill.user_id) - dbmanager.query(query=query, args=args) - - return get_skills(user_id=skill.user_id, dbmanager=dbmanager) - - -def delete_message( - user_id: str, msg_id: str, session_id: str, dbmanager: DBManager, delete_all: bool = False -) -> List[dict]: - """ - Delete a specific message or all messages for a user and session from the database. - - :param user_id: The ID of the user whose messages are to be deleted - :param msg_id: The ID of the specific message to be deleted (ignored if delete_all is True) - :param session_id: The ID of the session whose messages are to be deleted - :param dbmanager: The DBManager instance to interact with the database - :param delete_all: If True, all messages for the user will be deleted - :return: A list of the remaining messages if not all were deleted, otherwise an empty list - """ - - if delete_all: - query = "DELETE FROM messages WHERE user_id = ? AND session_id = ?" - args = (user_id, session_id) - dbmanager.query(query=query, args=args) - return [] - else: - query = "DELETE FROM messages WHERE user_id = ? AND msg_id = ? AND session_id = ?" - args = (user_id, msg_id, session_id) - dbmanager.query(query=query, args=args) - messages = get_messages(user_id=user_id, session_id=session_id, dbmanager=dbmanager) - return messages - - -def get_agents(user_id: str, dbmanager: DBManager) -> List[AgentFlowSpec]: - """ - Load agents from the database, sorted by timestamp. Load agents where id = user_id or user_id = default. - - :param user_id: The ID of the user whose agents are to be loaded - :param dbmanager: The DBManager instance to interact with the database - :return: A list of AgentFlowSpec objects - """ - - query = "SELECT * FROM agents WHERE user_id = ? OR user_id = ?" - args = (user_id, "default") - result = dbmanager.query(query=query, args=args, return_json=True) - # Sort by timestamp ascending - result = sorted(result, key=lambda k: k["timestamp"], reverse=True) - agents = [] - for row in result: - row["config"] = json.loads(row["config"]) - row["skills"] = json.loads(row["skills"] or "[]") - agent = AgentFlowSpec(**row) - agents.append(agent) - return agents - - -def upsert_agent(agent_flow_spec: AgentFlowSpec, dbmanager: DBManager) -> List[Dict[str, Any]]: - """ - Insert or update an agent for a specific user in the database. - - If the agent with the given ID already exists, it will be updated with the new data. - Otherwise, a new agent will be created. - - :param agent_flow_spec: The AgentFlowSpec object containing agent configuration - :param dbmanager: The DBManager instance to interact with the database - :return: A list of dictionaries, each representing an agent after insertion or update - """ - - existing_agent = get_item_by_field("agents", "id", agent_flow_spec.id, dbmanager) - - if existing_agent: - updated_data = { - "user_id": agent_flow_spec.user_id, - "timestamp": agent_flow_spec.timestamp, - "config": json.dumps(agent_flow_spec.config.dict()), - "type": agent_flow_spec.type, - "skills": json.dumps([x.dict() for x in agent_flow_spec.skills] if agent_flow_spec.skills else []), - } - update_item("agents", agent_flow_spec.id, updated_data, dbmanager) - else: - query = "INSERT INTO agents (id, user_id, timestamp, config, type, skills) VALUES (?, ?, ?, ?, ?,?)" - config_json = json.dumps(agent_flow_spec.config.dict()) - args = ( - agent_flow_spec.id, - agent_flow_spec.user_id, - agent_flow_spec.timestamp, - config_json, - agent_flow_spec.type, - json.dumps([x.dict() for x in agent_flow_spec.skills] if agent_flow_spec.skills else []), - ) - dbmanager.query(query=query, args=args) - - agents = get_agents(user_id=agent_flow_spec.user_id, dbmanager=dbmanager) - return agents - - -def delete_agent(agent: AgentFlowSpec, dbmanager: DBManager) -> List[Dict[str, Any]]: - """ - Delete an agent for a specific user from the database. - - :param agent: The AgentFlowSpec object containing agent configuration - :param dbmanager: The DBManager instance to interact with the database - :return: A list of dictionaries, each representing an agent after deletion - """ - - # delete based on agent.id and agent.user_id - query = "DELETE FROM agents WHERE id = ? AND user_id = ?" - args = (agent.id, agent.user_id) - dbmanager.query(query=query, args=args) - - return get_agents(user_id=agent.user_id, dbmanager=dbmanager) - - -def get_item_by_field(table: str, field: str, value: Any, dbmanager: DBManager) -> Optional[Dict[str, Any]]: - query = f"SELECT * FROM {table} WHERE {field} = ?" - args = (value,) - result = dbmanager.query(query=query, args=args) - return result[0] if result else None - - -def update_item(table: str, item_id: str, updated_data: Dict[str, Any], dbmanager: DBManager) -> None: - set_clause = ", ".join([f"{key} = ?" for key in updated_data.keys()]) - query = f"UPDATE {table} SET {set_clause} WHERE id = ?" - args = (*updated_data.values(), item_id) - dbmanager.query(query=query, args=args) - - -def get_workflows(user_id: str, dbmanager: DBManager) -> List[Dict[str, Any]]: - """ - Load workflows for a specific user from the database, sorted by timestamp. - - :param user_id: The ID of the user whose workflows are to be loaded - :param dbmanager: The DBManager instance to interact with the database - :return: A list of dictionaries, each representing a workflow - """ - query = "SELECT * FROM workflows WHERE user_id = ? OR user_id = ?" - args = (user_id, "default") - result = dbmanager.query(query=query, args=args, return_json=True) - # Sort by timestamp ascending - result = sorted(result, key=lambda k: k["timestamp"], reverse=True) - workflows = [] - for row in result: - row["sender"] = json.loads(row["sender"]) - row["receiver"] = json.loads(row["receiver"]) - workflow = AgentWorkFlowConfig(**row) - workflows.append(workflow) - return workflows - - -def upsert_workflow(workflow: AgentWorkFlowConfig, dbmanager: DBManager) -> List[Dict[str, Any]]: - """ - Insert or update a workflow for a specific user in the database. - - If the workflow with the given ID already exists, it will be updated with the new data. - Otherwise, a new workflow will be created. - - :param workflow: The AgentWorkFlowConfig object containing workflow data - :param dbmanager: The DBManager instance to interact with the database - :return: A list of dictionaries, each representing a workflow after insertion or update - """ - existing_workflow = get_item_by_field("workflows", "id", workflow.id, dbmanager) - - # print(workflow.receiver) - - if existing_workflow: - updated_data = { - "user_id": workflow.user_id, - "timestamp": workflow.timestamp, - "sender": json.dumps(workflow.sender.dict()), - "receiver": json.dumps( - [receiver.dict() for receiver in workflow.receiver] - if isinstance(workflow.receiver, list) - else workflow.receiver.dict() - ), - "type": workflow.type, - "name": workflow.name, - "description": workflow.description, - "summary_method": workflow.summary_method, - } - update_item("workflows", workflow.id, updated_data, dbmanager) - else: - query = "INSERT INTO workflows (id, user_id, timestamp, sender, receiver, type, name, description, summary_method) VALUES (?, ?, ?, ?, ?, ?, ?, ?,?)" - args = ( - workflow.id, - workflow.user_id, - workflow.timestamp, - json.dumps(workflow.sender.dict()), - json.dumps( - [receiver.dict() for receiver in workflow.receiver] - if isinstance(workflow.receiver, list) - else workflow.receiver.dict() - ), - workflow.type, - workflow.name, - workflow.description, - workflow.summary_method, - ) - dbmanager.query(query=query, args=args) - - return get_workflows(user_id=workflow.user_id, dbmanager=dbmanager) - - -def delete_workflow(workflow: AgentWorkFlowConfig, dbmanager: DBManager) -> List[Dict[str, Any]]: - """ - Delete a workflow for a specific user from the database. If the workflow does not exist, do nothing. - - :param workflow: The AgentWorkFlowConfig object containing workflow data - :param dbmanager: The DBManager instance to interact with the database - :return: A list of dictionaries, each representing a workflow after deletion - """ - - # delete where workflow.id =id and workflow.user_id = user_id - - query = "DELETE FROM workflows WHERE id = ? AND user_id = ?" - args = (workflow.id, workflow.user_id) - dbmanager.query(query=query, args=args) - - return get_workflows(user_id=workflow.user_id, dbmanager=dbmanager) diff --git a/samples/apps/autogen-studio/autogenstudio/utils/utils.py b/samples/apps/autogen-studio/autogenstudio/utils/utils.py index 49a8ac91acd..ed533ec3883 100644 --- a/samples/apps/autogen-studio/autogenstudio/utils/utils.py +++ b/samples/apps/autogen-studio/autogenstudio/utils/utils.py @@ -3,15 +3,17 @@ import os import re import shutil +from datetime import datetime from pathlib import Path -from typing import Dict, List, Tuple, Union +from typing import Any, Dict, List, Tuple, Union from dotenv import load_dotenv +from loguru import logger -import autogen -from autogen.oai.client import OpenAIWrapper +from autogen.coding import DockerCommandLineCodeExecutor, LocalCommandLineCodeExecutor +from autogen.oai.client import ModelClient, OpenAIWrapper -from ..datamodel import AgentConfig, AgentFlowSpec, AgentWorkFlowConfig, LLMConfig, Model, Skill +from ..datamodel import CodeExecutionConfigTypes, Model, Skill from ..version import APP_NAME @@ -25,6 +27,23 @@ def md5_hash(text: str) -> str: return hashlib.md5(text.encode()).hexdigest() +def check_and_cast_datetime_fields(obj: Any) -> Any: + if hasattr(obj, "created_at") and isinstance(obj.created_at, str): + obj.created_at = str_to_datetime(obj.created_at) + + if hasattr(obj, "updated_at") and isinstance(obj.updated_at, str): + obj.updated_at = str_to_datetime(obj.updated_at) + + return obj + + +def str_to_datetime(dt_str: str) -> datetime: + if dt_str[-1] == "Z": + # Replace 'Z' with '+00:00' for UTC timezone + dt_str = dt_str[:-1] + "+00:00" + return datetime.fromisoformat(dt_str) + + def clear_folder(folder_path: str) -> None: """ Clear the contents of a folder. @@ -98,7 +117,16 @@ def get_file_type(file_path: str) -> str: CSV_EXTENSIONS = {".csv", ".xlsx"} # Supported image extensions - IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".svg", ".webp"} + IMAGE_EXTENSIONS = { + ".png", + ".jpg", + ".jpeg", + ".gif", + ".bmp", + ".tiff", + ".svg", + ".webp", + } # Supported (web) video extensions VIDEO_EXTENSIONS = {".mp4", ".webm", ".ogg", ".mov", ".avi", ".wmv"} @@ -199,20 +227,42 @@ def get_modified_files(start_timestamp: float, end_timestamp: float, source_dir: return modified_files -def init_app_folders(app_file_path: str) -> Dict[str, str]: +def get_app_root() -> str: """ - Initialize folders needed for a web server, such as static file directories - and user-specific data directories. Also load any .env file if it exists. + Get the root directory of the application. - :param root_file_path: The root directory where webserver folders will be created - :return: A dictionary with the path of each created folder + :return: The root directory of the application. """ - app_name = f".{APP_NAME}" default_app_root = os.path.join(os.path.expanduser("~"), app_name) if not os.path.exists(default_app_root): os.makedirs(default_app_root, exist_ok=True) app_root = os.environ.get("AUTOGENSTUDIO_APPDIR") or default_app_root + return app_root + + +def get_db_uri(app_root: str) -> str: + """ + Get the default database URI for the application. + + :param app_root: The root directory of the application. + :return: The default database URI. + """ + db_uri = f"sqlite:///{os.path.join(app_root, 'database.sqlite')}" + db_uri = os.environ.get("AUTOGENSTUDIO_DATABASE_URI") or db_uri + logger.info(f"Using database URI: {db_uri}") + return db_uri + + +def init_app_folders(app_file_path: str) -> Dict[str, str]: + """ + Initialize folders needed for a web server, such as static file directories + and user-specific data directories. Also load any .env file if it exists. + + :param root_file_path: The root directory where webserver folders will be created + :return: A dictionary with the path of each created folder + """ + app_root = get_app_root() if not os.path.exists(app_root): os.makedirs(app_root, exist_ok=True) @@ -220,7 +270,7 @@ def init_app_folders(app_file_path: str) -> Dict[str, str]: # load .env file if it exists env_file = os.path.join(app_root, ".env") if os.path.exists(env_file): - print(f"Loading environment variables from {env_file}") + logger.info(f"Loaded environment variables from {env_file}") load_dotenv(env_file) files_static_root = os.path.join(app_root, "files/") @@ -233,8 +283,9 @@ def init_app_folders(app_file_path: str) -> Dict[str, str]: "files_static_root": files_static_root, "static_folder_root": static_folder_root, "app_root": app_root, + "database_engine_uri": get_db_uri(app_root=app_root), } - print(f"Initialized application data folder: {app_root}") + logger.info(f"Initialized application data folder: {app_root}") return folders @@ -258,11 +309,11 @@ def get_skills_from_prompt(skills: List[Skill], work_dir: str) -> str: for skill in skills: prompt += f""" -##### Begin of {skill.title} ##### +##### Begin of {skill.name} ##### {skill.content} -#### End of {skill.title} #### +#### End of {skill.name} #### """ @@ -290,7 +341,6 @@ def delete_files_in_folder(folders: Union[str, List[str]]) -> None: for folder in folders: # Check if the folder exists if not os.path.isdir(folder): - print(f"The folder {folder} does not exist.") continue # List all the entries in the directory @@ -306,56 +356,7 @@ def delete_files_in_folder(folders: Union[str, List[str]]) -> None: shutil.rmtree(path) except Exception as e: # Print the error message and skip - print(f"Failed to delete {path}. Reason: {e}") - - -def get_default_agent_config(work_dir: str) -> AgentWorkFlowConfig: - """ - Get a default agent flow config . - """ - - llm_config = LLMConfig( - config_list=[{"model": "gpt-4"}], - temperature=0, - ) - - USER_PROXY_INSTRUCTIONS = """If the request has been addressed sufficiently, summarize the answer and end with the word TERMINATE. Otherwise, ask a follow-up question. - """ - - userproxy_spec = AgentFlowSpec( - type="userproxy", - config=AgentConfig( - name="user_proxy", - human_input_mode="NEVER", - system_message=USER_PROXY_INSTRUCTIONS, - code_execution_config={ - "work_dir": work_dir, - "use_docker": False, - }, - max_consecutive_auto_reply=10, - llm_config=llm_config, - is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"), - ), - ) - - assistant_spec = AgentFlowSpec( - type="assistant", - config=AgentConfig( - name="primary_assistant", - system_message=autogen.AssistantAgent.DEFAULT_SYSTEM_MESSAGE, - llm_config=llm_config, - ), - ) - - flow_config = AgentWorkFlowConfig( - name="default", - sender=userproxy_spec, - receiver=assistant_spec, - type="default", - description="Default agent flow config", - ) - - return flow_config + logger.info(f"Failed to delete {path}. Reason: {e}") def extract_successful_code_blocks(messages: List[Dict[str, str]]) -> List[str]: @@ -392,7 +393,7 @@ def sanitize_model(model: Model): Sanitize model dictionary to remove None values and empty strings and only keep valid keys. """ if isinstance(model, Model): - model = model.dict() + model = model.model_dump() valid_keys = ["model", "base_url", "api_key", "api_type", "api_version"] # only add key if value is not None sanitized_model = {k: v for k, v in model.items() if (v is not None and v != "") and k in valid_keys} @@ -410,16 +411,36 @@ def test_model(model: Model): return response.choices[0].message.content -# summarize_chat_history (messages, model) .. returns a summary of the chat history +def load_code_execution_config(code_execution_type: CodeExecutionConfigTypes, work_dir: str): + """ + Load the code execution configuration based on the code execution type. + :param code_execution_type: The code execution type. + :param work_dir: The working directory to store code execution files. + :return: The code execution configuration. -def summarize_chat_history(task: str, messages: List[Dict[str, str]], model: Model): + """ + work_dir = Path(work_dir) + work_dir.mkdir(exist_ok=True) + executor = None + if code_execution_type == CodeExecutionConfigTypes.local: + executor = LocalCommandLineCodeExecutor(work_dir=work_dir) + elif code_execution_type == CodeExecutionConfigTypes.docker: + executor = DockerCommandLineCodeExecutor(work_dir=work_dir) + elif code_execution_type == CodeExecutionConfigTypes.none: + return False + else: + raise ValueError(f"Invalid code execution type: {code_execution_type}") + code_execution_config = { + "executor": executor, + } + return code_execution_config + + +def summarize_chat_history(task: str, messages: List[Dict[str, str]], client: ModelClient): """ Summarize the chat history using the model endpoint and returning the response. """ - - sanitized_model = sanitize_model(model) - client = OpenAIWrapper(config_list=[sanitized_model]) summarization_system_prompt = f""" You are a helpful assistant that is able to review the chat history between a set of agents (userproxy agents, assistants etc) as they try to address a given TASK and provide a summary. Be SUCCINCT but also comprehensive enough to allow others (who cannot see the chat history) understand and recreate the solution. @@ -427,7 +448,7 @@ def summarize_chat_history(task: str, messages: List[Dict[str, str]], model: Mod === {task} === - The summary should focus on extracting the actual solution to the task from the chat history (assuming the task was addressed) such that any other agent reading the summary will understand what the actual solution is. Use a neutral tone and DO NOT directly mention the agents. Instead only focus on the actions that were carried out (e.g. do not say 'assistant agent generated some code visualization code ..' instead say say 'visualization code was generated ..' ). + The summary should focus on extracting the actual solution to the task from the chat history (assuming the task was addressed) such that any other agent reading the summary will understand what the actual solution is. Use a neutral tone and DO NOT directly mention the agents. Instead only focus on the actions that were carried out (e.g. do not say 'assistant agent generated some code visualization code ..' instead say say 'visualization code was generated ..'. The answer should be framed as a response to the user task. E.g. if the task is "What is the height of the Eiffel tower", the summary should be "The height of the Eiffel Tower is ..."). """ summarization_prompt = [ { diff --git a/samples/apps/autogen-studio/autogenstudio/version.py b/samples/apps/autogen-studio/autogenstudio/version.py index 18b7f42aac3..bafe37f75b1 100644 --- a/samples/apps/autogen-studio/autogenstudio/version.py +++ b/samples/apps/autogen-studio/autogenstudio/version.py @@ -1,3 +1,3 @@ -VERSION = "0.0.54" +VERSION = "0.0.56rc9" __version__ = VERSION APP_NAME = "autogenstudio" diff --git a/samples/apps/autogen-studio/autogenstudio/web/app.py b/samples/apps/autogen-studio/autogenstudio/web/app.py index 6d5412e9fed..76ab8139ebc 100644 --- a/samples/apps/autogen-studio/autogenstudio/web/app.py +++ b/samples/apps/autogen-studio/autogenstudio/web/app.py @@ -1,25 +1,23 @@ import asyncio -import json import os import queue import threading import traceback from contextlib import asynccontextmanager +from typing import Any -from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect +from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles +from loguru import logger from openai import OpenAIError from ..chatmanager import AutoGenChatManager, WebSocketConnectionManager -from ..datamodel import ( - DBWebRequestModel, - DeleteMessageWebRequestModel, - Message, - Session, -) -from ..utils import DBManager, dbutils, init_app_folders, md5_hash, test_model -from ..version import APP_NAME, VERSION +from ..database import workflow_from_id +from ..database.dbmanager import DBManager +from ..datamodel import Agent, Message, Model, Response, Session, Skill, Workflow +from ..utils import check_and_cast_datetime_fields, init_app_folders, md5_hash, test_model +from ..version import VERSION managers = {"chat": None} # manage calls to autogen # Create thread-safe queue for messages between api thread and autogen threads @@ -27,18 +25,29 @@ active_connections = [] active_connections_lock = asyncio.Lock() websocket_manager = WebSocketConnectionManager( - active_connections=active_connections, active_connections_lock=active_connections_lock + active_connections=active_connections, + active_connections_lock=active_connections_lock, ) def message_handler(): while True: message = message_queue.get() - print("Active Connections: ", [client_id for _, client_id in websocket_manager.active_connections]) - print("Current message connection id: ", message["connection_id"]) + logger.info( + "** Processing Agent Message on Queue: Active Connections: " + + str([client_id for _, client_id in websocket_manager.active_connections]) + + " **" + ) for connection, socket_client_id in websocket_manager.active_connections: if message["connection_id"] == socket_client_id: + logger.info( + f"Sending message to connection_id: {message['connection_id']}. Connection ID: {socket_client_id}" + ) asyncio.run(websocket_manager.send_message(message, connection)) + else: + logger.info( + f"Skipping message for connection_id: {message['connection_id']}. Connection ID: {socket_client_id}" + ) message_queue.task_done() @@ -46,10 +55,19 @@ def message_handler(): message_handler_thread.start() +app_file_path = os.path.dirname(os.path.abspath(__file__)) +folders = init_app_folders(app_file_path) +ui_folder_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui") + +database_engine_uri = folders["database_engine_uri"] +dbmanager = DBManager(engine_uri=database_engine_uri) + + @asynccontextmanager async def lifespan(app: FastAPI): print("***** App started *****") managers["chat"] = AutoGenChatManager(message_queue=message_queue) + dbmanager.create_db_and_tables() yield # Close all active connections @@ -75,477 +93,312 @@ async def lifespan(app: FastAPI): ) -app_file_path = os.path.dirname(os.path.abspath(__file__)) -# init folders skills, workdir, static, files etc -folders = init_app_folders(app_file_path) -ui_folder_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui") - api = FastAPI(root_path="/api") # mount an api route such that the main route serves the ui and the /api app.mount("/api", api) app.mount("/", StaticFiles(directory=ui_folder_path, html=True), name="ui") -api.mount("/files", StaticFiles(directory=folders["files_static_root"], html=True), name="files") +api.mount( + "/files", + StaticFiles(directory=folders["files_static_root"], html=True), + name="files", +) -db_path = os.path.join(folders["app_root"], "database.sqlite") -dbmanager = DBManager(path=db_path) # manage database operations # manage websocket connections -@api.post("/messages") -async def add_message(req: DBWebRequestModel): - message = Message(**req.message.dict()) - user_history = dbutils.get_messages(user_id=message.user_id, session_id=req.message.session_id, dbmanager=dbmanager) - - # save incoming message to db - dbutils.create_message(message=message, dbmanager=dbmanager) - user_dir = os.path.join(folders["files_static_root"], "user", md5_hash(message.user_id)) - os.makedirs(user_dir, exist_ok=True) - +def create_entity(model: Any, model_class: Any, filters: dict = None): + """Create a new entity""" + model = check_and_cast_datetime_fields(model) try: - response_message: Message = managers["chat"].chat( - message=message, - history=user_history, - user_dir=user_dir, - flow_config=req.workflow, - connection_id=req.connection_id, - ) - - # save agent's response to db - messages = dbutils.create_message(message=response_message, dbmanager=dbmanager) - response = { - "status": True, - "message": "Message processed successfully", - "data": messages, - # "metadata": json.loads(response_message.metadata), - } - return response - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while processing message: " + str(ex_error), - } - - -@api.get("/messages") -async def get_messages(user_id: str = None, session_id: str = None): - if user_id is None: - raise HTTPException(status_code=400, detail="user_id is required") - try: - user_history = dbutils.get_messages(user_id=user_id, session_id=session_id, dbmanager=dbmanager) - - return { - "status": True, - "data": user_history, - "message": "Messages retrieved successfully", - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving messages: " + str(ex_error), - } - + response: Response = dbmanager.upsert(model) + return response.model_dump(mode="json") -@api.get("/gallery") -async def get_gallery_items(gallery_id: str = None): - try: - gallery = dbutils.get_gallery(gallery_id=gallery_id, dbmanager=dbmanager) - return { - "status": True, - "data": gallery, - "message": "Gallery items retrieved successfully", - } except Exception as ex_error: print(ex_error) return { "status": False, - "message": "Error occurred while retrieving messages: " + str(ex_error), + "message": f"Error occurred while creating {model_class.__name__}: " + str(ex_error), } -@api.get("/sessions") -async def get_user_sessions(user_id: str = None): - """Return a list of all sessions for a user""" - if user_id is None: - raise HTTPException(status_code=400, detail="user_id is required") +def list_entity( + model_class: Any, + filters: dict = None, + return_json: bool = True, + order: str = "desc", +): + """List all entities for a user""" + return dbmanager.get(model_class, filters=filters, return_json=return_json, order=order) - try: - user_sessions = dbutils.get_sessions(user_id=user_id, dbmanager=dbmanager) - return { - "status": True, - "data": user_sessions, - "message": "Sessions retrieved successfully", - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving sessions: " + str(ex_error), - } +def delete_entity(model_class: Any, filters: dict = None): + """Delete an entity""" + return dbmanager.delete(filters=filters, model_class=model_class) -@api.post("/sessions") -async def create_user_session(req: DBWebRequestModel): - """Create a new session for a user""" - # print(req.session, "**********" ) - - try: - session = Session(user_id=req.session.user_id, flow_config=req.session.flow_config) - user_sessions = dbutils.create_session(user_id=req.user_id, session=session, dbmanager=dbmanager) - - return { - "status": True, - "message": "Session created successfully", - "data": user_sessions, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while creating session: " + str(ex_error), - } - - -@api.post("/sessions/rename") -async def rename_user_session(name: str, req: DBWebRequestModel): - """Rename a session for a user""" - print("Rename: " + name) - print("renaming session for user: " + req.user_id + " to: " + name) - try: - session = dbutils.rename_session(name=name, session=req.session, dbmanager=dbmanager) - return { - "status": True, - "message": "Session renamed successfully", - "data": session, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while renaming session: " + str(ex_error), - } - - -@api.post("/sessions/publish") -async def publish_user_session_to_gallery(req: DBWebRequestModel): - """Create a new session for a user""" - - try: - gallery_item = dbutils.create_gallery(req.session, tags=req.tags, dbmanager=dbmanager) - return { - "status": True, - "message": "Session successfully published", - "data": gallery_item, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while publishing session: " + str(ex_error), - } +@api.get("/skills") +async def list_skills(user_id: str): + """List all skills for a user""" + filters = {"user_id": user_id} + return list_entity(Skill, filters=filters) -@api.delete("/sessions/delete") -async def delete_user_session(req: DBWebRequestModel): - """Delete a session for a user""" - try: - sessions = dbutils.delete_session(session=req.session, dbmanager=dbmanager) - return { - "status": True, - "message": "Session deleted successfully", - "data": sessions, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while deleting session: " + str(ex_error), - } +@api.post("/skills") +async def create_skill(skill: Skill): + """Create a new skill""" + filters = {"user_id": skill.user_id} + return create_entity(skill, Skill, filters=filters) -@api.post("/messages/delete") -async def remove_message(req: DeleteMessageWebRequestModel): - """Delete a message from the database""" +@api.delete("/skills/delete") +async def delete_skill(skill_id: int, user_id: str): + """Delete a skill""" + filters = {"id": skill_id, "user_id": user_id} + return delete_entity(Skill, filters=filters) - try: - messages = dbutils.delete_message( - user_id=req.user_id, msg_id=req.msg_id, session_id=req.session_id, dbmanager=dbmanager - ) - return { - "status": True, - "message": "Message deleted successfully", - "data": messages, - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while deleting message: " + str(ex_error), - } +@api.get("/models") +async def list_models(user_id: str): + """List all models for a user""" + filters = {"user_id": user_id} + return list_entity(Model, filters=filters) -@api.get("/skills") -async def get_user_skills(user_id: str): - try: - skills = dbutils.get_skills(user_id, dbmanager=dbmanager) - return { - "status": True, - "message": "Skills retrieved successfully", - "data": skills, - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving skills: " + str(ex_error), - } +@api.post("/models") +async def create_model(model: Model): + """Create a new model""" + return create_entity(model, Model) -@api.post("/skills") -async def create_user_skills(req: DBWebRequestModel): +@api.post("/models/test") +async def test_model_endpoint(model: Model): + """Test a model""" try: - skills = dbutils.upsert_skill(skill=req.skill, dbmanager=dbmanager) + response = test_model(model) return { "status": True, - "message": "Skills retrieved successfully", - "data": skills, + "message": "Model tested successfully", + "data": response, } - - except Exception as ex_error: - print(ex_error) + except (OpenAIError, Exception) as ex_error: return { "status": False, - "message": "Error occurred while creating skills: " + str(ex_error), + "message": "Error occurred while testing model: " + str(ex_error), } -@api.delete("/skills/delete") -async def delete_user_skills(req: DBWebRequestModel): - """Delete a skill for a user""" +@api.delete("/models/delete") +async def delete_model(model_id: int, user_id: str): + """Delete a model""" + filters = {"id": model_id, "user_id": user_id} + return delete_entity(Model, filters=filters) - try: - skills = dbutils.delete_skill(req.skill, dbmanager=dbmanager) - return { - "status": True, - "message": "Skill deleted successfully", - "data": skills, - } +@api.get("/agents") +async def list_agents(user_id: str): + """List all agents for a user""" + filters = {"user_id": user_id} + return list_entity(Agent, filters=filters) - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while deleting skill: " + str(ex_error), - } +@api.post("/agents") +async def create_agent(agent: Agent): + """Create a new agent""" + return create_entity(agent, Agent) -@api.get("/agents") -async def get_user_agents(user_id: str): - try: - agents = dbutils.get_agents(user_id, dbmanager=dbmanager) - return { - "status": True, - "message": "Agents retrieved successfully", - "data": agents, - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving agents: " + str(ex_error), - } +@api.delete("/agents/delete") +async def delete_agent(agent_id: int, user_id: str): + """Delete an agent""" + filters = {"id": agent_id, "user_id": user_id} + return delete_entity(Agent, filters=filters) -@api.post("/agents") -async def create_user_agents(req: DBWebRequestModel): - """Create a new agent for a user""" +@api.post("/agents/link/model/{agent_id}/{model_id}") +async def link_agent_model(agent_id: int, model_id: int): + """Link a model to an agent""" + return dbmanager.link(link_type="agent_model", primary_id=agent_id, secondary_id=model_id) - try: - agents = dbutils.upsert_agent(agent_flow_spec=req.agent, dbmanager=dbmanager) - return { - "status": True, - "message": "Agent created successfully", - "data": agents, - } +@api.delete("/agents/link/model/{agent_id}/{model_id}") +async def unlink_agent_model(agent_id: int, model_id: int): + """Unlink a model from an agent""" + return dbmanager.unlink(link_type="agent_model", primary_id=agent_id, secondary_id=model_id) - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while creating agent: " + str(ex_error), - } +@api.get("/agents/link/model/{agent_id}") +async def get_agent_models(agent_id: int): + """Get all models linked to an agent""" + return dbmanager.get_linked_entities("agent_model", agent_id, return_json=True) -@api.delete("/agents/delete") -async def delete_user_agent(req: DBWebRequestModel): - """Delete an agent for a user""" - try: - agents = dbutils.delete_agent(agent=req.agent, dbmanager=dbmanager) +@api.post("/agents/link/skill/{agent_id}/{skill_id}") +async def link_agent_skill(agent_id: int, skill_id: int): + """Link an a skill to an agent""" + return dbmanager.link(link_type="agent_skill", primary_id=agent_id, secondary_id=skill_id) - return { - "status": True, - "message": "Agent deleted successfully", - "data": agents, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while deleting agent: " + str(ex_error), - } +@api.delete("/agents/link/skill/{agent_id}/{skill_id}") +async def unlink_agent_skill(agent_id: int, skill_id: int): + """Unlink an a skill from an agent""" + return dbmanager.unlink(link_type="agent_skill", primary_id=agent_id, secondary_id=skill_id) -@api.get("/models") -async def get_user_models(user_id: str): - try: - models = dbutils.get_models(user_id, dbmanager=dbmanager) +@api.get("/agents/link/skill/{agent_id}") +async def get_agent_skills(agent_id: int): + """Get all skills linked to an agent""" + return dbmanager.get_linked_entities("agent_skill", agent_id, return_json=True) - return { - "status": True, - "message": "Models retrieved successfully", - "data": models, - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving models: " + str(ex_error), - } +@api.post("/agents/link/agent/{primary_agent_id}/{secondary_agent_id}") +async def link_agent_agent(primary_agent_id: int, secondary_agent_id: int): + """Link an agent to another agent""" + return dbmanager.link( + link_type="agent_agent", + primary_id=primary_agent_id, + secondary_id=secondary_agent_id, + ) -@api.post("/models") -async def create_user_models(req: DBWebRequestModel): - """Create a new model for a user""" - try: - models = dbutils.upsert_model(model=req.model, dbmanager=dbmanager) +@api.delete("/agents/link/agent/{primary_agent_id}/{secondary_agent_id}") +async def unlink_agent_agent(primary_agent_id: int, secondary_agent_id: int): + """Unlink an agent from another agent""" + return dbmanager.unlink( + link_type="agent_agent", + primary_id=primary_agent_id, + secondary_id=secondary_agent_id, + ) - return { - "status": True, - "message": "Model created successfully", - "data": models, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while creating model: " + str(ex_error), - } +@api.get("/agents/link/agent/{agent_id}") +async def get_linked_agents(agent_id: int): + """Get all agents linked to an agent""" + return dbmanager.get_linked_entities("agent_agent", agent_id, return_json=True) -@api.post("/models/test") -async def test_user_models(req: DBWebRequestModel): - """Test a model to verify it works""" +@api.get("/workflows") +async def list_workflows(user_id: str): + """List all workflows for a user""" + filters = {"user_id": user_id} + return list_entity(Workflow, filters=filters) - try: - response = test_model(model=req.model) - return { - "status": True, - "message": "Model tested successfully", - "data": response, - } - except OpenAIError as oai_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while testing model: " + str(oai_error), - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while testing model: " + str(ex_error), - } +@api.get("/workflows/{workflow_id}") +async def get_workflow(workflow_id: int, user_id: str): + """Get a workflow""" + filters = {"id": workflow_id, "user_id": user_id} + return list_entity(Workflow, filters=filters) -@api.delete("/models/delete") -async def delete_user_model(req: DBWebRequestModel): - """Delete a model for a user""" +@api.post("/workflows") +async def create_workflow(workflow: Workflow): + """Create a new workflow""" + return create_entity(workflow, Workflow) - try: - models = dbutils.delete_model(model=req.model, dbmanager=dbmanager) - return { - "status": True, - "message": "Model deleted successfully", - "data": models, - } +@api.delete("/workflows/delete") +async def delete_workflow(workflow_id: int, user_id: str): + """Delete a workflow""" + filters = {"id": workflow_id, "user_id": user_id} + return delete_entity(Workflow, filters=filters) + + +@api.post("/workflows/link/agent/{workflow_id}/{agent_id}/{agent_type}") +async def link_workflow_agent(workflow_id: int, agent_id: int, agent_type: str): + """Link an agent to a workflow""" + return dbmanager.link( + link_type="workflow_agent", + primary_id=workflow_id, + secondary_id=agent_id, + agent_type=agent_type, + ) + + +@api.delete("/workflows/link/agent/{workflow_id}/{agent_id}/{agent_type}") +async def unlink_workflow_agent(workflow_id: int, agent_id: int, agent_type: str): + """Unlink an agent from a workflow""" + return dbmanager.unlink( + link_type="workflow_agent", + primary_id=workflow_id, + secondary_id=agent_id, + agent_type=agent_type, + ) + + +@api.get("/workflows/link/agent/{workflow_id}/{agent_type}") +async def get_linked_workflow_agents(workflow_id: int, agent_type: str): + """Get all agents linked to a workflow""" + return dbmanager.get_linked_entities( + link_type="workflow_agent", + primary_id=workflow_id, + agent_type=agent_type, + return_json=True, + ) - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while deleting model: " + str(ex_error), - } +@api.get("/sessions") +async def list_sessions(user_id: str): + """List all sessions for a user""" + filters = {"user_id": user_id} + return list_entity(Session, filters=filters) -@api.get("/workflows") -async def get_user_workflows(user_id: str): - try: - workflows = dbutils.get_workflows(user_id, dbmanager=dbmanager) - return { - "status": True, - "message": "Workflows retrieved successfully", - "data": workflows, - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving workflows: " + str(ex_error), - } +@api.post("/sessions") +async def create_session(session: Session): + """Create a new session""" + return create_entity(session, Session) -@api.post("/workflows") -async def create_user_workflow(req: DBWebRequestModel): - """Create a new workflow for a user""" - try: - workflow = dbutils.upsert_workflow(workflow=req.workflow, dbmanager=dbmanager) - return { - "status": True, - "message": "Workflow created successfully", - "data": workflow, - } +@api.delete("/sessions/delete") +async def delete_session(session_id: int, user_id: str): + """Delete a session""" + filters = {"id": session_id, "user_id": user_id} + return delete_entity(Session, filters=filters) - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while creating workflow: " + str(ex_error), - } +@api.get("/sessions/{session_id}/messages") +async def list_messages(user_id: str, session_id: int): + """List all messages for a use session""" + filters = {"user_id": user_id, "session_id": session_id} + return list_entity(Message, filters=filters, order="asc", return_json=True) -@api.delete("/workflows/delete") -async def delete_user_workflow(req: DBWebRequestModel): - """Delete a workflow for a user""" +@api.post("/sessions/{session_id}/workflow/{workflow_id}/run") +async def run_session_workflow(message: Message, session_id: int, workflow_id: int): + """Runs a workflow on provided message""" try: - workflow = dbutils.delete_workflow(workflow=req.workflow, dbmanager=dbmanager) - return { - "status": True, - "message": "Workflow deleted successfully", - "data": workflow, - } + user_message_history = ( + dbmanager.get( + Message, + filters={"user_id": message.user_id, "session_id": message.session_id}, + return_json=True, + ).data + if session_id is not None + else [] + ) + # save incoming message + dbmanager.upsert(message) + user_dir = os.path.join(folders["files_static_root"], "user", md5_hash(message.user_id)) + os.makedirs(user_dir, exist_ok=True) + workflow = workflow_from_id(workflow_id, dbmanager=dbmanager) + agent_response: Message = managers["chat"].chat( + message=message, + history=user_message_history, + user_dir=user_dir, + workflow=workflow, + connection_id=message.connection_id, + ) + response: Response = dbmanager.upsert(agent_response) + return response.model_dump(mode="json") except Exception as ex_error: - print(ex_error) + print(traceback.format_exc()) return { "status": False, - "message": "Error occurred while deleting workflow: " + str(ex_error), + "message": "Error occurred while processing message: " + str(ex_error), } @@ -558,11 +411,16 @@ async def get_version(): } +# websockets + + async def process_socket_message(data: dict, websocket: WebSocket, client_id: str): print(f"Client says: {data['type']}") if data["type"] == "user_message": - user_request_body = DBWebRequestModel(**data["data"]) - response = await add_message(user_request_body) + user_message = Message(**data["data"]) + session_id = data["data"].get("session_id", None) + workflow_id = data["data"].get("workflow_id", None) + response = await run_session_workflow(message=user_message, session_id=session_id, workflow_id=workflow_id) response_socket_message = { "type": "agent_response", "data": response, diff --git a/samples/apps/autogen-studio/autogenstudio/workflowmanager.py b/samples/apps/autogen-studio/autogenstudio/workflowmanager.py index c5475e58d83..8b41caab428 100644 --- a/samples/apps/autogen-studio/autogenstudio/workflowmanager.py +++ b/samples/apps/autogen-studio/autogenstudio/workflowmanager.py @@ -1,23 +1,26 @@ import os from datetime import datetime -from typing import Dict, List, Optional, Union - -from requests import Session +from typing import Any, Dict, List, Optional, Union import autogen -from .datamodel import AgentConfig, AgentFlowSpec, AgentWorkFlowConfig, Message, SocketMessage -from .utils import clear_folder, get_skills_from_prompt, sanitize_model +from .datamodel import ( + Agent, + AgentType, + Message, + SocketMessage, +) +from .utils import clear_folder, get_skills_from_prompt, load_code_execution_config, sanitize_model -class AutoGenWorkFlowManager: +class WorkflowManager: """ AutoGenWorkFlowManager class to load agents from a provided configuration and run a chat between them """ def __init__( self, - config: AgentWorkFlowConfig, + workflow: Dict, history: Optional[List[Message]] = None, work_dir: str = None, clear_work_dir: bool = True, @@ -33,20 +36,57 @@ def __init__( history: An optional list of previous messages to populate the agents' history. """ + # TODO - improved typing for workflow self.send_message_function = send_message_function self.connection_id = connection_id self.work_dir = work_dir or "work_dir" if clear_work_dir: clear_folder(self.work_dir) - self.config = config - # given the config, return an AutoGen agent object - self.sender = self.load(config.sender) - # given the config, return an AutoGen agent object - self.receiver = self.load(config.receiver) + self.workflow = workflow + self.sender = self.load(workflow.get("sender")) + self.receiver = self.load(workflow.get("receiver")) self.agent_history = [] if history: - self.populate_history(history) + self._populate_history(history) + + def _serialize_agent( + self, + agent: Agent, + mode: str = "python", + include: Optional[List[str]] = {"config"}, + exclude: Optional[List[str]] = None, + ) -> Dict: + """ """ + # exclude = ["id","created_at", "updated_at","user_id","type"] + exclude = exclude or {} + include = include or {} + if agent.type != AgentType.groupchat: + exclude.update( + { + "config": { + "admin_name", + "messages", + "max_round", + "admin_name", + "speaker_selection_method", + "allow_repeat_speaker", + } + } + ) + else: + include = { + "config": { + "admin_name", + "messages", + "max_round", + "admin_name", + "speaker_selection_method", + "allow_repeat_speaker", + } + } + result = agent.model_dump(warnings=False, exclude=exclude, include=include, mode=mode) + return result["config"] def process_message( self, @@ -84,25 +124,14 @@ def process_message( if request_reply is not False or sender_type == "groupchat": self.agent_history.append(message_payload) # add to history if self.send_message_function: # send over the message queue - socket_msg = SocketMessage(type="agent_message", data=message_payload, connection_id=self.connection_id) + socket_msg = SocketMessage( + type="agent_message", + data=message_payload, + connection_id=self.connection_id, + ) self.send_message_function(socket_msg.dict()) - def _sanitize_history_message(self, message: str) -> str: - """ - Sanitizes the message e.g. remove references to execution completed - - Args: - message: The message to be sanitized. - - Returns: - The sanitized message. - """ - to_replace = ["execution succeeded", "exitcode"] - for replace in to_replace: - message = message.replace(replace, "") - return message - - def populate_history(self, history: List[Message]) -> None: + def _populate_history(self, history: List[Message]) -> None: """ Populates the agent message history from the provided list of messages. @@ -127,19 +156,12 @@ def populate_history(self, history: List[Message]) -> None: silent=True, ) - def sanitize_agent_spec(self, agent_spec: AgentFlowSpec) -> AgentFlowSpec: - """ - Sanitizes the agent spec by setting loading defaults - - Args: - config: The agent configuration to be sanitized. - agent_type: The type of the agent. + def sanitize_agent(self, agent: Dict) -> Agent: + """ """ - Returns: - The sanitized agent configuration. - """ - - agent_spec.config.is_termination_msg = agent_spec.config.is_termination_msg or ( + skills = agent.get("skills", []) + agent = Agent.model_validate(agent) + agent.config.is_termination_msg = agent.config.is_termination_msg or ( lambda x: "TERMINATE" in x.get("content", "").rstrip()[-20:] ) @@ -149,40 +171,33 @@ def get_default_system_message(agent_type: str) -> str: else: return "You are a helpful AI Assistant." - # sanitize llm_config if present - if agent_spec.config.llm_config is not False: + if agent.config.llm_config is not False: config_list = [] - for llm in agent_spec.config.llm_config.config_list: + for llm in agent.config.llm_config.config_list: # check if api_key is present either in llm or env variable if "api_key" not in llm and "OPENAI_API_KEY" not in os.environ: - error_message = f"api_key is not present in llm_config or OPENAI_API_KEY env variable for agent ** {agent_spec.config.name}**. Update your workflow to provide an api_key to use the LLM." + error_message = f"api_key is not present in llm_config or OPENAI_API_KEY env variable for agent ** {agent.config.name}**. Update your workflow to provide an api_key to use the LLM." raise ValueError(error_message) # only add key if value is not None sanitized_llm = sanitize_model(llm) config_list.append(sanitized_llm) - agent_spec.config.llm_config.config_list = config_list - if agent_spec.config.code_execution_config is not False: - code_execution_config = agent_spec.config.code_execution_config or {} - code_execution_config["work_dir"] = self.work_dir - # tbd check if docker is installed - code_execution_config["use_docker"] = False - agent_spec.config.code_execution_config = code_execution_config - - if agent_spec.skills: - # get skill prompt, also write skills to a file named skills.py - skills_prompt = "" - skills_prompt = get_skills_from_prompt(agent_spec.skills, self.work_dir) - if agent_spec.config.system_message: - agent_spec.config.system_message = agent_spec.config.system_message + "\n\n" + skills_prompt - else: - agent_spec.config.system_message = ( - get_default_system_message(agent_spec.type) + "\n\n" + skills_prompt - ) - - return agent_spec - - def load(self, agent_spec: AgentFlowSpec) -> autogen.Agent: + agent.config.llm_config.config_list = config_list + + agent.config.code_execution_config = load_code_execution_config( + agent.config.code_execution_config, work_dir=self.work_dir + ) + + if skills: + skills_prompt = "" + skills_prompt = get_skills_from_prompt(skills, self.work_dir) + if agent.config.system_message: + agent.config.system_message = agent.config.system_message + "\n\n" + skills_prompt + else: + agent.config.system_message = get_default_system_message(agent.type) + "\n\n" + skills_prompt + return agent + + def load(self, agent: Any) -> autogen.Agent: """ Loads an agent based on the provided agent specification. @@ -192,43 +207,40 @@ def load(self, agent_spec: AgentFlowSpec) -> autogen.Agent: Returns: An instance of the loaded agent. """ - agent_spec = self.sanitize_agent_spec(agent_spec) - if agent_spec.type == "groupchat": - agents = [ - self.load(self.sanitize_agent_spec(agent_config)) for agent_config in agent_spec.groupchat_config.agents - ] - group_chat_config = agent_spec.groupchat_config.dict() - group_chat_config["agents"] = agents + if not agent: + raise ValueError( + "An agent configuration in this workflow is empty. Please provide a valid agent configuration." + ) + + linked_agents = agent.get("agents", []) + agent = self.sanitize_agent(agent) + if agent.type == "groupchat": + groupchat_agents = [self.load(agent) for agent in linked_agents] + group_chat_config = self._serialize_agent(agent) + group_chat_config["agents"] = groupchat_agents groupchat = autogen.GroupChat(**group_chat_config) agent = ExtendedGroupChatManager( - groupchat=groupchat, **agent_spec.config.dict(), message_processor=self.process_message + groupchat=groupchat, + message_processor=self.process_message, + llm_config=agent.config.llm_config.model_dump(), ) return agent else: - agent = self.load_agent_config(agent_spec.config, agent_spec.type) + if agent.type == "assistant": + agent = ExtendedConversableAgent( + **self._serialize_agent(agent), + message_processor=self.process_message, + ) + elif agent.type == "userproxy": + agent = ExtendedConversableAgent( + **self._serialize_agent(agent), + message_processor=self.process_message, + ) + else: + raise ValueError(f"Unknown agent type: {agent.type}") return agent - def load_agent_config(self, agent_config: AgentConfig, agent_type: str) -> autogen.Agent: - """ - Loads an agent based on the provided agent configuration. - - Args: - agent_config: The configuration of the agent to be loaded. - agent_type: The type of the agent to be loaded. - - Returns: - An instance of the loaded agent. - """ - if agent_type == "assistant": - agent = ExtendedConversableAgent(**agent_config.dict(), message_processor=self.process_message) - elif agent_type == "userproxy": - agent = ExtendedConversableAgent(**agent_config.dict(), message_processor=self.process_message) - else: - raise ValueError(f"Unknown agent type: {agent_type}") - - return agent - def run(self, message: str, clear_history: bool = False) -> None: """ Initiates a chat between the sender and receiver agents with an initial message @@ -262,6 +274,9 @@ def receive( super().receive(message, sender, request_reply, silent) +"" + + class ExtendedGroupChatManager(autogen.GroupChatManager): def __init__(self, message_processor=None, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/samples/apps/autogen-studio/frontend/src/components/atoms.tsx b/samples/apps/autogen-studio/frontend/src/components/atoms.tsx index 8bc70f89a90..c4c1368a123 100644 --- a/samples/apps/autogen-studio/frontend/src/components/atoms.tsx +++ b/samples/apps/autogen-studio/frontend/src/components/atoms.tsx @@ -4,53 +4,18 @@ import { Cog8ToothIcon, XMarkIcon, ClipboardIcon, - PlusIcon, - UserGroupIcon, - UsersIcon, - ExclamationTriangleIcon, InformationCircleIcon, } from "@heroicons/react/24/outline"; import React, { ReactNode, useEffect, useRef, useState } from "react"; import Icon from "./icons"; -import { - Button, - Divider, - Dropdown, - Input, - MenuProps, - Modal, - Select, - Slider, - Table, - Space, - Tooltip, - message, - theme, -} from "antd"; +import { Modal, Table, Tooltip, theme } from "antd"; import Editor from "@monaco-editor/react"; import Papa from "papaparse"; import remarkGfm from "remark-gfm"; import ReactMarkdown from "react-markdown"; import { atomDark } from "react-syntax-highlighter/dist/esm/styles/prism"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; -import { - checkAndSanitizeInput, - fetchJSON, - getServerUrl, - obscureString, - truncateText, -} from "./utils"; -import { - IAgentFlowSpec, - IFlowConfig, - IGroupChatFlowSpec, - ILLMConfig, - IModelConfig, - ISkill, - IStatus, -} from "./types"; -import TextArea from "antd/es/input/TextArea"; -import { appContext } from "../hooks/provider"; +import { truncateText } from "./utils"; const { useToken } = theme; interface CodeProps { @@ -162,12 +127,13 @@ export const Card = ({ border = hoverable ? border : "border-secondary"; return ( -
-
+
{title && (
{title} @@ -176,7 +142,7 @@ export const Card = ({
{subtitle}
{children}
-
+ ); }; @@ -303,7 +269,7 @@ export const MessageBox = ({ title, children, className }: IProps) => { export const GroupView = ({ children, title, - className = " bg-primary ", + className = "text-primary bg-primary ", }: any) => { return (
@@ -590,19 +556,21 @@ export const ControlRowView = ({ value, control, className, + truncateLength = 20, }: { title: string; description: string; - value: string | number; + value: string | number | boolean; control: any; className?: string; + truncateLength?: number; }) => { return (
{title} - {truncateText(value + "", 20)} + {truncateText(value + "", truncateLength)} {" "} @@ -614,291 +582,6 @@ export const ControlRowView = ({ ); }; -export const ModelSelector = ({ - configs, - setConfigs, - className, -}: { - configs: IModelConfig[]; - setConfigs: (configs: IModelConfig[]) => void; - className?: string; -}) => { - // const [configs, setConfigs] = useState(modelConfigs); - const [isModalVisible, setIsModalVisible] = useState(false); - const [newModelConfig, setNewModelConfig] = useState( - null - ); - const [editIndex, setEditIndex] = useState(null); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - - const [models, setModels] = useState([]); - const serverUrl = getServerUrl(); - - const { user } = React.useContext(appContext); - const listModelsUrl = `${serverUrl}/models?user_id=${user?.email}`; - - // const sanitizeModelConfig = (config: IModelConfig) => { - // const sanitizedConfig: IModelConfig = { model: config.model }; - // if (config.api_key) sanitizedConfig.api_key = config.api_key; - // if (config.base_url) sanitizedConfig.base_url = config.base_url; - // if (config.api_type) sanitizedConfig.api_type = config.api_type; - // if (config.api_version) sanitizedConfig.api_version = config.api_version; - // return sanitizedConfig; - // }; - - const handleRemoveConfig = (index: number) => { - const updatedConfigs = configs.filter((_, i) => i !== index); - - setConfigs(updatedConfigs); - }; - - const showModal = (config: IModelConfig | null, index: number | null) => { - setNewModelConfig(config); - setEditIndex(index); - setIsModalVisible(true); - }; - - const fetchModels = () => { - setError(null); - setLoading(true); - // const fetch; - const payLoad = { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }; - - const onSuccess = (data: any) => { - if (data && data.status) { - // message.success(data.message); - setModels(data.data); - } else { - message.error(data.message); - } - setLoading(false); - }; - const onError = (err: any) => { - setError(err); - message.error(err.message); - setLoading(false); - }; - fetchJSON(listModelsUrl, payLoad, onSuccess, onError); - }; - - useEffect(() => { - fetchModels(); - }, []); - - const modelItems: MenuProps["items"] = - models.length > 0 - ? models.map((model: IModelConfig, index: number) => ({ - key: index, - label: ( - <> -
{model.model}
-
- {truncateText(model.description || "", 20)} -
- - ), - value: index, - })) - : [ - { - key: -1, - label: <>No models found, - value: 0, - }, - ]; - - const modelOnClick: MenuProps["onClick"] = ({ key }) => { - const selectedIndex = parseInt(key.toString()); - let selectedModel = models[selectedIndex]; - const updatedConfigs = [...configs, selectedModel]; - setConfigs(updatedConfigs); - }; - - const menuStyle: React.CSSProperties = { - boxShadow: "none", - }; - - const { token } = useToken(); - const contentStyle: React.CSSProperties = { - backgroundColor: token.colorBgElevated, - borderRadius: token.borderRadiusLG, - boxShadow: token.boxShadowSecondary, - }; - - const addModelsMessage = ( - - {" "} - Please - create models in the Model tab - - ); - - const AddModelsDropDown = () => { - return ( - ( -
- {React.cloneElement(menu as React.ReactElement, { - style: menuStyle, - })} - {models.length === 0 && ( - <> - - -
{addModelsMessage}
- - )} -
- )} - > -
- add -
-
- ); - }; - - const handleOk = () => { - if (newModelConfig?.model.trim()) { - const sanitizedConfig = newModelConfig; - - if (editIndex !== null) { - // Edit existing model - const updatedConfigs = [...configs]; - updatedConfigs[editIndex] = sanitizedConfig; - setConfigs(updatedConfigs); - } else { - // Add new model - setConfigs([...configs, sanitizedConfig]); - } - setIsModalVisible(false); - setNewModelConfig(null); - setEditIndex(null); - } else { - // Handle case where 'model' field is empty - // Could provide user feedback here (e.g., input validation error) - message.error("Model name cannot be empty"); - } - }; - - const handleCancel = () => { - setIsModalVisible(false); - setNewModelConfig(null); - setEditIndex(null); - }; - - const updateNewModelConfig = (field: keyof IModelConfig, value: string) => { - setNewModelConfig((prevState) => - prevState ? { ...prevState, [field]: value } : null - ); - }; - - const modelButtons = configs.map((config, i) => { - const tooltipText = ( - <> -
{config.model}
- {config.base_url &&
{config.base_url}
} - {config.api_key &&
{obscureString(config.api_key, 3)}
} -
- {truncateText(config.description || "", 90)} -
- - ); - return ( -
showModal(config, i)} - > -
- {" "} - -
{config.model}
{" "} -
-
{ - e.stopPropagation(); // Prevent opening the modal to edit - handleRemoveConfig(i); - }} - className="ml-1 text-primary hover:text-accent duration-300" - > - -
-
-
- ); - }); - - return ( -
-
- {modelButtons} - -
- - Cancel - , - , - ]} - > -
Enter parameters for your model.
- updateNewModelConfig("model", e.target.value)} - /> - updateNewModelConfig("api_key", e.target.value)} - /> - updateNewModelConfig("base_url", e.target.value)} - /> - updateNewModelConfig("api_type", e.target.value)} - /> - - updateNewModelConfig("api_version", e.target.value)} - /> -
-
- ); -}; - export const BounceLoader = ({ className, title = "", @@ -937,7 +620,7 @@ export const ImageLoader = ({ Dynamic content setIsLoading(false)} @@ -1077,946 +760,6 @@ export const PdfViewer = ({ url }: { url: string }) => { ); }; -export const AgentFlowSpecView = ({ - title = "Agent Specification", - flowSpec, - setFlowSpec, -}: { - title: string; - flowSpec: IAgentFlowSpec; - setFlowSpec: (newFlowSpec: IAgentFlowSpec) => void; - editMode?: boolean; -}) => { - // Local state for the FlowView component - const [localFlowSpec, setLocalFlowSpec] = - React.useState(flowSpec); - - // Required to monitor localAgent updates that occur in GroupChatFlowSpecView and reflect updates. - useEffect(() => { - setLocalFlowSpec(flowSpec); - }, [flowSpec]); - - // Event handlers for updating local state and propagating changes - - const onControlChange = (value: any, key: string) => { - if (key === "llm_config") { - if (value.config_list.length === 0) { - value = false; - } - } - const updatedFlowSpec = { - ...localFlowSpec, - config: { ...localFlowSpec.config, [key]: value }, - }; - - setLocalFlowSpec(updatedFlowSpec); - setFlowSpec(updatedFlowSpec); - }; - - const llm_config: ILLMConfig = localFlowSpec?.config?.llm_config || { - config_list: [], - temperature: 0.1, - }; - - const nameValidation = checkAndSanitizeInput(flowSpec?.config?.name); - - return ( - <> -
{title}
- {flowSpec?.config?.name}
- className="mb-4 bg-primary " - > - - { - onControlChange(e.target.value, "name"); - }} - /> - {!nameValidation.status && ( -
- {nameValidation.message} -
- )} - - } - /> - - { - onControlChange(e.target.value, "description"); - }} - /> - } - /> - - { - onControlChange(value, "max_consecutive_auto_reply"); - }} - /> - } - /> - - { - onControlChange(e.target.value, "default_auto_reply"); - }} - /> - } - /> - - { - onControlChange(value, "human_input_mode"); - }} - options={ - [ - { label: "NEVER", value: "NEVER" }, - // { label: "TERMINATE", value: "TERMINATE" }, - // { label: "ALWAYS", value: "ALWAYS" }, - ] as any - } - /> - } - /> - - {llm_config && llm_config.config_list.length > 0 && ( - { - onControlChange(e.target.value, "system_message"); - }} - /> - } - /> - )} - - {llm_config && ( - { - const llm_config = { - ...(flowSpec.config.llm_config || { temperature: 0.1 }), - config_list, - }; - onControlChange(llm_config, "llm_config"); - }} - /> - } - /> - )} - - {llm_config && llm_config.config_list.length > 0 && ( - { - const llm_config = { - ...flowSpec.config.llm_config, - temperature: value, - }; - onControlChange(llm_config, "llm_config"); - }} - /> - } - /> - )} - - { - { - const updatedFlowSpec = { - ...localFlowSpec, - skills, - }; - setLocalFlowSpec(updatedFlowSpec); - setFlowSpec(updatedFlowSpec); - }} - /> - } - /> - } - - - ); -}; - -interface SkillSelectorProps { - skills: ISkill[]; - setSkills: (skills: ISkill[]) => void; - className?: string; -} - -export const SkillSelector: React.FC = ({ - skills, - setSkills, - className, -}) => { - const [isModalVisible, setIsModalVisible] = useState(false); - const [showSkillModal, setShowSkillModal] = React.useState(false); - const [newSkill, setNewSkill] = useState(null); - - const [localSkills, setLocalSkills] = useState(skills); - const [selectedSkill, setSelectedSkill] = useState(null); - - const handleRemoveSkill = (index: number) => { - const updatedSkills = localSkills.filter((_, i) => i !== index); - setLocalSkills(updatedSkills); - setSkills(updatedSkills); - }; - - const handleAddSkill = () => { - if (newSkill) { - const updatedSkills = [...localSkills, newSkill]; - setLocalSkills(updatedSkills); - setSkills(updatedSkills); - setNewSkill(null); - } - }; - - useEffect(() => { - if (selectedSkill) { - setShowSkillModal(true); - } - }, [selectedSkill]); - - return ( - <> - { - setShowSkillModal(false); - setSelectedSkill(null); - }} - onCancel={() => { - setShowSkillModal(false); - setSelectedSkill(null); - }} - > - {selectedSkill && ( -
-
{selectedSkill.file_name}
- -
- )} -
- -
- {localSkills.map((skill, index) => ( -
- { - setSelectedSkill(skill); - }} - className=" inline-block " - > - {skill.title} - - handleRemoveSkill(index)} - className="ml-1 text-primary hover:text-accent duration-300 w-4 h-4 inline-block" - /> -
- ))} - -
{ - setIsModalVisible(true); - }} - > - add -
-
- - setIsModalVisible(false)} - footer={[ - , - , - ]} - > - - - - ); -}; - -export const SkillLoader = ({ - skill, - setSkill, -}: { - skill: ISkill | null; - setSkill: (skill: ISkill | null) => void; -}) => { - const [skills, setSkills] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = React.useState({ - status: true, - message: "All good", - }); - const serverUrl = getServerUrl(); - const { user } = React.useContext(appContext); - const listSkillsUrl = `${serverUrl}/skills?user_id=${user?.email}`; - - const fetchSkills = () => { - setError(null); - setLoading(true); - // const fetch; - const payLoad = { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }; - - const onSuccess = (data: any) => { - if (data && data.status) { - message.success(data.message); - setSkills(data.data); - if (data.data.length > 0) { - setSkill(data.data[0]); - } - } else { - message.error(data.message); - } - setLoading(false); - }; - const onError = (err: any) => { - setError(err); - message.error(err.message); - setLoading(false); - }; - fetchJSON(listSkillsUrl, payLoad, onSuccess, onError); - }; - - useEffect(() => { - fetchSkills(); - }, []); - - const skillOptions = skills.map((skill: ISkill, index: number) => ({ - label: skill.title, - value: index, - })); - return ( -
- - - {skills && ( - <> - ({ - label: spec.config.name, - value: index, - }))} - /> -
- )} - {/* {JSON.stringify(localAgent)} */} - - ); -}; - -export const AgentSelector = ({ - flowSpec, - setFlowSpec, -}: { - flowSpec: IAgentFlowSpec | null; - setFlowSpec: (agent: IAgentFlowSpec | null) => void; -}) => { - const [isModalVisible, setIsModalVisible] = useState(false); - - return ( -
-
setIsModalVisible(true)} - className="hover:bg-secondary h-full duration-300 border border-dashed rounded p-2" - > - {flowSpec && ( -
- {flowSpec.type === "groupchat" ? ( - - ) : ( - - )} - {flowSpec.config.name} -
- {" "} - {flowSpec.config.description || flowSpec.config.name} -
-
- {" "} - - {(flowSpec.skills && flowSpec.skills?.length) || 0} skills - - - | max replies: {flowSpec.config.max_consecutive_auto_reply} - -
-
- )} -
- { - <> - { - setFlowSpec(agent); - }} - /> - - } -
- ); -}; -export const FlowConfigViewer = ({ - flowConfig, - setFlowConfig, -}: { - flowConfig: IFlowConfig; - setFlowConfig: (newFlowConfig: IFlowConfig) => void; -}) => { - // Local state for sender and receiver FlowSpecs - const [senderFlowSpec, setSenderFlowSpec] = - React.useState(flowConfig.sender); - - const [localFlowConfig, setLocalFlowConfig] = - React.useState(flowConfig); - - const [receiverFlowSpec, setReceiverFlowSpec] = - React.useState(flowConfig.receiver); - - // Update the local state and propagate changes to the parent component - const updateSenderFlowSpec = (newFlowSpec: IAgentFlowSpec | null) => { - setSenderFlowSpec(newFlowSpec); - if (newFlowSpec) { - setFlowConfig({ ...flowConfig, sender: newFlowSpec }); - } - }; - - const updateReceiverFlowSpec = (newFlowSpec: IAgentFlowSpec | null) => { - setReceiverFlowSpec(newFlowSpec); - if (newFlowSpec) { - setFlowConfig({ ...flowConfig, receiver: newFlowSpec }); - } - }; - - const updateFlowConfig = (key: string, value: string) => { - // When an updatedFlowConfig is created using localFlowConfig, if the contents of FlowConfigViewer Modal are changed after the Agent Specification Modal is updated, the updated contents of the Agent Specification Modal are not saved. Fixed to localFlowConfig->flowConfig. Fixed a bug. - const updatedFlowConfig = { ...flowConfig, [key]: value }; - console.log("updatedFlowConfig: ", updatedFlowConfig); - setLocalFlowConfig(updatedFlowConfig); - setFlowConfig(updatedFlowConfig); - }; - - return ( - <> - {/*
{flowConfig.name}
*/} - updateFlowConfig("name", e.target.value)} - /> - } - /> - - updateFlowConfig("description", e.target.value)} - /> - } - /> - - updateFlowConfig("summary_method", value)} - options={ - [ - { label: "last", value: "last" }, - { label: "none", value: "none" }, - { label: "llm", value: "llm" }, - ] as any - } - /> - } - /> -
-
-
Sender
- -
-
-
Receiver
- -
-
- - ); -}; - export const MonacoEditor = ({ value, editorRef, diff --git a/samples/apps/autogen-studio/frontend/src/components/header.tsx b/samples/apps/autogen-studio/frontend/src/components/header.tsx index 8ec85326923..d0adf2e0a3a 100644 --- a/samples/apps/autogen-studio/frontend/src/components/header.tsx +++ b/samples/apps/autogen-studio/frontend/src/components/header.tsx @@ -25,7 +25,7 @@ const Header = ({ meta, link }: any) => { const links: any[] = [ { name: "Build", href: "/build" }, { name: "Playground", href: "/" }, - { name: "Gallery", href: "/gallery" }, + // { name: "Gallery", href: "/gallery" }, // { name: "Data Explorer", href: "/explorer" }, ]; diff --git a/samples/apps/autogen-studio/frontend/src/components/types.ts b/samples/apps/autogen-studio/frontend/src/components/types.ts index 522682a4884..eba39144602 100644 --- a/samples/apps/autogen-studio/frontend/src/components/types.ts +++ b/samples/apps/autogen-studio/frontend/src/components/types.ts @@ -2,14 +2,13 @@ export type NotificationType = "success" | "info" | "warning" | "error"; export interface IMessage { user_id: string; - root_msg_id: string; - msg_id?: string; role: string; content: string; - timestamp?: string; - personalize?: boolean; - ra?: string; - session_id?: string; + created_at?: string; + updated_at?: string; + session_id?: number; + connection_id?: string; + workflow_id?: number; } export interface IStatus { @@ -21,7 +20,7 @@ export interface IStatus { export interface IChatMessage { text: string; sender: "user" | "bot"; - metadata?: any; + meta?: any; msg_id: string; } @@ -30,6 +29,7 @@ export interface ILLMConfig { timeout?: number; cache_seed?: number | null; temperature: number; + max_tokens: number; } export interface IAgentConfig { @@ -40,47 +40,36 @@ export interface IAgentConfig { system_message: string | ""; is_termination_msg?: boolean | string; default_auto_reply?: string | null; - code_execution_config?: boolean | string | { [key: string]: any } | null; + code_execution_config?: "none" | "local" | "docker"; description?: string; -} -export interface IAgentFlowSpec { - type: "assistant" | "userproxy" | "groupchat"; - config: IAgentConfig; - timestamp?: string; - id?: string; - skills?: Array; - user_id?: string; + admin_name?: string; + messages?: Array; + max_round?: number; + speaker_selection_method?: string; + allow_repeat_speaker?: boolean; } -export interface IGroupChatConfig { - agents: Array; - admin_name: string; - messages: Array; - max_round: number; - speaker_selection_method: "auto" | "round_robin" | "random"; - allow_repeat_speaker: boolean | Array; -} - -export interface IGroupChatFlowSpec { - type: "groupchat"; +export interface IAgent { + type?: "assistant" | "userproxy" | "groupchat"; config: IAgentConfig; - groupchat_config: IGroupChatConfig; - id?: string; - timestamp?: string; + created_at?: string; + updated_at?: string; + id?: number; + skills?: Array; user_id?: string; - description?: string; } -export interface IFlowConfig { +export interface IWorkflow { name: string; description: string; - sender: IAgentFlowSpec; - receiver: IAgentFlowSpec | IGroupChatFlowSpec; + sender: IAgent; + receiver: IAgent; type: "twoagents" | "groupchat"; - timestamp?: string; + created_at?: string; + updated_at?: string; summary_method?: "none" | "last" | "llm"; - id?: string; + id?: number; user_id?: string; } @@ -89,11 +78,12 @@ export interface IModelConfig { api_key?: string; api_version?: string; base_url?: string; - api_type?: string; + api_type?: "open_ai" | "azure" | "google"; user_id?: string; - timestamp?: string; + created_at?: string; + updated_at?: string; description?: string; - id?: string; + id?: number; } export interface IMetadataFile { @@ -105,27 +95,29 @@ export interface IMetadataFile { } export interface IChatSession { - id: string; + id?: number; user_id: string; - timestamp: string; - flow_config: IFlowConfig; + workflow_id?: number; + created_at?: string; + updated_at?: string; name: string; } export interface IGalleryItem { - id: string; + id: number; messages: Array; session: IChatSession; tags: Array; - timestamp: string; + created_at: string; + updated_at: string; } export interface ISkill { - title: string; - file_name?: string; + name: string; content: string; - id?: string; - timestamp?: string; + id?: number; description?: string; user_id?: string; + created_at?: string; + updated_at?: string; } diff --git a/samples/apps/autogen-studio/frontend/src/components/utils.ts b/samples/apps/autogen-studio/frontend/src/components/utils.ts index 73b9f42207c..2264f5c66a2 100644 --- a/samples/apps/autogen-studio/frontend/src/components/utils.ts +++ b/samples/apps/autogen-studio/frontend/src/components/utils.ts @@ -1,12 +1,11 @@ import { + IAgent, IAgentConfig, - IAgentFlowSpec, - IFlowConfig, - IGroupChatFlowSpec, ILLMConfig, IModelConfig, ISkill, IStatus, + IWorkflow, } from "./types"; export const getServerUrl = () => { @@ -66,7 +65,8 @@ export function fetchJSON( url: string | URL, payload: any = {}, onSuccess: (data: any) => void, - onError: (error: IStatus) => void + onError: (error: IStatus) => void, + onFinal: () => void = () => {} ) { return fetch(url, payload) .then(function (response) { @@ -95,6 +95,9 @@ export function fetchJSON( status: false, message: `There was an error connecting to server. (${err}) `, }); + }) + .finally(() => { + onFinal(); }); } export const capitalize = (s: string) => { @@ -243,60 +246,138 @@ export const formatDuration = (seconds: number) => { return parts.length > 0 ? parts.join(" ") : "0 sec"; }; -export const sampleAgentConfig = (user_id: string = "guestuser@gmail.com") => { - const sampleAgent: IAgentFlowSpec = { +export const sampleModelConfig = (modelType: string = "open_ai") => { + const openaiConfig: IModelConfig = { + model: "gpt-4-1106-preview", + api_type: "open_ai", + description: "OpenAI GPT-4 model", + }; + const azureConfig: IModelConfig = { + model: "gpt-4", + api_type: "azure", + api_version: "v1", + base_url: "https://youazureendpoint.azure.com/", + description: "Azure model", + }; + + const googleConfig: IModelConfig = { + model: "gemini-1.0-pro", + api_type: "google", + description: "Google Gemini Model model", + }; + + switch (modelType) { + case "open_ai": + return openaiConfig; + case "azure": + return azureConfig; + case "google": + return googleConfig; + default: + return openaiConfig; + } +}; + +export const getRandomIntFromDateAndSalt = (salt: number = 43444) => { + const currentDate = new Date(); + const seed = currentDate.getTime() + salt; + const randomValue = Math.sin(seed) * 10000; + const randomInt = Math.floor(randomValue) % 100; + return randomInt; +}; + +export const sampleAgentConfig = (agent_type: string = "assistant") => { + const llm_config: ILLMConfig = { + config_list: [], + temperature: 0.1, + timeout: 600, + cache_seed: null, + max_tokens: 1000, + }; + + const userProxyConfig: IAgentConfig = { + name: "userproxy", + human_input_mode: "NEVER", + description: "User Proxy", + max_consecutive_auto_reply: 25, + system_message: "You are a helpful assistant.", + default_auto_reply: "TERMINATE", + llm_config: false, + code_execution_config: "local", + }; + const userProxyFlowSpec: IAgent = { + type: "userproxy", + config: userProxyConfig, + }; + + const assistantConfig: IAgentConfig = { + name: "primary_assistant", + description: "Primary Assistant", + llm_config: llm_config, + human_input_mode: "NEVER", + max_consecutive_auto_reply: 25, + code_execution_config: "none", + system_message: + "You are a helpful AI assistant. Solve tasks using your coding and language skills. In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly. Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill. When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user. If you want the user to save the code in a file before executing it, put # filename: inside the code block as the first line. Don't include multiple code blocks in one response. Do not ask users to copy and paste the result. Instead, use 'print' function for the output when relevant. Check the execution result returned by the user. If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. Reply 'TERMINATE' in the end when everything is done.", + }; + + const assistantFlowSpec: IAgent = { type: "assistant", - user_id: user_id, - config: { - name: "sample_assistant", - description: "Sample assistant", - llm_config: { - config_list: [ - { - model: "gpt-4-1106-preview", - }, - ], - temperature: 0.1, - timeout: 600, - cache_seed: null, - }, - human_input_mode: "NEVER", - code_execution_config: false, - max_consecutive_auto_reply: 8, - system_message: - "You are a helpful AI assistant. Solve tasks using your coding and language skills. In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly. Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill. When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user. If you want the user to save the code in a file before executing it, put # filename: inside the code block as the first line. Don't include multiple code blocks in one response. Do not ask users to copy and paste the result. Instead, use 'print' function for the output when relevant. Check the execution result returned by the user. If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. Reply 'TERMINATE' in the end when everything is done.", + config: assistantConfig, + }; + + const groupChatAssistantConfig = Object.assign( + { + admin_name: "groupchat_assistant", + messages: [], + max_round: 10, + speaker_selection_method: "auto", + allow_repeat_speaker: false, }, + assistantConfig + ); + groupChatAssistantConfig.name = "groupchat_assistant"; + groupChatAssistantConfig.system_message = + "You are a helpful assistant skilled at cordinating a group of other assistants to solve a task. "; + groupChatAssistantConfig.description = "Group Chat Assistant"; + + const groupChatFlowSpec: IAgent = { + type: "groupchat", + config: groupChatAssistantConfig, }; - return sampleAgent; + + if (agent_type === "userproxy") { + return userProxyFlowSpec; + } else if (agent_type === "assistant") { + return assistantFlowSpec; + } else if (agent_type === "groupchat") { + return groupChatFlowSpec; + } else { + return assistantFlowSpec; + } }; export const sampleWorkflowConfig = (type = "twoagents") => { - const llm_model_config: IModelConfig[] = [ - { - model: "gpt-4-1106-preview", - }, - ]; + const llm_model_config: IModelConfig[] = []; const llm_config: ILLMConfig = { config_list: llm_model_config, temperature: 0.1, timeout: 600, cache_seed: null, + max_tokens: 1000, }; const userProxyConfig: IAgentConfig = { name: "userproxy", human_input_mode: "NEVER", - max_consecutive_auto_reply: 5, + max_consecutive_auto_reply: 15, system_message: "You are a helpful assistant.", default_auto_reply: "TERMINATE", llm_config: false, - code_execution_config: { - work_dir: null, - use_docker: false, - }, + code_execution_config: "local", }; - const userProxyFlowSpec: IAgentFlowSpec = { + const userProxyFlowSpec: IAgent = { type: "userproxy", config: userProxyConfig, }; @@ -306,17 +387,17 @@ export const sampleWorkflowConfig = (type = "twoagents") => { llm_config: llm_config, human_input_mode: "NEVER", max_consecutive_auto_reply: 8, - code_execution_config: false, + code_execution_config: "none", system_message: "You are a helpful AI assistant. Solve tasks using your coding and language skills. In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly. Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill. When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user. If you want the user to save the code in a file before executing it, put # filename: inside the code block as the first line. Don't include multiple code blocks in one response. Do not ask users to copy and paste the result. Instead, use 'print' function for the output when relevant. Check the execution result returned by the user. If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. Reply 'TERMINATE' in the end when everything is done.", }; - const assistantFlowSpec: IAgentFlowSpec = { + const assistantFlowSpec: IAgent = { type: "assistant", config: assistantConfig, }; - const workFlowConfig: IFlowConfig = { + const workFlowConfig: IWorkflow = { name: "Default Agent Workflow", description: "Default Agent Workflow", sender: userProxyFlowSpec, @@ -324,26 +405,27 @@ export const sampleWorkflowConfig = (type = "twoagents") => { type: "twoagents", }; - const groupChatAssistantConfig = Object.assign({}, assistantConfig); - groupChatAssistantConfig.name = "groupchat_assistant"; - groupChatAssistantConfig.system_message = - "You are a helpful assistant skilled at cordinating a group of other assistants to solve a task. "; - - const groupChatFlowSpec: IGroupChatFlowSpec = { - type: "groupchat", - config: groupChatAssistantConfig, - groupchat_config: { - agents: [assistantFlowSpec, assistantFlowSpec], + const groupChatAssistantConfig = Object.assign( + { admin_name: "groupchat_assistant", messages: [], max_round: 10, speaker_selection_method: "auto", allow_repeat_speaker: false, + description: "Group Chat Assistant", }, - description: "Default Group Workflow", + assistantConfig + ); + groupChatAssistantConfig.name = "groupchat_assistant"; + groupChatAssistantConfig.system_message = + "You are a helpful assistant skilled at cordinating a group of other assistants to solve a task. "; + + const groupChatFlowSpec: IAgent = { + type: "groupchat", + config: groupChatAssistantConfig, }; - const groupChatWorkFlowConfig: IFlowConfig = { + const groupChatWorkFlowConfig: IWorkflow = { name: "Default Group Workflow", description: "Default Group Workflow", sender: userProxyFlowSpec, @@ -359,79 +441,72 @@ export const sampleWorkflowConfig = (type = "twoagents") => { return workFlowConfig; }; -export const getModels = () => { - const models = [ - { - model: "gpt-4-1106-preview", - }, - { - model: "gpt-3.5-turbo-16k", - }, - { - model: "TheBloke/zephyr-7B-alpha-AWQ", - base_url: "http://localhost:8000/v1", - }, - ]; - return models; -}; - export const getSampleSkill = () => { const content = ` - ## This is a sample skill. Replace with your own skill function - ## In general, a good skill must have 3 sections: - ## 1. Imports (import libraries needed for your skill) - ## 2. Function definition AND docstrings (this helps the LLM understand what the function does and how to use it) - ## 3. Function body (the actual code that implements the function) - - import numpy as np - import matplotlib.pyplot as plt - from matplotlib import font_manager as fm - - def save_cat_ascii_art_to_png(filename='ascii_cat.png'): - """ - Creates ASCII art of a cat and saves it to a PNG file. - - :param filename: str, the name of the PNG file to save the ASCII art. - """ - # ASCII art string - cat_art = [ - " /\_/\ ", - " ( o.o ) ", - " > ^ < " - ] - - # Determine shape of output array - height = len(cat_art) - width = max(len(line) for line in cat_art) - - # Create a figure and axis to display ASCII art - fig, ax = plt.subplots(figsize=(width, height)) - ax.axis('off') # Hide axes - - # Get a monospace font - prop = fm.FontProperties(family='monospace') - - # Display ASCII art using text - for y, line in enumerate(cat_art): - ax.text(0, height-y-1, line, fontproperties=prop, fontsize=12) - - # Adjust layout - plt.tight_layout() - - # Save figure to file - plt.savefig(filename, dpi=120, bbox_inches='tight', pad_inches=0.1) - plt.close(fig)`; +from typing import List +import uuid +import requests # to perform HTTP requests +from pathlib import Path + +from openai import OpenAI + + +def generate_and_save_images(query: str, image_size: str = "1024x1024") -> List[str]: + """ + Function to paint, draw or illustrate images based on the users query or request. Generates images from a given query using OpenAI's DALL-E model and saves them to disk. Use the code below anytime there is a request to create an image. + + :param query: A natural language description of the image to be generated. + :param image_size: The size of the image to be generated. (default is "1024x1024") + :return: A list of filenames for the saved images. + """ + + client = OpenAI() # Initialize the OpenAI client + response = client.images.generate(model="dall-e-3", prompt=query, n=1, size=image_size) # Generate images + + # List to store the file names of saved images + saved_files = [] + + # Check if the response is successful + if response.data: + for image_data in response.data: + # Generate a random UUID as the file name + file_name = str(uuid.uuid4()) + ".png" # Assuming the image is a PNG + file_path = Path(file_name) + + img_url = image_data.url + img_response = requests.get(img_url) + if img_response.status_code == 200: + # Write the binary content to a file + with open(file_path, "wb") as img_file: + img_file.write(img_response.content) + print(f"Image saved to {file_path}") + saved_files.append(str(file_path)) + else: + print(f"Failed to download the image from {img_url}") + else: + print("No image data found in the response!") + + # Return the list of saved files + return saved_files + + +# Example usage of the function: +# generate_and_save_images("A cute baby sea otter") + `; const skill: ISkill = { - title: "save_cat_ascii_art_to_png", - description: "save cat ascii art to png", + name: "generate_images", + description: "Generate and save images based on a user's query.", content: content, }; return skill; }; -export const timeAgo = (dateString: string): string => { +export const timeAgo = ( + dateString: string, + returnFormatted: boolean = false +): string => { // if dateStr is empty, return empty string if (!dateString) { return ""; @@ -454,10 +529,20 @@ export const timeAgo = (dateString: string): string => { const minutesAgo = Math.floor(timeDifference / (1000 * 60)); const hoursAgo = Math.floor(minutesAgo / 60); - // Format the date into a readable format e.g. "November 27" - const options: Intl.DateTimeFormatOptions = { month: "long", day: "numeric" }; + // Format the date into a readable format e.g. "November 27, 2021, 3:45 PM" + const options: Intl.DateTimeFormatOptions = { + month: "long", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + }; const formattedDate = timestamp.toLocaleDateString(undefined, options); + if (returnFormatted) { + return formattedDate; + } + // Determine the time difference string let timeAgoStr: string; if (minutesAgo < 1) { @@ -527,7 +612,7 @@ export const fetchVersion = () => { */ export const sanitizeConfig = ( data: any, - keys: string[] = ["api_key", "id"] + keys: string[] = ["api_key", "id", "created_at", "updated_at"] ): any => { if (Array.isArray(data)) { return data.map((item) => sanitizeConfig(item, keys)); diff --git a/samples/apps/autogen-studio/frontend/src/components/views/builder/agents.tsx b/samples/apps/autogen-studio/frontend/src/components/views/builder/agents.tsx index be8a30f7247..8800ebfbdd3 100644 --- a/samples/apps/autogen-studio/frontend/src/components/views/builder/agents.tsx +++ b/samples/apps/autogen-studio/frontend/src/components/views/builder/agents.tsx @@ -8,24 +8,17 @@ import { } from "@heroicons/react/24/outline"; import { Dropdown, MenuProps, Modal, message } from "antd"; import * as React from "react"; -import { IAgentFlowSpec, IStatus } from "../../types"; +import { IAgent, IStatus } from "../../types"; import { appContext } from "../../../hooks/provider"; import { fetchJSON, getServerUrl, - sampleAgentConfig, sanitizeConfig, timeAgo, truncateText, } from "../../utils"; -import { - AgentFlowSpecView, - BounceLoader, - Card, - CardHoverBar, - LaunchButton, - LoadingOverlay, -} from "../../atoms"; +import { BounceLoader, Card, CardHoverBar, LoadingOverlay } from "../../atoms"; +import { AgentViewer } from "./utils/agentconfig"; const AgentsView = ({}: any) => { const [loading, setLoading] = React.useState(false); @@ -37,25 +30,30 @@ const AgentsView = ({}: any) => { const { user } = React.useContext(appContext); const serverUrl = getServerUrl(); const listAgentsUrl = `${serverUrl}/agents?user_id=${user?.email}`; - const saveAgentsUrl = `${serverUrl}/agents`; - const deleteAgentUrl = `${serverUrl}/agents/delete`; - const [agents, setAgents] = React.useState([]); - const [selectedAgent, setSelectedAgent] = - React.useState(null); + const [agents, setAgents] = React.useState([]); + const [selectedAgent, setSelectedAgent] = React.useState(null); const [showNewAgentModal, setShowNewAgentModal] = React.useState(false); const [showAgentModal, setShowAgentModal] = React.useState(false); - const sampleAgent = sampleAgentConfig(user?.email || ""); - const [newAgent, setNewAgent] = React.useState( - sampleAgent - ); + const sampleAgent = { + config: { + name: "sample_agent", + description: "Sample agent description", + human_input_mode: "NEVER", + max_consecutive_auto_reply: 3, + system_message: "", + }, + }; + const [newAgent, setNewAgent] = React.useState(sampleAgent); - const deleteAgent = (agent: IAgentFlowSpec) => { + const deleteAgent = (agent: IAgent) => { setError(null); setLoading(true); + + const deleteAgentUrl = `${serverUrl}/agents/delete?user_id=${user?.email}&agent_id=${agent.id}`; // const fetch; const payLoad = { method: "DELETE", @@ -71,8 +69,7 @@ const AgentsView = ({}: any) => { const onSuccess = (data: any) => { if (data && data.status) { message.success(data.message); - console.log("agents", data.data); - setAgents(data.data); + fetchAgents(); } else { message.error(data.message); } @@ -98,8 +95,6 @@ const AgentsView = ({}: any) => { const onSuccess = (data: any) => { if (data && data.status) { - // message.success(data.message); - setAgents(data.data); } else { message.error(data.message); @@ -114,42 +109,6 @@ const AgentsView = ({}: any) => { fetchJSON(listAgentsUrl, payLoad, onSuccess, onError); }; - const saveAgent = (agent: IAgentFlowSpec) => { - setError(null); - setLoading(true); - // const fetch; - - const payLoad = { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - user_id: user?.email, - agent: agent, - }), - }; - - const onSuccess = (data: any) => { - if (data && data.status) { - message.success(data.message); - // console.log("agents", data.data); - setAgents(data.data); - } else { - message.error(data.message); - } - setLoading(false); - setNewAgent(sampleAgent); - }; - const onError = (err: any) => { - setError(err); - message.error(err.message); - setLoading(false); - }; - fetchJSON(saveAgentsUrl, payLoad, onSuccess, onError); - }; - React.useEffect(() => { if (user) { // console.log("fetching messages", messages); @@ -157,7 +116,7 @@ const AgentsView = ({}: any) => { } }, []); - const agentRows = (agents || []).map((agent: IAgentFlowSpec, i: number) => { + const agentRows = (agents || []).map((agent: IAgent, i: number) => { const cardItems = [ { title: "Download", @@ -185,11 +144,10 @@ const AgentsView = ({}: any) => { let newAgent = { ...agent }; newAgent.config.name = `${agent.config.name}_copy`; newAgent.user_id = user?.email; - newAgent.timestamp = new Date().toISOString(); + newAgent.updated_at = new Date().toISOString(); if (newAgent.id) { delete newAgent.id; } - setNewAgent(newAgent); setShowNewAgentModal(true); }, @@ -206,27 +164,41 @@ const AgentsView = ({}: any) => { }, ]; return ( -
-
- {truncateText(agent.config.name, 25)}
- } - onClick={() => { - setSelectedAgent(agent); - setShowAgentModal(true); - }} - > -
- {" "} - {truncateText(agent.config.description || "", 70)} +
  • + + {truncateText(agent.config.name || "", 25)}
  • -
    {timeAgo(agent.timestamp || "")}
    - - -
    -
    + } + onClick={() => { + setSelectedAgent(agent); + setShowAgentModal(true); + }} + > + +
    + {timeAgo(agent.updated_at || "")} +
    + + + ); }); @@ -237,45 +209,39 @@ const AgentsView = ({}: any) => { setShowAgentModal, handler, }: { - agent: IAgentFlowSpec | null; - setAgent: (agent: IAgentFlowSpec | null) => void; + agent: IAgent | null; + setAgent: (agent: IAgent | null) => void; showAgentModal: boolean; setShowAgentModal: (show: boolean) => void; - handler?: (agent: IAgentFlowSpec | null) => void; + handler?: (agent: IAgent | null) => void; }) => { - const [localAgent, setLocalAgent] = React.useState( - agent - ); + const [localAgent, setLocalAgent] = React.useState(agent); + + const closeModal = () => { + setShowAgentModal(false); + if (handler) { + handler(localAgent); + } + }; return ( - Agent Specification{" "} - - {agent?.config?.name || ""} - {" "} - - } + title={<>Agent Configuration} width={800} open={showAgentModal} onOk={() => { - setAgent(null); - setShowAgentModal(false); - if (handler) { - handler(localAgent); - } + closeModal(); }} onCancel={() => { - setAgent(null); - setShowAgentModal(false); + closeModal(); }} + footer={[]} > {agent && ( - )} {/* {JSON.stringify(localAgent)} */} @@ -344,10 +310,8 @@ const AgentsView = ({}: any) => { setAgent={setSelectedAgent} setShowAgentModal={setShowAgentModal} showAgentModal={showAgentModal} - handler={(agent: IAgentFlowSpec | null) => { - if (agent) { - saveAgent(agent); - } + handler={(agent: IAgent | null) => { + fetchAgents(); }} /> @@ -356,10 +320,8 @@ const AgentsView = ({}: any) => { setAgent={setNewAgent} setShowAgentModal={setShowNewAgentModal} showAgentModal={showNewAgentModal} - handler={(agent: IAgentFlowSpec | null) => { - if (agent) { - saveAgent(agent); - } + handler={(agent: IAgent | null) => { + fetchAgents(); }} /> @@ -397,7 +359,7 @@ const AgentsView = ({}: any) => { {agents && agents.length > 0 && (
    -
    {agentRows}
    +
      {agentRows}
    )} diff --git a/samples/apps/autogen-studio/frontend/src/components/views/builder/models.tsx b/samples/apps/autogen-studio/frontend/src/components/views/builder/models.tsx index be2c11099e3..2a3b0506d79 100644 --- a/samples/apps/autogen-studio/frontend/src/components/views/builder/models.tsx +++ b/samples/apps/autogen-studio/frontend/src/components/views/builder/models.tsx @@ -2,7 +2,6 @@ import { ArrowDownTrayIcon, ArrowUpTrayIcon, DocumentDuplicateIcon, - ExclamationTriangleIcon, InformationCircleIcon, PlusIcon, TrashIcon, @@ -18,8 +17,15 @@ import { timeAgo, truncateText, } from "../../utils"; -import { BounceLoader, Card, CardHoverBar, LoadingOverlay } from "../../atoms"; +import { + BounceLoader, + Card, + CardHoverBar, + ControlRowView, + LoadingOverlay, +} from "../../atoms"; import TextArea from "antd/es/input/TextArea"; +import { ModelConfigView } from "./utils/modelconfig"; const ModelsView = ({}: any) => { const [loading, setLoading] = React.useState(false); @@ -31,8 +37,7 @@ const ModelsView = ({}: any) => { const { user } = React.useContext(appContext); const serverUrl = getServerUrl(); const listModelsUrl = `${serverUrl}/models?user_id=${user?.email}`; - const saveModelsUrl = `${serverUrl}/models`; - const deleteModelUrl = `${serverUrl}/models/delete`; + const createModelUrl = `${serverUrl}/models`; const testModelUrl = `${serverUrl}/models/test`; const defaultModel: IModelConfig = { @@ -50,28 +55,23 @@ const ModelsView = ({}: any) => { ); const [showNewModelModal, setShowNewModelModal] = React.useState(false); - const [showModelModal, setShowModelModal] = React.useState(false); const deleteModel = (model: IModelConfig) => { setError(null); setLoading(true); - // const fetch; + const deleteModelUrl = `${serverUrl}/models/delete?user_id=${user?.email}&model_id=${model.id}`; const payLoad = { method: "DELETE", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ - user_id: user?.email, - model: model, - }), }; const onSuccess = (data: any) => { if (data && data.status) { message.success(data.message); - setModels(data.data); + fetchModels(); } else { message.error(data.message); } @@ -111,9 +111,10 @@ const ModelsView = ({}: any) => { fetchJSON(listModelsUrl, payLoad, onSuccess, onError); }; - const saveModel = (model: IModelConfig) => { + const createModel = (model: IModelConfig) => { setError(null); setLoading(true); + model.user_id = user?.email; const payLoad = { method: "POST", @@ -121,17 +122,14 @@ const ModelsView = ({}: any) => { Accept: "application/json", "Content-Type": "application/json", }, - body: JSON.stringify({ - user_id: user?.email, - model: model, - }), + body: JSON.stringify(model), }; const onSuccess = (data: any) => { if (data && data.status) { message.success(data.message); - // console.log("models", data.data); - setModels(data.data); + const updatedModels = [data.data].concat(models || []); + setModels(updatedModels); } else { message.error(data.message); } @@ -142,7 +140,7 @@ const ModelsView = ({}: any) => { message.error(err.message); setLoading(false); }; - fetchJSON(saveModelsUrl, payLoad, onSuccess, onError); + fetchJSON(createModelUrl, payLoad, onSuccess, onError); }; React.useEffect(() => { @@ -180,7 +178,7 @@ const ModelsView = ({}: any) => { let newModel = { ...model }; newModel.model = `${model.model} Copy`; newModel.user_id = user?.email; - newModel.timestamp = new Date().toISOString(); + newModel.updated_at = new Date().toISOString(); if (newModel.id) { delete newModel.id; } @@ -200,27 +198,35 @@ const ModelsView = ({}: any) => { }, ]; return ( -
    -
    - {truncateText(model.model || "", 20)}
    - } - onClick={() => { - setSelectedModel(model); - setShowModelModal(true); - }} +
  • + {truncateText(model.model || "", 20)}
  • + } + onClick={() => { + setSelectedModel(model); + setShowModelModal(true); + }} + > +
    + {" "} + {truncateText(model.description || model.model || "", 70)} +
    +
    -
    - {" "} - {truncateText(model.description || model.model || "", 70)} -
    -
    {timeAgo(model.timestamp || "")}
    - - -
    -
    + {timeAgo(model.updated_at || "")} +
    + + + ); }); @@ -231,47 +237,20 @@ const ModelsView = ({}: any) => { setShowModelModal, handler, }: { - model: IModelConfig | null; + model: IModelConfig; setModel: (model: IModelConfig | null) => void; showModelModal: boolean; setShowModelModal: (show: boolean) => void; handler?: (agent: IModelConfig) => void; }) => { - const [loadingModelTest, setLoadingModelTest] = React.useState(false); - const [modelStatus, setModelStatus] = React.useState(null); - - const [localModel, setLocalModel] = React.useState( - model - ); - const testModel = (model: IModelConfig) => { - setModelStatus(null); - setLoadingModelTest(true); - const payLoad = { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - user_id: user?.email, - model: model, - }), - }; + const [localModel, setLocalModel] = React.useState(model); - const onSuccess = (data: any) => { - if (data && data.status) { - message.success(data.message); - setModelStatus(data.data); - } else { - message.error(data.message); - } - setLoadingModelTest(false); - setModelStatus(data); - }; - const onError = (err: any) => { - message.error(err.message); - setLoadingModelTest(false); - }; - fetchJSON(testModelUrl, payLoad, onSuccess, onError); + const closeModal = () => { + setModel(null); + setShowModelModal(false); + if (handler) { + handler(model); + } }; return ( @@ -284,137 +263,21 @@ const ModelsView = ({}: any) => { } width={800} open={showModelModal} - footer={[ - , - , - , - ]} + footer={[]} onOk={() => { - setModel(null); - setShowModelModal(false); - if (handler) { - if (localModel) { - handler(localModel); - } - } + closeModal(); }} onCancel={() => { - setModel(null); - setShowModelModal(false); + closeModal(); }} > -
    -
    Enter parameters for your model.
    - { - setLocalModel({ ...localModel, model: e.target.value }); - }} - /> - { - if (localModel) { - setLocalModel({ ...localModel, api_key: e.target.value }); - } - }} - /> - { - if (localModel) { - setLocalModel({ ...localModel, base_url: e.target.value }); - } - }} - /> - { - if (localModel) { - setLocalModel({ ...localModel, api_type: e.target.value }); - } - }} + {model && ( + - { - if (localModel) { - setLocalModel({ ...localModel, api_version: e.target.value }); - } - }} - /> -