From c908a26c2e99f15732166765d6b980c12c3d1346 Mon Sep 17 00:00:00 2001 From: Mohan Kumar Mithur Date: Thu, 15 Jul 2021 03:09:58 -0400 Subject: [PATCH 01/18] HIP-Testsuite initial commit This contains set of scripts for cloning/building/running several tests from various external repos Change-Id: I5dcedde32989b736f8e9078495d37630fa3730d3 --- README.md | 68 + cfg.py | 79 + requirements.txt | 1 + run.py | 51 + src/amd/AMD.py | 3 + src/amd/Test.py | 201 ++ src/amd/TesterRepository.py | 128 + src/amd/TestersExecutor.py | 276 +++ src/amd/__init__.py | 0 src/amd/applications/__init__.py | 0 .../application_test_classifier.py | 11 + src/amd/applications/hip_examples/__init__.py | 0 .../applications/hip_examples/hip_examples.py | 2184 +++++++++++++++++ .../hip_examples/hip_examples_build.py | 353 +++ .../hip_examples/hip_examples_parser.py | 247 ++ .../hip_samples/Samples_Patch_4.2.x | 1514 ++++++++++++ src/amd/applications/hip_samples/__init__.py | 0 .../applications/hip_samples/hip_samples.py | 1290 ++++++++++ .../hip_samples/hip_samples_build.py | 85 + src/amd/common/__init__.py | 0 src/amd/common/hip_get_packages.py | 89 + src/amd/common/hip_shell.py | 23 + src/amd/config_processor.py | 10 + src/amd/conformance/__init__.py | 0 .../conformance_test_classifier.py | 11 + src/amd/conformance/hip_dtest.py | 167 ++ src/amd/conformance/hip_dtest_build.py | 253 ++ src/amd/list_tests.py | 122 + src/amd/match_fun_args_call.py | 12 + src/amd/stress/__init__.py | 0 src/amd/targets.py | 144 ++ src/amd/test_classifier.py | 14 + src/amd/test_selector.py | 122 + src/amd/version.py | 3 + temp/CTEST_parse.py | 116 + temp/temp.py | 31 + 36 files changed, 7608 insertions(+) create mode 100644 README.md create mode 100644 cfg.py create mode 100644 requirements.txt create mode 100644 run.py create mode 100644 src/amd/AMD.py create mode 100644 src/amd/Test.py create mode 100644 src/amd/TesterRepository.py create mode 100644 src/amd/TestersExecutor.py create mode 100644 src/amd/__init__.py create mode 100644 src/amd/applications/__init__.py create mode 100644 src/amd/applications/application_test_classifier.py create mode 100644 src/amd/applications/hip_examples/__init__.py create mode 100644 src/amd/applications/hip_examples/hip_examples.py create mode 100644 src/amd/applications/hip_examples/hip_examples_build.py create mode 100644 src/amd/applications/hip_examples/hip_examples_parser.py create mode 100644 src/amd/applications/hip_samples/Samples_Patch_4.2.x create mode 100644 src/amd/applications/hip_samples/__init__.py create mode 100644 src/amd/applications/hip_samples/hip_samples.py create mode 100644 src/amd/applications/hip_samples/hip_samples_build.py create mode 100644 src/amd/common/__init__.py create mode 100644 src/amd/common/hip_get_packages.py create mode 100644 src/amd/common/hip_shell.py create mode 100644 src/amd/config_processor.py create mode 100644 src/amd/conformance/__init__.py create mode 100644 src/amd/conformance/conformance_test_classifier.py create mode 100644 src/amd/conformance/hip_dtest.py create mode 100644 src/amd/conformance/hip_dtest_build.py create mode 100644 src/amd/list_tests.py create mode 100644 src/amd/match_fun_args_call.py create mode 100644 src/amd/stress/__init__.py create mode 100644 src/amd/targets.py create mode 100644 src/amd/test_classifier.py create mode 100644 src/amd/test_selector.py create mode 100644 src/amd/version.py create mode 100644 temp/CTEST_parse.py create mode 100644 temp/temp.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..bf6cccc --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +Test Repository +# testsuite + +Right now the test set is composed of four high-level directories as git submodules: +- HIP-Examples (official HIP examples) + +``` + +Since we use submodules, to clone all the submodules, you'll need to use + +``` +git clone https://github.com/mohanmithur/testsuite +cd testsuite +git submodule update --init --recursive +``` + +To update the submodules to the latest: +Enter the submodule directory: +``` +cd projB/projA +``` +Pull the repo from you project A (will not update the git status of your parent, project B): +``` +git pull origin master +``` +Go back to the root directory & check update: +``` +cd .. +git status +``` +If the submodule updated before, it will show something like below: +``` +# Not currently on any branch. +# Changed but not updated: +# (use "git add ..." to update what will be committed) +# (use "git checkout -- ..." to discard changes in working directory) +# +# modified: projB/projA (new commits) +# +``` +Then, commit the update: +``` +git add projB/projA +git commit -m "projA submodule updated" +``` +(From https://stackoverflow.com/questions/8191299/update-a-submodule-to-the-latest-commit) + +# Running the tests on JLSE + +To run on gen9 on JLSE: + +``` +$ ./runSuite.sh +``` + +# Adding another repo as a submodule + +``` +$ git submodule add https://github.com/chaconinc/DbConnector +$ git commit -am 'Add DbConnector module' +``` + +# Removing a submodule + +``` +$ git rm the_submodule +$ rm -rf .git/modules/the_submodule +``` diff --git a/cfg.py b/cfg.py new file mode 100644 index 0000000..715b5c7 --- /dev/null +++ b/cfg.py @@ -0,0 +1,79 @@ +version = "1.0.0" + +user_password = "AH64_uh1" +log_location = None + +# None/amd/nvidia +HIP_PLATFORM = None + +# None/0/1/2/3 +Optimization_Level = None + +# None/0/1/2/4 +HIPCC_VERBOSE = None + +# None/cuda directory +CUDA_PATH = None + +# None/rocm directory +ROCM_PATH = None + +# None/offload target +build_for_target = None + +# -I. +# None/List of paths +includes_path = None + +# -l +# None/List of -l +link_libs = None + +# L. +# None/list of -L +link_libs_path = None + +# None/(test_name/test_suite/regex OR list of test_names/test_suites/regex) +# e.g.1 run_tests = ["bitextract", "matrixtranspose"] +# e.g.2 run_tests = "hip_samples" +# e.g.3 run_tests = ".*samples.*" + +# If ts: test suite, tc: test case +# e.g.4 run_tests = [[ts1,tc1], [ts1,tc2], [tc3], [ts2,tc4], [ts3]] +# e.g.4 run_tests = [ts1:tc1, ts1:tc2, tc3, ts2:tc4, ts3] +run_tests = None + + +branch = "rocm-4.2.x" +repos = { + "hip_examples": { + "repo_url": "https://github.com/ROCm-Developer-Tools/HIP-Examples", + "branch": branch, + # "commit_id": "" + }, + "hip": { + "repo_url": "https://github.com/ROCm-Developer-Tools/HIP", + "branch": branch, + # "commit_id": "" + }, + "mixbench": { + "repo_url": "https://github.com/ekondis/mixbench.git", + "branch": None, + # "commit_id": "" + }, + "gpu_stream": { + "repo_url": "https://github.com/UoB-HPC/GPU-STREAM.git", + "branch": None, + # "commit_id": "" + }, + "rocclr": { + "repo_url": "https://github.com/ROCm-Developer-Tools/ROCclr.git", + "branch": branch, + # "commit_id": "" + }, + "opencl": { + "repo_url": "https://github.com/RadeonOpenCompute/ROCm-OpenCL-Runtime.git", + "branch": branch, + # "commit_id": "" + } +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..04fdfa2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +prettytable \ No newline at end of file diff --git a/run.py b/run.py new file mode 100644 index 0000000..ba3a71e --- /dev/null +++ b/run.py @@ -0,0 +1,51 @@ +import sys +import os +import argparse + +if __name__ == "__main__": + sys.path.append(os.path.join(os.path.dirname(__file__), "src")) + + +from amd.TestersExecutor import TestersExecutor +from amd.list_tests import list_tests +import cfg + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--platform', help="On which hip_platform to test? amd/nvidia/ default:amd") + parser.add_argument('-t', '--tests', nargs='+', + # required=True, + # choices=TESTS_CHOICES, + metavar='', help="Test name/Regex/Category/Category:*/List of those separated by space") + parser.add_argument('-lst', '--list_tests', default=False, action='store_true', help="List all tests") + parser.add_argument('-lstq', '--list_tests_quick', default=False, action='store_true', help="List all tests quickly, Warning: This may not list some tests which are time consuming to generate, and only category:* will be displayed for them, use -lst for listing all tests") + + args = parser.parse_args() + + if args.platform: + cfg.HIP_PLATFORM = args.platform + + if args.list_tests: + list_tests(quick=False, cfg=cfg) + return False + + if args.list_tests_quick: + list_tests(quick=True, cfg=cfg) + return False + + if args.tests: + cfg.run_tests = args.tests + + return True + + +def main(): + if parse_args(): + tester_executor: TestersExecutor = TestersExecutor() + tester_executor.config = cfg + tester_executor.executeTests() + + +if __name__ == "__main__": + main() diff --git a/src/amd/AMD.py b/src/amd/AMD.py new file mode 100644 index 0000000..d9738fe --- /dev/null +++ b/src/amd/AMD.py @@ -0,0 +1,3 @@ +class AMDObject: + def __init__(self): + pass diff --git a/src/amd/Test.py b/src/amd/Test.py new file mode 100644 index 0000000..e49139d --- /dev/null +++ b/src/amd/Test.py @@ -0,0 +1,201 @@ +from typing import List, Union, Set, Dict +from amd.AMD import AMDObject +from amd.targets import Target +from enum import Enum, auto +from amd.test_classifier import TestClassifier +from amd.config_processor import ConfigProcessor + + +class Test(AMDObject): + def __init__(self): + # Cycle Import + from amd.TesterRepository import Tester + + AMDObject.__init__(self) + self.test_name: Union[None, str] = None + self.also_matched_with_test_names: Union[None, List[str]] = None + self.classifiers: Union[None, List[TestClassifier]] = None + self.tester: Union[None, Tester] = None + self.applicable_for_target: Union[None, Set[Target]] = None + + +class TestResult(Enum): + PASS = auto() + FAIL = auto() + SKIP = auto() + ERROR = auto() + + +class UserAccess(ConfigProcessor): + def __init__(self): + ConfigProcessor.__init__(self) + self.user_password: Union[None, str] = None + + def loadConfig(self): + self.user_password = self.config.user_password + + +class LogLocation(AMDObject): + def __init__(self): + AMDObject.__init__(self) + self.log_location: Union[None, str] = None + + +class TestData(AMDObject): + def __init__(self): + AMDObject.__init__(self) + self.test: Union[None, Test] = None + self.test_result: Union[None, TestResult] = None + + +class CompileData(AMDObject): + def __init__(self): + AMDObject.__init__(self) + self.build_for_target: Union[None, Set[Target]] = None + + +class GitData(AMDObject): + def __init__(self): + AMDObject.__init__(self) + self.repo_url: Union[None, str] = None + self.branch: Union[None, str] = None + self.commit_id: Union[None, str] = None + + +class AllGitData(ConfigProcessor): + def __init__(self): + ConfigProcessor.__init__(self) + self.repos: Union[None, Dict[str, GitData]] = None + + def loadConfig(self): + repos = dict() + for repo_key, repo_detail in self.config.repos.items(): + git_data = GitData() + git_data.repo_url = repo_detail["repo_url"] + if "branch" in repo_detail: + git_data.branch = repo_detail["branch"] + if "commit_id" in repo_detail: + git_data.commit_id = repo_detail["commit_id"] + repos[repo_key] = git_data + self.repos = repos + + +class HIPCCVerbose(Enum): + ZERO = auto() + ONE = auto() + TWO = auto() + FOUR = auto() + + +class Optimization_Level(Enum): + ZERO = auto() + ONE = auto() + TWO = auto() + THREE = auto() + + +class HIP_PLATFORM(Enum): + nvidia = auto() + amd = auto() + + +class HIPCCCompileData(CompileData, ConfigProcessor): + def __init__(self): + CompileData.__init__(self) + ConfigProcessor.__init__(self) + + self.Optimization_Level: Union[None, Optimization_Level] = None + + self.HIPCC_VERBOSE: Union[None, HIPCCVerbose] = None + self.HIP_PLATFORM: Union[None, HIP_PLATFORM] = None + + self.CUDA_PATH: Union[None, str] = None + self.ROCM_PATH: Union[None, str] = None + + # if self.build_for_target, then --offload_arch= + + # -I ./ + self.includes_path: Union[None, List[str]] = None + # -lm + self.link_libs: Union[None, List[str]] = None + # -L. + self.link_libs_path: Union[None, List[str]] = None + + def loadConfig(self): + if self.config.HIP_PLATFORM == "amd": + self.HIP_PLATFORM = HIP_PLATFORM.amd + elif self.config.HIP_PLATFORM == "nvidia": + self.HIP_PLATFORM = HIP_PLATFORM.nvidia + else: + self.HIP_PLATFORM = HIP_PLATFORM.amd + + if self.config.Optimization_Level == 0: + self.Optimization_Level = Optimization_Level.ZERO + elif self.config.Optimization_Level == 1: + self.Optimization_Level = Optimization_Level.ONE + elif self.config.Optimization_Level == 2: + self.Optimization_Level = Optimization_Level.TWO + elif self.config.Optimization_Level == 3: + self.Optimization_Level = Optimization_Level.THREE + else: + self.Optimization_Level = None + + if self.config.HIPCC_VERBOSE == 0: + self.HIPCC_VERBOSE = HIPCCVerbose.ZERO + elif self.config.HIPCC_VERBOSE == 1: + self.HIPCC_VERBOSE = HIPCCVerbose.ONE + elif self.config.HIPCC_VERBOSE == 2: + self.HIPCC_VERBOSE = HIPCCVerbose.TWO + elif self.config.HIPCC_VERBOSE == 4: + self.HIPCC_VERBOSE = HIPCCVerbose.FOUR + else: + self.HIPCC_VERBOSE = None + + self.ROCM_PATH = self.config.ROCM_PATH + self.CUDA_PATH = self.config.CUDA_PATH + + self.build_for_target = self.config.build_for_target + self.includes_path = self.config.includes_path + self.link_libs = self.config.link_libs + self.link_libs_path = self.config.link_libs_path + + # ToDo offload_target + + +class HIPTestData(TestData, HIPCCCompileData, AllGitData, LogLocation, UserAccess): + def __init__(self): + UserAccess.__init__(self) + LogLocation.__init__(self) + TestData.__init__(self) + HIPCCCompileData.__init__(self) + AllGitData.__init__(self) + + def loadConfig(self): + UserAccess.loadConfig(self) + AllGitData.loadConfig(self) + HIPCCCompileData.loadConfig(self) + + +class Quick(AMDObject): + def __init__(self): + AMDObject.__init__(self) + self.quick: bool = False + + +class GetTestsData(Quick): + def __init__(self): + Quick.__init__(self) + + +class HIPBuildData(GetTestsData, HIPCCCompileData, UserAccess, AllGitData, LogLocation): + def __init__(self): + GetTestsData.__init__(self) + HIPCCCompileData.__init__(self) + UserAccess.__init__(self) + AllGitData.__init__(self) + LogLocation.__init__(self) + + def loadConfig(self): + UserAccess.loadConfig(self) + AllGitData.loadConfig(self) + HIPCCCompileData.loadConfig(self) diff --git a/src/amd/TesterRepository.py b/src/amd/TesterRepository.py new file mode 100644 index 0000000..b66ce09 --- /dev/null +++ b/src/amd/TesterRepository.py @@ -0,0 +1,128 @@ +from amd.Test import Test, TestData, GetTestsData, LogLocation, Quick +from amd.test_classifier import TestClassifier +from amd.AMD import AMDObject +from amd.config_processor import ConfigProcessor +from amd.match_fun_args_call import match_fun_args_call +import amd + +from typing import List, Union +import pkgutil +import traceback +import typing + + +class Tester(AMDObject): + def __init__(self): + AMDObject.__init__(self) + + def getTests(self, get_tests_data: GetTestsData = None) -> List[Test]: + test = Test() + test.tester = self + test.test_name = self.__class__.__name__ + return [test] + + def test(self, test_data: TestData): + pass + + def get_test_classifiers(self) -> Union[None, List[TestClassifier]]: + return None + + def clean(self): + pass + + +class TesterRepository(AMDObject): + def __init__(self): + AMDObject.__init__(self) + self.getTestersFrom = [amd] + self.testers: Union[None, List[Tester]] = None + + def getTesters(self) -> List[Tester]: + return self.testers + + def addTesterFrom(self, pkgs: List): + self.getTestersFrom.extend(pkgs) + + def clearTesterFrom(self): + self.getTestersFrom.clear() + + def addAllTesters(self): + if self.testers is None: + self.testers = list() + tester_children = get_cls_children_from_pkgs(cls=Tester, pkgs=self.getTestersFrom) + for tester_child in tester_children: + self.testers.append(tester_child()) + + def addTester(self, tester: Tester): + if self.testers is None: + self.testers = list() + self.testers.append(tester) + + +class GetTests(ConfigProcessor): + def __init__(self, tester_repository: TesterRepository): + ConfigProcessor.__init__(self) + self.tester_repository = tester_repository + + def get_tests(self, log_location=None, quick=None): + config = self.config + testers: List[Tester] = self.tester_repository.getTesters() + tests = list() + + for tester in testers: + get_tests_t = typing.get_type_hints(tester.getTests) + get_tests_data_t = None + if get_tests_t: + if "get_tests_data" in get_tests_t: + get_tests_data_t = get_tests_t["get_tests_data"] + + if get_tests_data_t is not None: + get_tests_data = get_tests_data_t() + else: + get_tests_data = None + + if isinstance(get_tests_data, LogLocation): + get_tests_data: LogLocation + get_tests_data.log_location = log_location + + if isinstance(get_tests_data, ConfigProcessor): + get_tests_data.config = config + get_tests_data.loadConfig() + + if isinstance(get_tests_data, Quick): + get_tests_data: Quick + get_tests_data.quick = quick + + try: + tests_of_tester = match_fun_args_call(fun=tester.getTests, args={"get_tests_data": get_tests_data}) + tests.extend(tests_of_tester) + except Exception as err: + print("{tester} failed to generate tests".format(tester=tester.__class__.__name__)) + traceback.print_exc() + continue + return tests + + +def get_cls_children_from_pkgs(cls, pkgs: List) -> set: + import_all_scripts_from(pkgs=pkgs) + cls_children = get_cls_children(cls=cls) + return cls_children + + +def get_cls_children(cls) -> set: + cls_children = set() + + for subclass in cls.__subclasses__(): + cls_children.add(subclass) + cls_children.union(get_cls_children(subclass)) + + return cls_children + + +def import_all_scripts_from(pkgs: List): + for pkg in pkgs: + for importer, modname, ispkg in pkgutil.iter_modules(pkg.__path__): + if ispkg: + import_all_scripts_from([importer.find_module(modname).load_module(modname)]) + else: + importer.find_module(modname).load_module(modname) diff --git a/src/amd/TestersExecutor.py b/src/amd/TestersExecutor.py new file mode 100644 index 0000000..949b49e --- /dev/null +++ b/src/amd/TestersExecutor.py @@ -0,0 +1,276 @@ +from amd.TesterRepository import TesterRepository, Tester, Test +from amd.test_selector import TestSelector +from amd.config_processor import ConfigProcessor +from amd.Test import TestResult + +import os +import traceback +import logging +import subprocess +import sys +import typing +from typing import Union, List, Dict +import json +import re +import time +import datetime + + +class TestersExecutor(ConfigProcessor): + def __init__(self): + ConfigProcessor.__init__(self) + + def executeTests(self, tester_repository: TesterRepository=None): + start_datetime = datetime.datetime.now() + config = self.config + log_location = config.log_location + if log_location is None: + log_location = os.getcwd() + os.makedirs(log_location, exist_ok=True) + root_log_dir = "report" + root_log_location = os.path.join(log_location, root_log_dir) + os.makedirs(root_log_location, exist_ok=True) + timestamped_log_location = os.path.join(root_log_location, start_datetime.strftime("%Y_%m_%d_%H_%M_%S")) + os.makedirs(timestamped_log_location, exist_ok=True) + relative_timestamped_log_location = os.path.join(root_log_dir, start_datetime.strftime("%Y_%m_%d_%H_%M_%S")) + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + ch = logging.StreamHandler() + ch.setLevel(logging.DEBUG) + ch.setFormatter(formatter) + fh = logging.FileHandler(filename=os.path.join(timestamped_log_location, "report.log"), mode='w+') + fh.setLevel(logging.DEBUG) + fh.setFormatter(formatter) + logger.addHandler(ch) + logger.addHandler(fh) + + selected_test_filter = config.run_tests + if selected_test_filter: + logger.info("Selected Test Filter: {selected_test_filter}".format(selected_test_filter=" ".join(selected_test_filter))) + logger.info("Execution Logs: {log_location}".format(log_location=log_location)) + + if tester_repository is None: + tester_repository: TesterRepository = TesterRepository() + tester_repository.addAllTesters() + + test_selector: TestSelector = TestSelector(tester_repository=tester_repository) + test_selector.config = config + tests: List[Test] = test_selector.select_tests(log_location=timestamped_log_location) + tests: List[Test] = sorted(tests, key=lambda x: x.test_name) + tests_status = dict() + tests_logs = dict() + tests_relative_logs = dict() + + for test in tests: + print("Started Test: {test_name}".format(test_name=test.test_name.lower())) + + test_t = typing.get_type_hints(test.tester.test) + test_data_t = None + if test_t: + test_data_t = test_t["test_data"] + + test_data = test_data_t() + + if isinstance(test_data, ConfigProcessor): + test_data: ConfigProcessor + test_data.config = config + test_data.loadConfig() + test_data.test = test + test_data.log_location = os.path.join(timestamped_log_location, test.test_name.lower()) + os.makedirs(test_data.log_location, exist_ok=True) + + try: + test.tester.test(test_data=test_data) + except Exception as error: + test_data.test_result = TestResult.ERROR + traceback.print_exc() + + tests_status[test] = test_data.test_result + tests_logs[test] = test_data.log_location + tests_relative_logs[test] = os.path.join(relative_timestamped_log_location, test.test_name.lower()) + print("Completed Test: {test_name} with result {result}".format(test_name=test.test_name.lower(), result=test_data.test_result.name)) + + for test in tests: + try: + test.tester.clean() + except Exception as error: + traceback.print_exc() + + end_datetime = datetime.datetime.now() + + # ### Reporting + try: + opt_rocm_version: Union[None, str] = get_opt_rocm_version() + except Exception as error: + opt_rocm_version = None + try: + os_name: Union[None, str] = get_os_name() + except Exception as error: + os_name = None + try: + os_version: Union[None, str] = get_os_version() + except Exception as error: + os_version = None + try: + rocm_agents: Union[None, List[str]] = get_rocm_agents() + except Exception as error: + rocm_agents = None + + passed_tests = get_passed_tests(tests_status=tests_status) + failed_tests = get_failed_tests(tests_status=tests_status) + errored_tests = get_errored_tests(tests_status=tests_status) + skipped_tests = get_skipped_tests(tests_status=tests_status) + + logger.info("Start Time: {start_datetime}".format(start_datetime=start_datetime.strftime("%Y/%m/%d %H:%M:%S"))) + logger.info("End Time: {end_datetime}".format(end_datetime=end_datetime.strftime("%Y/%m/%d %H:%M:%S"))) + # ### prettytable + field_names = ["Test Name", "Result", "Log"] + system_info_field_names = ["Component", "Information"] + test_cnt_field_names = ["PASS", "FAIL", "ERROR", "SKIP"] + try: + from prettytable import PrettyTable + if tests_status: + summary_table = PrettyTable() + summary_table.field_names = field_names + test_cnt_table = PrettyTable() + test_cnt_table.field_names = test_cnt_field_names + for test, test_status in passed_tests.items(): + summary_table.add_row([test.test_name.lower(), test_status.name, tests_relative_logs[test]]) + for test, test_status in failed_tests.items(): + summary_table.add_row([test.test_name.lower(), test_status.name, tests_relative_logs[test]]) + for test, test_status in errored_tests.items(): + summary_table.add_row([test.test_name.lower(), test_status.name, tests_relative_logs[test]]) + for test, test_status in skipped_tests.items(): + summary_table.add_row([test.test_name.lower(), test_status.name, tests_relative_logs[test]]) + + logger.info('\n' + summary_table.get_string(title="Summary")) + + test_cnt_table.add_row([str(len(passed_tests)), str(len(failed_tests)), str(len(errored_tests)), str(len(skipped_tests))]) + logger.info('\n' + test_cnt_table.get_string(title="Metrics")) + + system_info_table = PrettyTable() + system_info_table.field_names = system_info_field_names + system_info_table.add_row(["OS", (os_name if os_name else "Can't get OS name!") + " " + (os_version if os_version else "Can't get OS version")]) + system_info_table.add_row(["ROCm Agents", ", ".join(rocm_agents) if rocm_agents else "Can't get ROCm agents"]) + system_info_table.add_row(["/opt/rocm version", opt_rocm_version if opt_rocm_version else "Can't get /opt/rocm version"]) + logger.info('\n' + system_info_table.get_string(title="System Information")) + except Exception as error: + logger.warning("For better UI experience, please pip3 install -r requirements.txt") + + if tests_status: + logger.info("********Summary********") + logger.info(" | ".join(field_names)) + for test, test_status in passed_tests.items(): + logger.info(test.test_name.lower() + " | " + test_status.name + " | " + tests_relative_logs[test]) + for test, test_status in failed_tests.items(): + logger.info(test.test_name.lower() + " | " + test_status.name + " | " + tests_relative_logs[test]) + for test, test_status in errored_tests.items(): + logger.info(test.test_name.lower() + " | " + test_status.name + " | " + tests_relative_logs[test]) + for test, test_status in skipped_tests.items(): + logger.info(test.test_name.lower() + " | " + test_status.name + " | " + tests_relative_logs[test]) + + logger.info("********Metrics********") + logger.info(" | ".join(test_cnt_field_names)) + logger.info(str(len(passed_tests)) + " | " + str(len(failed_tests)) + " | " + str(len(errored_tests)) + " | " + str(len(skipped_tests))) + + logger.info("********System Information********") + logger.info(" | ".join(system_info_field_names)) + logger.info("OS" + " | " + (os_name if os_name else "Can't get OS name!") + " " + (os_version if os_version else "Can't get OS version")) + logger.info("ROCm Agents" + " | " + ", ".join(rocm_agents) if rocm_agents else "Can't get ROCm agents") + logger.info("/opt/rocm version" + " | " + opt_rocm_version if opt_rocm_version else "Can't get /opt/rocm version") + + logger.info("Note, All log locations are relative to {log_location}".format(log_location=log_location)) + + # ### json + json_root = dict() + tests_root = json_root["tests"] = dict() + for test, test_status in tests_status.items(): + test_root = tests_root[test.test_name.lower()] = dict() + test_root["status"] = test_status.name + test_root["log_location"] = tests_logs[test] + + json_root["num_passed"] = len(passed_tests) + json_root["num_failed"] = len(failed_tests) + json_root["num_errored"] = len(errored_tests) + json_root["num_skipped"] = len(skipped_tests) + + json_root["opt_rocm_version"] = opt_rocm_version + json_root["os_name"] = os_name + json_root["os_version"] = os_version + json_root["rocm_agents"] = rocm_agents + json_root["start_datetime"] = start_datetime.strftime("%Y_%m_%d_%H_%M_%S") + json_root["end_datetime"] = end_datetime.strftime("%Y_%m_%d_%H_%M_%S") + json_root["selected_test_filter"] = selected_test_filter + + with open(os.path.join(timestamped_log_location, 'report.json'), 'w+', encoding='utf-8') as f: + json.dump(json_root, f, ensure_ascii=False, indent=4) + + +def get_os_name() -> str: + cmd = "awk -F= '/^NAME=/{print $2}' /etc/os-release" + o, e = subprocess.Popen([cmd], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True).communicate() + o = o.decode('utf-8') + os_version = o.strip('\n') + os_version = os_version.replace('"', '') + return os_version + + +def get_os_version() -> str: + cmd = "awk -F= '/^VERSION_ID=/{print $2}' /etc/os-release" + o, e = subprocess.Popen([cmd], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True).communicate() + o = o.decode('utf-8') + os_version = o.strip('\n') + os_version = os_version.replace('"', '') + return os_version + + +class NoROCmAgentsFound(Exception): + def __init__(self): + Exception.__init__(self) + + +def get_rocm_agents() -> List[str]: + gpus = list() + cmd = "/opt/rocm/bin/rocminfo" + o, e = subprocess.Popen([cmd], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True).communicate() + o = o.decode('utf-8') + for l in o.splitlines(): + gpus_match = re.findall("^ Name:\s+(.+)", l) + if gpus_match: + gpus.append(gpus_match[0].strip()) + if not gpus: + raise NoROCmAgentsFound() + return gpus + + +def get_opt_rocm_version() -> str: + cmd = "cat /opt/rocm/.info/version" + o, e = subprocess.Popen([cmd], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True).communicate() + o = o.decode('utf-8') + opt_rocm_version = o.strip('\n') + return opt_rocm_version + + +def get_passed_tests(tests_status: Dict[Test, TestResult]) -> Dict[Test, TestResult]: + return get_status_filtered_tests(tests_status=tests_status, status=TestResult.PASS) + + +def get_failed_tests(tests_status: Dict[Test, TestResult]) -> Dict[Test, TestResult]: + return get_status_filtered_tests(tests_status=tests_status, status=TestResult.FAIL) + + +def get_errored_tests(tests_status: Dict[Test, TestResult]) -> Dict[Test, TestResult]: + return get_status_filtered_tests(tests_status=tests_status, status=TestResult.ERROR) + + +def get_skipped_tests(tests_status: Dict[Test, TestResult]) -> Dict[Test, TestResult]: + return get_status_filtered_tests(tests_status=tests_status, status=TestResult.SKIP) + + +def get_status_filtered_tests(tests_status: Dict[Test, TestResult], status: TestResult) -> Dict[Test, TestResult]: + filtered_tests = dict() + for test, test_status in tests_status.items(): + if test_status == status: + filtered_tests[test] = test_status + return filtered_tests diff --git a/src/amd/__init__.py b/src/amd/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/applications/__init__.py b/src/amd/applications/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/applications/application_test_classifier.py b/src/amd/applications/application_test_classifier.py new file mode 100644 index 0000000..a149f4c --- /dev/null +++ b/src/amd/applications/application_test_classifier.py @@ -0,0 +1,11 @@ +from amd.test_classifier import TestClassifier + +from typing import Union + + +class APPLICATIONS(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"applications": matched_with_names}) diff --git a/src/amd/applications/hip_examples/__init__.py b/src/amd/applications/hip_examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/applications/hip_examples/hip_examples.py b/src/amd/applications/hip_examples/hip_examples.py new file mode 100644 index 0000000..e334373 --- /dev/null +++ b/src/amd/applications/hip_examples/hip_examples.py @@ -0,0 +1,2184 @@ +from amd.TesterRepository import Tester, Test, TestData +from amd.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from amd.test_classifier import TestClassifier +from amd.applications.hip_examples.hip_examples_build import BuildRunAmd, BuildRunNvidia +from amd.common.hip_get_packages import HipPackages +from amd.common.hip_shell import * + +import os + +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, path, cwd): + self.cwdAbs = cwd + self.appPath = os.path.join(self.cwdAbs,\ + "src/amd/applications/hip_examples/") + self.examplepath = os.path.join(self.appPath, "HIP-Examples/") + self.thistestpath = os.path.join(self.examplepath, path) + self.prepareobj = None + self.apprepo = "" # Default + self.appbranch = "" + self.appcommitId = "" + self.hiprepo = "" # Default + self.hipbranch = "" + self.hipcommitId = "" + self.gpustrm_repo = "" + self.gpustrm_branch = "" + self.gpustrm_commitId = "" + self.mixbn_repo = "" + self.mixbn_branch = "" + self.mixbn_commitId = "" + + def set_hip_repoinfo(self, test_data: HIPTestData): + if test_data.repos["hip"].repo_url != None: + self.hiprepo = test_data.repos["hip"].repo_url + else: + return False + if test_data.repos["hip"].branch != None: + self.hipbranch = test_data.repos["hip"].branch + if test_data.repos["hip"].commit_id != None: + self.hipcommitId = test_data.repos["hip"].commit_id + return True + + def set_hipex_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["hip_examples"].repo_url != None: + self.apprepo = test_data.repos["hip_examples"].repo_url + else: + validrepconfig &= False + if test_data.repos["hip_examples"].branch != None: + self.appbranch = test_data.repos["hip_examples"].branch + if test_data.repos["hip_examples"].commit_id != None: + self.appcommitId = test_data.repos["hip_examples"].commit_id + + validrepconfig &= self.set_hip_repoinfo(test_data) + return validrepconfig + + def set_mixben_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["mixbench"].repo_url != None: + self.mixbn_repo = test_data.repos["mixbench"].repo_url + else: + validrepconfig &= False + if test_data.repos["mixbench"].branch != None: + self.mixbn_branch = test_data.repos["mixbench"].branch + if test_data.repos["mixbench"].commit_id != None: + self.mixbn_commitId = test_data.repos["mixbench"].commit_id + + validrepconfig &= self.set_hip_repoinfo(test_data) + return validrepconfig + + def set_gpustr_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["gpu_stream"].repo_url != None: + self.gpustrm_repo = test_data.repos["gpu_stream"].repo_url + else: + validrepconfig &= False + if test_data.repos["gpu_stream"].branch != None: + self.gpustrm_branch = test_data.repos["gpu_stream"].branch + if test_data.repos["gpu_stream"].commit_id != None: + self.gpustrm_commitId = test_data.repos["gpu_stream"].commit_id + + validrepconfig &= self.set_hip_repoinfo(test_data) + return validrepconfig + + def download_deppackage(self, logFile): + return HipPackages().pull_repo(logFile, self.hiprepo, self.hipbranch,\ + self.hipcommitId, "HIP") + + def download_hipexample(self, logFile): + ret = self.download_deppackage(logFile) + ret &= HipPackages().pull_repo(logFile, self.apprepo,\ + self.appbranch, self.appcommitId, "hip_examples") + return ret + + def download_gpustream(self, logFile): + ret = self.download_deppackage(logFile) + ret &= HipPackages().pull_repo(logFile, self.gpustrm_repo,\ + self.gpustrm_branch, self.gpustrm_commitId, "gpu-stream") + return ret + + def download_mixbench(self, logFile): + ret = self.download_deppackage(logFile) + ret &= HipPackages().pull_repo(logFile, self.mixbn_repo,\ + self.mixbn_branch, self.mixbn_commitId, "mixbench") + return ret + + def buildtest(self, logFile, platform, testid): + if platform == HIP_PLATFORM.nvidia: + self.prepareobj = BuildRunNvidia(self.thistestpath) + elif platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.thistestpath) + else: + print("Invalid Platform") + return False + return self.prepareobj.buildtest(logFile, testid) + + def clean(self, testid): + self.prepareobj.clean(testid) + + def runtest(self, logFile, testid): + return self.prepareobj.runtest(logFile, testid) + + +class EXAMPLES(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"examples": matched_with_names}) + + +class MINIAPP(EXAMPLES): + def __init__(self): + EXAMPLES.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + EXAMPLES.add_matched_with_names(self, {"mini-app": matched_with_names}) + + +class STRESS(EXAMPLES): + def __init__(self): + EXAMPLES.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + EXAMPLES.add_matched_with_names(self, {"stress": matched_with_names}) + + +class PERFORMANCE(EXAMPLES): + def __init__(self): + EXAMPLES.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + EXAMPLES.add_matched_with_names(self, {"performance": matched_with_names}) + + +# Test vectorAdd/ +class VectorAdd(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "vectorAdd/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "vectorAdd") + + def test(self, test_data: HIPTestData): + print("=============== vectorAdd test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "vectorAdd" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test gpu-burn/ +class GpuBurn(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "gpu-burn/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = STRESS() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "gpu-burn") + + def test(self, test_data: HIPTestData): + print("=============== gpu-burn test ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "gpu-burn" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test strided-access/ +class StridedAccess(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "strided-access/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "strided-access") + + def test(self, test_data: HIPTestData): + print("=============== strided-access test ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "strided-access" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rtm8/ +class Rtm8(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rtm8/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rtm8") + + def test(self, test_data: HIPTestData): + print("=============== rtm8 test ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rtm8" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + +# Test reduction/ +class Reduction(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "reduction/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "reduction") + + def test(self, test_data: HIPTestData): + print("=============== reduction test ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "reduction" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test mini-nbody/ +class Mini_nbody(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "mini-nbody/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "mini-nbody") + + def test(self, test_data: HIPTestData): + print("=============== mini-nbody test ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "mini-nbody" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test add4/ +class Add4(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "add4/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "add4") + + def test(self, test_data: HIPTestData): + print("=============== add4 test ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "add4" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test cuda-stream/ +class Cuda_stream(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "cuda-stream/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "cuda-stream") + + def test(self, test_data: HIPTestData): + print("=============== cuda-stream test ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "cuda-stream" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test openmp-helloworld/ +# class Openmp_helloworld(Tester, PrepareTest): + # def __init__(self): + # Tester.__init__(self) + # self.cwd = os.getcwd() + # PrepareTest.__init__(self, "openmp-helloworld/", self.cwd) + + # def getTests(self) -> List[Test]: + # test = Test() + # test.test_name = self.__class__.__name__ + # intro = MINIAPP() + # intro.add_matched_with_names() + # test.classifiers = [intro] + # test.tester = self + # return [test] + + # def clean(self): + # PrepareTest.clean(self, "openmp-helloworld") + + # def test(self, test_data: HIPTestData): + # print("=============== openmp-helloworld test ===============") + # # Set repo info + # isrepocfgvalid = self.set_hipex_repoinfo(test_data) + # if not isrepocfgvalid: + # test_data.test_result = TestResult.ERROR + # return + # # Create the log directory + # resultLogDir = test_data.log_location + # testid = "openmp-helloworld" + # with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + # res = self.download_hipexample(testLogger) + # if not res: + # test_data.test_result = TestResult.FAIL + # return + # res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + # if not res: + # test_data.test_result = TestResult.FAIL + # return + + # if True == self.runtest(testLogger, testid): + # test_data.test_result = TestResult.PASS + # else: + # test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/bfs/ +class Bfs(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/bfs/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.bfs") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/bfs test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.bfs" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/cfd/ +class Cfd(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/cfd/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.cfd") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/cfd test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.cfd" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/dwt2d/ +class Dwt2d(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/dwt2d/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.dwt2d") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/dwt2d test start ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.dwt2d" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + +# Test rodinia_3.0/hip/gaussian/ +class Gaussian(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/gaussian/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.gaussian") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/gaussian test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.gaussian" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/heartwall/ +class Heartwall(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/heartwall/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.heartwall") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/heartwall test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.heartwall" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/hotspot/ +class Hotspot(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/hotspot/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.hotspot") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/hotspot test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.hotspot" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/hybridsort/ +class Hybridsort(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/hybridsort/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.hybridsort") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/hybridsort test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.hybridsort" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/kmeans/ +class Kmeans(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/kmeans/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.kmeans") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/kmeans test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.kmeans" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/lavaMD/ +class LavaMD(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/lavaMD/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.lavaMD") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/lavaMD test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.lavaMD" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/lud/ +class Lud(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/lud/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.lud") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/lud test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.lud" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/myocyte/ +class Myocyte(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/myocyte/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.myocyte") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/myocyte test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.myocyte" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/nn/ +class Nn(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/nn/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.nn") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/nn test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.nn" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/nw/ +class Nw(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/nw/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.nw") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/nw test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.nw" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/particlefilter/ +class Particlefilter(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/particlefilter/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.particlefilter") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/particlefilter test start ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.particlefilter" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + + +# Test rodinia_3.0/hip/pathfinder/ +class pathfinder(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/pathfinder/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.pathfinder") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/pathfinder test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.pathfinder" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/srad/ +class Srad(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/srad/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.srad") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/srad test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.srad" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/streamcluster/ +class Streamcluster(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/streamcluster/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.streamcluster") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/streamcluster test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.streamcluster" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/b+tree/ +# class Btree(Tester, PrepareTest): + # def __init__(self): + # Tester.__init__(self) + # self.cwd = os.getcwd() + # PrepareTest.__init__(self, "rodinia_3.0/hip/b+tree/", self.cwd) + + # def getTests(self) -> List[Test]: + # test = Test() + # test.test_name = self.__class__.__name__ + # intro = PERFORMANCE() + # intro.add_matched_with_names() + # test.classifiers = [intro] + # test.tester = self + # return [test] + + # def clean(self): + # PrepareTest.clean(self, "rodinia_3.b+tree") + + # def test(self, test_data: HIPTestData): + # print("=============== rodinia_3.0/hip/b+tree test start ===============") + # # Set repo info + # isrepocfgvalid = self.set_hipex_repoinfo(test_data) + # if not isrepocfgvalid: + # test_data.test_result = TestResult.ERROR + # return + # # Create the log directory + # resultLogDir = test_data.log_location + # testid = "rodinia_3.b+tree" + # with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + # res = self.download_hipexample(testLogger) + # if not res: + # test_data.test_result = TestResult.FAIL + # return + + # res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + # if not res: + # test_data.test_result = TestResult.FAIL + # return + + # if True == self.runtest(testLogger, testid): + # test_data.test_result = TestResult.PASS + # else: + # test_data.test_result = TestResult.FAIL + + +# Test rodinia_3.0/hip/backprop/ +class Backprop(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "rodinia_3.0/hip/backprop/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "rodinia_3.backprop") + + def test(self, test_data: HIPTestData): + print("=============== rodinia_3.0/hip/backprop test start ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "rodinia_3.backprop" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + +# Test HIP-Examples-Applications/BinomialOption/ +class BinomialOption(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/BinomialOption/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.BinomialOption") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/BinomialOption test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.BinomialOption" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/BitonicSort/ +class BitonicSort(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/BitonicSort/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.BitonicSort") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/BitonicSort test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.BitonicSort" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/dct/ +class Dct(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/dct/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.dct") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/dct test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.dct" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/dwtHaar1D/ +class DwtHaar1D(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/dwtHaar1D/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.dwtHaar1D") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/dwtHaar1D test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.dwtHaar1D" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/FastWalshTransform/ +class FastWalshTransform(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/FastWalshTransform/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.FastWalshTransform") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/FastWalshTransform test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.FastWalshTransform" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/FloydWarshall/ +class FloydWarshall(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/FloydWarshall/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.FloydWarshall") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/FloydWarshall test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.FloydWarshall" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/HelloWorld/ +class HelloWorld(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/HelloWorld/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.HelloWorld") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/HelloWorld test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.HelloWorld" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/Histogram/ +class Histogram(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/Histogram/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.Histogram") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/Histogram test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.Histogram" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/MatrixMultiplication/ +class MatrixMultiplication(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/MatrixMultiplication/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.MatrixMultiplication") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/MatrixMultiplication test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.MatrixMultiplication" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/PrefixSum/ +class PrefixSum(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/PrefixSum/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.PrefixSum") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/PrefixSum test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.PrefixSum" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/RecursiveGaussian/ +class RecursiveGaussian(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/RecursiveGaussian/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.RecursiveGaussian") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/RecursiveGaussian test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.RecursiveGaussian" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test HIP-Examples-Applications/SimpleConvolution/ +class SimpleConvolution(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "HIP-Examples-Applications/SimpleConvolution/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = MINIAPP() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "HIP-Examples-Applications.SimpleConvolution") + + def test(self, test_data: HIPTestData): + print("=============== HIP-Examples-Applications/SimpleConvolution test start ===============") + # Set repo info + isrepocfgvalid = self.set_hipex_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "HIP-Examples-Applications.SimpleConvolution" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_hipexample(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test GPU-STREAM Double +class GpuStreamDouble(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "../GPU-STREAM/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "GPU-STREAM-DOUBLE") + + def test(self, test_data: HIPTestData): + print("=============== GPU-STREAM Double start ===============") + # Set repo info + isrepocfgvalid = self.set_gpustr_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "GPU-STREAM-DOUBLE" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_gpustream(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test GPU-STREAM Float +# class GpuStreamFloat(Tester, PrepareTest): + # def __init__(self): + # Tester.__init__(self) + # self.cwd = os.getcwd() + # PrepareTest.__init__(self, "../GPU-STREAM/", self.cwd) + + # def getTests(self) -> List[Test]: + # test = Test() + # test.test_name = self.__class__.__name__ + # intro = PERFORMANCE() + # intro.add_matched_with_names() + # test.classifiers = [intro] + # test.tester = self + # return [test] + + # def clean(self): + # PrepareTest.clean(self, "GPU-STREAM-FLOAT") + + # def test(self, test_data: HIPTestData): + # print("=============== GPU-STREAM Float start ===============") + # # Set repo info + # isrepocfgvalid = self.set_gpustr_repoinfo(test_data) + # if not isrepocfgvalid: + # test_data.test_result = TestResult.ERROR + # return + # # Create the log directory + # resultLogDir = test_data.log_location + # testid = "GPU-STREAM-FLOAT" + # with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + # res = self.download_gpustream(testLogger) + # if not res: + # test_data.test_result = TestResult.FAIL + # return + + # res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + # if not res: + # test_data.test_result = TestResult.FAIL + # return + + # if True == self.runtest(testLogger, testid): + # test_data.test_result = TestResult.PASS + # else: + # test_data.test_result = TestResult.FAIL + + +# Test mixbench-hip-alt +class MixBenchAlt(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "../mixbench/mixbench-hip/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "mixbench-hip-alt") + + def test(self, test_data: HIPTestData): + print("=============== mixbench-hip-alt start ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + # Set repo info + isrepocfgvalid = self.set_mixben_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "mixbench-hip-alt" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_mixbench(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + +# Test mixbench-hip-ro +class MixBenchRO(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "../mixbench/mixbench-hip/", self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = PERFORMANCE() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self, "mixbench-hip-ro") + + def test(self, test_data: HIPTestData): + print("=============== GPU-STREAM Float start ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + # Set repo info + isrepocfgvalid = self.set_mixben_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + testid = "mixbench-hip-ro" + with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: + res = self.download_mixbench(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) + if not res: + test_data.test_result = TestResult.FAIL + return + + if True == self.runtest(testLogger, testid): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + + diff --git a/src/amd/applications/hip_examples/hip_examples_build.py b/src/amd/applications/hip_examples/hip_examples_build.py new file mode 100644 index 0000000..52aadce --- /dev/null +++ b/src/amd/applications/hip_examples/hip_examples_build.py @@ -0,0 +1,353 @@ +import os +import tempfile + +from amd.applications.hip_examples.hip_examples_parser import Hip_examples_parser +from amd.common.hip_shell import * + +class BuildRunCommon(): + ''' + In this class insert the build and execution steps for test cases + which are identical across different platforms (amd/nvidia/intel). + ''' + def __init__(self, path): + self.thistestpath = path + self.runlogdump = tempfile.TemporaryFile("w+") + self.runlog = "" + self.genbinaryname = None + self.binarydic = {"vectorAdd":["vectoradd_hip.exe"],\ + "gpu-burn":["/build/gpuburn-hip"],\ + "strided-access":["strided-access"],\ + "rtm8":["rtm8_hip"],\ + "reduction":["reduction"],\ + "mini-nbody":["/hip/nbody-block", "/hip/nbody-orig", "/hip/nbody-soa"],\ + "add4":["gpu-stream-hip"],\ + "cuda-stream":["stream"],\ + "openmp-helloworld":["openmp_helloworld.exe"],\ + "rodinia_3.bfs":["bfs"],\ + "rodinia_3.cfd":["euler3d"],\ + "rodinia_3.dwt2d":["dwt2d"],\ + "rodinia_3.gaussian":["gaussian"],\ + "rodinia_3.heartwall":["heartwall"],\ + "rodinia_3.hotspot":["hotspot"],\ + "rodinia_3.hybridsort":["hybridsort"],\ + "rodinia_3.kmeans":["kmeans"],\ + "rodinia_3.lavaMD":["lavaMD"],\ + "rodinia_3.lud":["lud"],\ + "rodinia_3.myocyte":["myocyte"],\ + "rodinia_3.nn":["nn"],\ + "rodinia_3.nw":["nw"],\ + "rodinia_3.particlefilter":["particlefilter_naive","particlefilter_float"],\ + "rodinia_3.pathfinder":["pathfinder"],\ + "rodinia_3.srad":["/srad_v1/srad"],\ + "rodinia_3.streamcluster":["streamcluster"],\ + "rodinia_3.b+tree":["b+tree"],\ + "rodinia_3.backprop":["backprop"],\ + "HIP-Examples-Applications.BinomialOption":["BinomialOption"],\ + "HIP-Examples-Applications.BitonicSort":["BitonicSort"],\ + "HIP-Examples-Applications.dct":["dct"],\ + "HIP-Examples-Applications.dwtHaar1D":["dwtHaar1D"],\ + "HIP-Examples-Applications.FastWalshTransform":["FastWalshTransform"],\ + "HIP-Examples-Applications.FloydWarshall":["FloydWarshall"],\ + "HIP-Examples-Applications.HelloWorld":["HelloWorld"],\ + "HIP-Examples-Applications.Histogram":["Histogram"],\ + "HIP-Examples-Applications.MatrixMultiplication":["MatrixMultiplication"],\ + "HIP-Examples-Applications.PrefixSum":["PrefixSum"],\ + "HIP-Examples-Applications.RecursiveGaussian":["RecursiveGaussian"],\ + "HIP-Examples-Applications.SimpleConvolution":["SimpleConvolution"],\ + "GPU-STREAM-DOUBLE":["hip-stream"],\ + "GPU-STREAM-FLOAT":["hip-stream"],\ + "mixbench-hip-alt":["mixbench-hip-alt", "mixbench-hip-ro"],\ + "mixbench-hip-ro":["mixbench-hip-alt", "mixbench-hip-ro"] + } + + def buildtest(self, logFile, testid, env = None): + # Prepare the shell command to execute + cmdcd = "cd "+self.thistestpath+";" + cmd_build = "" + if testid == "vectorAdd" or\ + testid == "gpu-burn" or\ + testid == "strided-access" or\ + testid == "cuda-stream" or\ + testid == "rodinia_3.bfs" or\ + testid == "rodinia_3.cfd" or\ + testid == "rodinia_3.dwt2d" or\ + testid == "rodinia_3.gaussian" or\ + testid == "rodinia_3.heartwall" or\ + testid == "rodinia_3.hotspot" or\ + testid == "rodinia_3.hybridsort" or\ + testid == "rodinia_3.kmeans" or\ + testid == "rodinia_3.lavaMD" or\ + testid == "rodinia_3.lud" or\ + testid == "rodinia_3.myocyte" or\ + testid == "rodinia_3.nn" or\ + testid == "rodinia_3.nw" or\ + testid == "rodinia_3.particlefilter" or\ + testid == "rodinia_3.pathfinder" or\ + testid == "rodinia_3.srad" or\ + testid == "rodinia_3.streamcluster" or\ + testid == "rodinia_3.b+tree" or\ + testid == "rodinia_3.backprop" or\ + testid == "HIP-Examples-Applications.BinomialOption" or\ + testid == "HIP-Examples-Applications.BitonicSort" or\ + testid == "HIP-Examples-Applications.dct" or\ + testid == "HIP-Examples-Applications.dwtHaar1D" or\ + testid == "HIP-Examples-Applications.FastWalshTransform" or\ + testid == "HIP-Examples-Applications.FloydWarshall" or\ + testid == "HIP-Examples-Applications.HelloWorld" or\ + testid == "HIP-Examples-Applications.Histogram" or\ + testid == "HIP-Examples-Applications.MatrixMultiplication" or\ + testid == "HIP-Examples-Applications.PrefixSum" or\ + testid == "HIP-Examples-Applications.RecursiveGaussian" or\ + testid == "HIP-Examples-Applications.SimpleConvolution": + cmd_build = "make clean;make;" + elif testid == "rtm8": + cmd_build = "./build_hip.sh;" + elif testid == "reduction": + cmd_build = "make clean;make;" + elif testid == "mini-nbody": + cmd_build = "cd hip;\ + bash ./HIP-nbody-block.sh;\ + bash ./HIP-nbody-orig.sh;\ + bash ./HIP-nbody-soa.sh;" + elif testid == "add4": + cmd_build = "make clean;./buildit.sh;" + elif testid == "openmp-helloworld": + cmd_build = "cd openmp-helloworld;make clean;make;" + elif testid == "GPU-STREAM-DOUBLE" or testid == "GPU-STREAM-FLOAT": + cmd_build = "make -f HIP.make clean;make -f HIP.make;" + elif testid == "mixbench-hip-alt" or testid == "mixbench-hip-ro": + cmd_build = "make clean;rm -Rf Makefile;cmake .;make;" + + cmdexc = cmdcd + cmd_build + # Execute the command on shell + if "HIP-Examples-Applications" in testid: + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + else: + self.runlog = execshellcmd(cmdexc, logFile, env) + # Check if the test binary/ies is/are generated + for binary in self.binarydic[testid]: + if not os.path.isfile(self.thistestpath + binary): + return False + + return True + + def runtest(self, logFile, testid, env = None): + res = True + if testid == "vectorAdd": + # Test already executed during make + # Run Parser + ret = Hip_examples_parser().vectorAdd(self.runlog) + if ret == "Failed": + res &= False + elif testid == "gpu-burn": + cmdexc = "cd " + self.thistestpath + ";" + "." +\ + self.binarydic[testid][0] + " -t 5" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().gpu_burn(self.runlog) + if ret == "Failed": + res &= False + elif testid == "strided-access": + cmdexc = "cd " + self.thistestpath + ";" + "./" +\ + self.binarydic[testid][0] + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().strided_access(self.runlog) + if ret == "Failed": + res &= False + elif testid == "rtm8": + cmdexc = "cd " + self.thistestpath + ";" + "./" +\ + self.binarydic[testid][0] + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().rtm8(self.runlog) + if ret == "Failed": + res &= False + elif testid == "reduction": + cmdexc = "cd " + self.thistestpath + ";" +\ + "bash ./run.sh" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().reduction(self.runlog) + if ret == "Failed": + res &= False + elif testid == "mini-nbody": + # Test already executed during make + # Run Parser + ret = Hip_examples_parser().mini_nbody(self.runlog) + if ret == "Failed": + res &= False + elif testid == "add4": + cmdexc = "cd " + self.thistestpath + ";" +\ + "./runhip.sh" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().add4(self.runlog) + if ret == "Failed": + res &= False + elif testid == "cuda-stream": + cmdexc = "cd " + self.thistestpath + ";" + "./" +\ + self.binarydic[testid][0] + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().cuda_stream(self.runlog) + if ret == "Failed": + res &= False + elif testid == "openmp-helloworld": + # Test already executed during make + # Run Parser + ret = Hip_examples_parser().openmp_helloworld(self.runlog) + if ret == "Failed": + res &= False + elif testid == "rodinia_3.bfs" or testid == "rodinia_3.cfd" or \ + testid == "rodinia_3.dwt2d" or testid == "rodinia_3.particlefilter": + cmdexc = "cd " + self.thistestpath + ";" + "make test;" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().rodina3(self.runlog, "PASSED", 2) + if ret == "Failed": + res &= False + elif testid == "rodinia_3.gaussian" or testid == "rodinia_3.lavaMD": + cmdexc = "cd " + self.thistestpath + ";" + "make test;" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().rodina3(self.runlog, "PASSED", 5) + if ret == "Failed": + res &= False + elif testid == "rodinia_3.heartwall" or testid == "rodinia_3.hotspot"\ + or testid == "rodinia_3.hybridsort" or testid == "rodinia_3.lud"\ + or testid == "rodinia_3.myocyte" or testid == "rodinia_3.nn"\ + or testid == "rodinia_3.nn" or testid == "rodinia_3.pathfinder"\ + or testid == "rodinia_3.srad" or testid == "rodinia_3.streamcluster"\ + or testid == "rodinia_3.b+tree" or testid == "rodinia_3.backprop": + cmdexc = "cd " + self.thistestpath + ";" + "make test;" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().rodina3(self.runlog, "PASSED", 1) + if ret == "Failed": + res &= False + elif testid == "rodinia_3.kmeans": + cmdexc = "cd " + self.thistestpath + ";" + "make test;" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().rodina3(self.runlog, "PASSED", 4) + if ret == "Failed": + res &= False + elif testid == "HIP-Examples-Applications.BinomialOption" or\ + testid == "HIP-Examples-Applications.BitonicSort" or\ + testid == "HIP-Examples-Applications.dct" or\ + testid == "HIP-Examples-Applications.dwtHaar1D" or\ + testid == "HIP-Examples-Applications.FastWalshTransform" or\ + testid == "HIP-Examples-Applications.FloydWarshall" or\ + testid == "HIP-Examples-Applications.HelloWorld" or\ + testid == "HIP-Examples-Applications.Histogram" or\ + testid == "HIP-Examples-Applications.MatrixMultiplication" or\ + testid == "HIP-Examples-Applications.PrefixSum" or\ + testid == "HIP-Examples-Applications.RecursiveGaussian" or\ + testid == "HIP-Examples-Applications.SimpleConvolution": + # Test already executed during make + # Run Parser + ret = Hip_examples_parser().hip_examples_applications(self.runlogdump) + if ret == "Failed": + res &= False + elif testid == "GPU-STREAM-DOUBLE": + cmdexc = "cd " + self.thistestpath + ";" +\ + "./hip-stream" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().gpu_stream(self.runlog) + if ret == "Failed": + res &= False + elif testid == "GPU-STREAM-FLOAT": + cmdexc = "cd " + self.thistestpath + ";" +\ + "./hip-stream --float" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().gpu_stream(self.runlog) + if ret == "Failed": + res &= False + elif testid == "mixbench-hip-alt": + cmdexc = "cd " + self.thistestpath + ";" +\ + "./mixbench-hip-alt" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().mix_bench(self.runlog) + if ret == "Failed": + res &= False + elif testid == "mixbench-hip-ro": + cmdexc = "cd " + self.thistestpath + ";" +\ + "./mixbench-hip-ro" + self.runlog = execshellcmd(cmdexc, logFile, env) + ret = Hip_examples_parser().mix_bench(self.runlog) + if ret == "Failed": + res &= False + + return res + + def clean(self, testid): + for binary in self.binarydic[testid]: + if os.path.exists(self.thistestpath + binary): + os.remove(self.thistestpath + binary) + +class BuildRunAmd(BuildRunCommon): + def __init__(self, path): + BuildRunCommon.__init__(self, path) + + def buildtest(self, logFile, testid): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + ret = BuildRunCommon.buildtest(self, logFile, testid) + return ret + + def runtest(self, logFile, testid): + # In this function put the execution steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + ret = BuildRunCommon.runtest(self, logFile, testid) + return ret + + # Clean all generated binaries + def clean(self, testid): + BuildRunCommon.clean(self, testid) + + +class BuildRunNvidia(BuildRunCommon): + def __init__(self, path): + self.hippath = os.path.join(os.getcwd(),"src/amd/conformance/HIP/") + BuildRunCommon.__init__(self, path) + + def getenvironmentvariables(self): + envtoset = os.environ.copy() + envtoset["HIP_PLATFORM"] = "nvidia" + envtoset["HIP_COMPILER"] = "nvcc" + envtoset["HIP_RUNTIME"] = "cuda" + envtoset["HIP_DIR"] = self.hippath + envtoset["HIP_PATH"] = os.path.join(self.hippath, "build") + return envtoset + + def setupenvironmentfornvcc(self, logFile): + cmdcd = "cd "+self.hippath+";" + envtoset = self.getenvironmentvariables() + cmdsetenv = "rm -Rf build/;mkdir build;cd build;" + cmdbuildinstall = "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install ..;" + cmdbuildinstall += "make install;" + cmdexc = cmdcd + cmdsetenv + cmdbuildinstall + execshellcmd(cmdexc, logFile, envtoset) + + def buildtest(self, logFile, testid): + buildbindirpresent = os.path.isdir(\ + os.path.join(self.hippath, "build/bin")) + buildincludedirpresent = os.path.isdir(\ + os.path.join(self.hippath, "build/include")) + buildinstalldirpresent = os.path.isdir(\ + os.path.join(self.hippath, "build/install")) + if not (buildbindirpresent & buildincludedirpresent & buildinstalldirpresent): + self.setupenvironmentfornvcc(logFile) + env = self.getenvironmentvariables() + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + ret = BuildRunCommon.buildtest(self, logFile, testid, env) + return ret + + def runtest(self, logFile, testid): + res = True + env = self.getenvironmentvariables() + # In this function put the execution steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + ret = BuildRunCommon.runtest(self, logFile, testid, env) + return ret + + # Clean all generated binaries + def clean(self, testid): + BuildRunCommon.clean(self, testid) + diff --git a/src/amd/applications/hip_examples/hip_examples_parser.py b/src/amd/applications/hip_examples/hip_examples_parser.py new file mode 100644 index 0000000..7e2af99 --- /dev/null +++ b/src/amd/applications/hip_examples/hip_examples_parser.py @@ -0,0 +1,247 @@ +import re + +class Hip_examples_parser: + def __init__(self): + pass + + def rodina3(self, text, pass_string, num): + if num == text.count(pass_string): + return "Passed" + return "Failed" + + def openmp_helloworld(self, text): + status = 'Failed' + text = text.splitlines() + for line in text: + if 'PASSED!' in line: + status = 'Passed' + return status + + def vectorAdd(self, text): + status = 'Failed' + text = text.splitlines() + for line in text: + if 'PASSED!' in line: + status = 'Passed' + return status + + def reduction(self, text): + status = 'Failed' + count = 0 + + text = text.splitlines() + try: + + for line in text: + if 'result is CORRECT' in line: + count = count + 1 + if count >= 8: + status = 'Passed' + except: + pass + return status + + def rtm8(self, text): + status = 'Failed' + count = 0 + text = text.splitlines() + try: + for line in text: + if "memory" in line: + count = count + 1 + if "pts" in line: + count = count + 1 + if "Tflops" in line: + count = count + 1 + if "dt" in line: + count = count + 1 + if "pt_rate" in line: + count = count + 1 + if "flop_rate" in line: + count = count + 1 + if "speedup" in line: + count = count + 1 + if count >= 7: + status = 'Passed' + except: + pass + return status + + def add4(self, text): + status = 'Failed' + copy_count = 0 + mul_count = 0 + add_count = 0 + triad_count = 0 + geomean_count = 0 + copy_re = re.compile('Copy\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+') + mul_re = re.compile('Mul\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+') + add_re = re.compile('Add4\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+') + triad_re = re.compile('Triad\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+') + geomean_re = re.compile('GEOMEAN\s+\d+\.\d+') + lines = text.splitlines() + try: + + for line in lines: + copy = re.search(copy_re, line) + mul = re.search(mul_re, line) + add = re.search(add_re, line) + triad = re.search(triad_re, line) + geomean = re.search(geomean_re, line) + if copy: + copy_count = copy_count + 1 + if mul: + mul_count = mul_count + 1 + if add: + add_count = add_count + 1 + if triad: + triad_count = triad_count + 1 + if geomean: + geomean_count = geomean_count + 1 + if copy_count >= 4 and mul_count >= 4 and add_count >= 4 and triad_count >= 4 and geomean_count >= 4: + status = 'Passed' + except: + pass + return status + + def gpu_burn(self, text): + status = 'Failed' + copy_count = 0 + mul_count = 0 + add_count = 0 + triad_count = 0 + geomean_count = 0 + lines = text.splitlines() + gpu_re = re.compile('Total no. of GPUs found:\s+(\d)') + gpus = 0 + for line in lines: + copy = re.search(gpu_re, line) + if copy: + gpus = int(copy.group(1)) + if 'Init Burn Thread for device' in line: + copy_count = copy_count + 1 + if 'Burn Thread using device' in line: + mul_count = mul_count + 1 + if 'Temps:' in line: + add_count = add_count + 1 + if 'Stopping burn thread on device' in line: + triad_count = triad_count + 1 + if copy_count >= gpus and mul_count >= gpus and add_count >= 5 and triad_count >= gpus and gpus >= 1: + status = 'Passed' + return status + + def cuda_stream(self, text): + status = 'Failed' + copy_count = 0 + mul_count = 0 + add_count = 0 + triad_count = 0 + lines = text.splitlines() + copy_re = re.compile(r'Copy:\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+') + mul_re = re.compile(r'Scale:\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+') + add_re = re.compile(r'Add:\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+') + triad_re = re.compile(r'Triad:\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+') + try: + for line in lines: + copy = re.search(copy_re, line) + mul = re.search(mul_re, line) + add = re.search(add_re, line) + triad = re.search(triad_re, line) + + if copy: + copy_count = copy_count + 1 + if mul: + mul_count = mul_count + 1 + if add: + add_count = add_count + 1 + if triad: + triad_count = triad_count + 1 + if copy_count >= 1 and mul_count >= 1 and add_count >= 1 and triad_count >= 1: + status = 'Passed' + except: + + print('exception') + pass + return status + + def mini_nbody(self, text): + hip_minibody_count = 0 + status = 'Failed' + count = 0 + lines = text.splitlines() + try: + for line in lines: + search = re.search('\d+\,\s+\d+\.\d+', line) + if search: + hip_minibody_count = hip_minibody_count + 1 + + if hip_minibody_count >= 26: + status = 'Passed' + except: + pass + return status + + def strided_access(self, text): + status = 'Failed' + count = 0 + try: + lines = text.splitlines() + for line in lines: + search = re.search('\d+\s+\d+\.\d+\s+\d+\.\d+', line) + if search: + count = count + 1 + if count >= 30: + status = 'Passed' + except: + pass + return status + + def gpu_stream(self, text): + testsuccess = True + test_status = 'Failed' + count = 0 + search = re.search('Copy\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d', text) + if search: + count = count + 1 + search = re.search('Mul\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d', text) + if search: + count = count + 1 + search = re.search('Add\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d', text) + if search: + count = count + 1 + search = re.search('Triad\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d', text) + if search: + count = count + 1 + search = re.search('Dot\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d', text) + if search: + count = count + 1 + + search = re.search("Validation failed", text); + if search: + testsuccess &= False + + if (count >= 5) and (testsuccess != False): + test_status = 'Passed' + else: + test_status = 'Failed' + + return test_status + + def mix_bench(self, text): + test_status = 'Failed' + count = len(re.findall(' +\d+, *\d+\.\d+| +\d+, +inf', text)) + if (count >= 33): + test_status = 'Passed' + return test_status + + def hip_examples_applications(self, logfile): + pass_pattern1 = 'fault' + pass_pattern2 = 'Aborted' + pass_pattern3 = 'Error' + pass_pattern4 = 'failed' + logfile.seek(0) + for line in logfile: + if (pass_pattern1 in line) or (pass_pattern2 in line)\ + or (pass_pattern3 in line) or (pass_pattern4 in line): + return "Failed" + return "Passed" \ No newline at end of file diff --git a/src/amd/applications/hip_samples/Samples_Patch_4.2.x b/src/amd/applications/hip_samples/Samples_Patch_4.2.x new file mode 100644 index 0000000..37efd52 --- /dev/null +++ b/src/amd/applications/hip_samples/Samples_Patch_4.2.x @@ -0,0 +1,1514 @@ +diff -ruN HIP/samples/0_Intro/bit_extract/Makefile samples/0_Intro/bit_extract/Makefile +--- HIP/samples/0_Intro/bit_extract/Makefile 2021-07-09 10:33:24.804268020 +0530 ++++ samples/0_Intro/bit_extract/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -20,17 +20,23 @@ + + #Dependencies : [MYHIP]/bin must be in user's path. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) + HIP_PATH=../../.. ++ endif + endif +-HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++ + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + # Show how to use PLATFORM to specify different options for each compiler: +-ifeq (${HIP_PLATFORM}, nvcc) +- HIPCC_FLAGS = -gencode=arch=compute_20,code=sm_20 +-endif ++# By default, it will be built for current target ++# ifeq (${HIP_PLATFORM}, nvidia) ++# HIPCC_FLAGS = -gencode=arch=compute_20,code=sm_20 ++# endif + + EXE=bit_extract + +diff -ruN HIP/samples/0_Intro/module_api/defaultDriver.cpp samples/0_Intro/module_api/defaultDriver.cpp +--- HIP/samples/0_Intro/module_api/defaultDriver.cpp 2021-07-09 10:33:24.804268020 +0530 ++++ samples/0_Intro/module_api/defaultDriver.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -44,10 +44,11 @@ + + hipInit(0); + hipDevice_t device; ++#ifdef __HIP_PLATFORM_NVIDIA__ + hipCtx_t context; + hipDeviceGet(&device, 0); + hipCtxCreate(&context, 0, device); +- ++#endif + hipMalloc((void**)&Ad, SIZE); + hipMalloc((void**)&Bd, SIZE); + +@@ -78,10 +79,12 @@ + std::cout << "FAILED!\n"; + }; + +- hipFree(Ad); +- hipFree(Bd); ++ hipFree((void *)Ad); ++ hipFree((void *)Bd); + delete[] A; + delete[] B; ++#ifdef __HIP_PLATFORM_NVIDIA__ + hipCtxDestroy(context); ++#endif + return 0; + } +diff -ruN HIP/samples/0_Intro/module_api/launchKernelHcc.cpp samples/0_Intro/module_api/launchKernelHcc.cpp +--- HIP/samples/0_Intro/module_api/launchKernelHcc.cpp 2021-07-09 10:33:24.804268020 +0530 ++++ samples/0_Intro/module_api/launchKernelHcc.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -55,10 +55,11 @@ + + hipInit(0); + hipDevice_t device; ++#ifdef __HIP_PLATFORM_NVIDIA__ + hipCtx_t context; + hipDeviceGet(&device, 0); + hipCtxCreate(&context, 0, device); +- ++#endif + hipMalloc((void**)&Ad, SIZE); + hipMalloc((void**)&Bd, SIZE); + +@@ -109,6 +110,8 @@ + hipFree(Bd); + delete[] A; + delete[] B; ++#ifdef __HIP_PLATFORM_NVIDIA__ + hipCtxDestroy(context); ++#endif + return 0; + } +diff -ruN HIP/samples/0_Intro/module_api/Makefile samples/0_Intro/module_api/Makefile +--- HIP/samples/0_Intro/module_api/Makefile 2021-07-09 10:33:24.804268020 +0530 ++++ samples/0_Intro/module_api/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,14 +18,24 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif ++ + HIPCC=$(HIP_PATH)/bin/hipcc +-HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + ++all: ++ifeq (nvidia, $(HIP_PLATFORM)) ++all: vcpy_kernel.code runKernel.hip.out defaultDriver.hip.out ++else + all: vcpy_kernel.code runKernel.hip.out launchKernelHcc.hip.out defaultDriver.hip.out ++endif + + runKernel.hip.out: runKernel.cpp + $(HIPCC) $(HIPCC_FLAGS) $< -o $@ +diff -ruN HIP/samples/0_Intro/module_api/runKernel.cpp samples/0_Intro/module_api/runKernel.cpp +--- HIP/samples/0_Intro/module_api/runKernel.cpp 2021-07-09 10:33:24.804268020 +0530 ++++ samples/0_Intro/module_api/runKernel.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -25,7 +25,7 @@ + #include + #include + #include +-#include ++//#include + + #define LEN 64 + #define SIZE LEN << 2 +@@ -52,10 +52,11 @@ + + hipInit(0); + hipDevice_t device; ++#ifdef __HIP_PLATFORM_NVIDIA__ + hipCtx_t context; + hipDeviceGet(&device, 0); + hipCtxCreate(&context, 0, device); +- ++#endif + hipMalloc((void**)&Ad, SIZE); + hipMalloc((void**)&Bd, SIZE); + +@@ -97,10 +98,12 @@ + std::cout << "FAILED!\n"; + }; + +- hipFree(Ad); +- hipFree(Bd); ++ hipFree((void *)Ad); ++ hipFree((void *)Bd); + delete[] A; + delete[] B; ++#ifdef __HIP_PLATFORM_NVIDIA__ + hipCtxDestroy(context); ++#endif + return 0; + } +diff -ruN HIP/samples/0_Intro/module_api_global/Makefile samples/0_Intro/module_api_global/Makefile +--- HIP/samples/0_Intro/module_api_global/Makefile 2021-07-09 10:33:24.804268020 +0530 ++++ samples/0_Intro/module_api_global/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,12 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif ++ + HIPCC=$(HIP_PATH)/bin/hipcc +-HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + all: vcpy_kernel.code runKernel.hip.out + +diff -ruN HIP/samples/0_Intro/module_api_global/runKernel.cpp samples/0_Intro/module_api_global/runKernel.cpp +--- HIP/samples/0_Intro/module_api_global/runKernel.cpp 2021-07-09 10:33:24.804268020 +0530 ++++ samples/0_Intro/module_api_global/runKernel.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -25,7 +25,7 @@ + #include + #include + #include +-#include ++//#include + + #define LEN 64 + #define SIZE LEN * sizeof(float) +@@ -54,10 +54,11 @@ + + hipInit(0); + hipDevice_t device; ++#ifdef __HIP_PLATFORM_NVIDIA__ + hipCtx_t context; + hipDeviceGet(&device, 0); + hipCtxCreate(&context, 0, device); +- ++#endif + hipMalloc((void**)&Ad, SIZE); + hipMalloc((void**)&Bd, SIZE); + +@@ -69,7 +70,7 @@ + float myDeviceGlobal_h = 42.0; + float* deviceGlobal; + size_t deviceGlobalSize; +- HIP_CHECK(hipModuleGetGlobal((void**)&deviceGlobal, &deviceGlobalSize, Module, "myDeviceGlobal")); ++ HIP_CHECK(hipModuleGetGlobal((hipDeviceptr_t *)&deviceGlobal, &deviceGlobalSize, Module, "myDeviceGlobal")); + HIP_CHECK(hipMemcpyHtoD(hipDeviceptr_t(deviceGlobal), &myDeviceGlobal_h, deviceGlobalSize)); + + #define ARRAY_SIZE 16 +@@ -77,7 +78,7 @@ + float myDeviceGlobalArray_h[ARRAY_SIZE]; + float *myDeviceGlobalArray; + size_t myDeviceGlobalArraySize; +- HIP_CHECK(hipModuleGetGlobal((void**)&myDeviceGlobalArray, &myDeviceGlobalArraySize, Module, "myDeviceGlobalArray")); ++ HIP_CHECK(hipModuleGetGlobal((hipDeviceptr_t *)&myDeviceGlobalArray, &myDeviceGlobalArraySize, Module, "myDeviceGlobalArray")); + for (int i = 0; i < ARRAY_SIZE; i++) { + myDeviceGlobalArray_h[i] = i * 1000.0f; + HIP_CHECK(hipMemcpyHtoD(hipDeviceptr_t(myDeviceGlobalArray), &myDeviceGlobalArray_h, myDeviceGlobalArraySize)); +@@ -101,7 +102,7 @@ + HIP_CHECK(hipModuleGetFunction(&Function, Module, "hello_world")); + HIP_CHECK(hipModuleLaunchKernel(Function, 1, 1, 1, LEN, 1, 1, 0, 0, NULL, (void**)&config)); + +- hipMemcpyDtoH(B, Bd, SIZE); ++ hipMemcpyDtoH(B, (hipDeviceptr_t)Bd, SIZE); + + int mismatchCount = 0; + for (uint32_t i = 0; i < LEN; i++) { +@@ -131,7 +132,7 @@ + printf("Num Regs = %d\n",val); + HIP_CHECK(hipModuleLaunchKernel(Function, 1, 1, 1, LEN, 1, 1, 0, 0, NULL, (void**)&config)); + +- hipMemcpyDtoH(B, Bd, SIZE); ++ hipMemcpyDtoH(B, (hipDeviceptr_t)Bd, SIZE); + + int mismatchCount = 0; + for (uint32_t i = 0; i < LEN; i++) { +@@ -156,6 +157,8 @@ + hipFree(Bd); + delete[] A; + delete[] B; ++#ifdef __HIP_PLATFORM_NVIDIA__ + hipCtxDestroy(context); ++#endif + return 0; + } +diff -ruN HIP/samples/0_Intro/square/Makefile samples/0_Intro/square/Makefile +--- HIP/samples/0_Intro/square/Makefile 2021-07-09 10:33:24.804268020 +0530 ++++ samples/0_Intro/square/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,12 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif +-HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++ + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + ifeq (${HIP_PLATFORM}, nvidia) + SOURCES=square.cu +diff -ruN HIP/samples/0_Intro/square/README.md samples/0_Intro/square/README.md +--- HIP/samples/0_Intro/square/README.md 2021-07-09 10:33:24.804268020 +0530 ++++ samples/0_Intro/square/README.md 2021-07-09 10:28:19.000000000 +0530 +@@ -1,8 +1,6 @@ + # Square.md + +-Simple test which shows how to use hipify-perl to port CUDA code to HIP. +-See related [blog](http://gpuopen.com/hip-to-be-squared-an-introductory-hip-tutorial) that explains the example. +-Now it is even simpler and requires no manual modification to the hipified source code - just hipify and compile: ++Simple test below is an example, shows how to use hipify-perl to port CUDA code to HIP: + + - Add hip/bin path to the PATH + +diff -ruN HIP/samples/1_Utils/hipBusBandwidth/Makefile samples/1_Utils/hipBusBandwidth/Makefile +--- HIP/samples/1_Utils/hipBusBandwidth/Makefile 2021-07-09 10:33:24.804268020 +0530 ++++ samples/1_Utils/hipBusBandwidth/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,11 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif ++ + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + EXE=hipBusBandwidth + CXXFLAGS = -O3 +diff -ruN HIP/samples/1_Utils/hipCommander/Makefile samples/1_Utils/hipCommander/Makefile +--- HIP/samples/1_Utils/hipCommander/Makefile 2021-07-09 10:33:24.804268020 +0530 ++++ samples/1_Utils/hipCommander/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,11 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif ++ + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + EXE=hipCommander + OPT=-O3 +diff -ruN HIP/samples/1_Utils/hipDispatchLatency/hipDispatchLatency.cpp samples/1_Utils/hipDispatchLatency/hipDispatchLatency.cpp +--- HIP/samples/1_Utils/hipDispatchLatency/hipDispatchLatency.cpp 2021-07-09 10:33:24.808268042 +0530 ++++ samples/1_Utils/hipDispatchLatency/hipDispatchLatency.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -65,10 +65,12 @@ + + int main() { + hipStream_t stream0 = 0; ++#ifdef __HIP_PLATFORM_NVIDIA__ + hipDevice_t device; + hipDeviceGet(&device, 0); + hipCtx_t context; + hipCtxCreate(&context, 0, device); ++#endif + hipModule_t module; + hipFunction_t function; + hipModuleLoad(&module, FILE_NAME); +@@ -136,6 +138,8 @@ + + hipEventDestroy(start); + hipEventDestroy(stop); ++#ifdef __HIP_PLATFORM_NVIDIA__ + hipCtxDestroy(context); ++#endif + } + +diff -ruN HIP/samples/1_Utils/hipDispatchLatency/Makefile samples/1_Utils/hipDispatchLatency/Makefile +--- HIP/samples/1_Utils/hipDispatchLatency/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/1_Utils/hipDispatchLatency/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,11 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif +-HIPCC=$(HIP_PATH)/bin/hipcc -std=c++11 ++ ++HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + CXXFLAGS = -O3 + +diff -ruN HIP/samples/1_Utils/hipInfo/Makefile samples/1_Utils/hipInfo/Makefile +--- HIP/samples/1_Utils/hipInfo/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/1_Utils/hipInfo/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,11 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif ++ + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + EXE=hipInfo + +diff -ruN HIP/samples/2_Cookbook/0_MatrixTranspose/Makefile samples/2_Cookbook/0_MatrixTranspose/Makefile +--- HIP/samples/2_Cookbook/0_MatrixTranspose/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/0_MatrixTranspose/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,12 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + TARGET=hcc + +diff -ruN HIP/samples/2_Cookbook/10_inline_asm/Makefile samples/2_Cookbook/10_inline_asm/Makefile +--- HIP/samples/2_Cookbook/10_inline_asm/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/10_inline_asm/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,12 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + TARGET=hcc + +@@ -35,7 +40,10 @@ + .PHONY: test + + ++all: ++ifeq ($(HIP_PLATFORM),amd) + all: $(EXECUTABLE) test ++endif + + CXXFLAGS =-g + CXX=$(HIPCC) +diff -ruN HIP/samples/2_Cookbook/11_texture_driver/Makefile samples/2_Cookbook/11_texture_driver/Makefile +--- HIP/samples/2_Cookbook/11_texture_driver/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/11_texture_driver/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,12 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif ++ + HIPCC=$(HIP_PATH)/bin/hipcc +-HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + all: tex2dKernel.code texture2dDrv.out + +diff -ruN HIP/samples/2_Cookbook/11_texture_driver/texture2dDrv.cpp samples/2_Cookbook/11_texture_driver/texture2dDrv.cpp +--- HIP/samples/2_Cookbook/11_texture_driver/texture2dDrv.cpp 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/11_texture_driver/texture2dDrv.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -26,7 +26,31 @@ + #include + + #define fileName "tex2dKernel.code" ++#ifdef __HIP_PLATFORM_NVIDIA__ ++#define CTX_CREATE() \ ++ hipCtx_t context;\ ++ initHipCtx(&context); ++#define CTX_DESTROY() HIP_CHECK(hipCtxDestroy(context)); ++#define ARRAY_DESTROY(array) HIP_CHECK(hipArrayDestroy(array)); ++#define HIP_TEX_REFERENCE hipTexRef ++#define HIP_ARRAY hiparray ++/** ++ * Internal Function ++ */ ++void initHipCtx(hipCtx_t *pcontext) { ++ hipInit(0); ++ hipDevice_t device; ++ hipDeviceGet(&device, 0); ++ hipCtxCreate(pcontext, 0, device); ++} + ++#else ++#define CTX_CREATE() ++#define CTX_DESTROY() ++#define ARRAY_DESTROY(array) HIP_CHECK(hipFreeArray(array)); ++#define HIP_TEX_REFERENCE textureReference* ++#define HIP_ARRAY hipArray* ++#endif + bool testResult = true; + + #define HIP_CHECK(cmd) \ +@@ -50,10 +74,11 @@ + hData[i * width + j] = i * width + j; + } + } ++ CTX_CREATE(); + hipModule_t Module; + HIP_CHECK(hipModuleLoad(&Module, fileName)); + +- hipArray* array; ++ HIP_ARRAY array; + HIP_ARRAY_DESCRIPTOR desc; + desc.Format = HIP_AD_FORMAT_FLOAT; + desc.NumChannels = 1; +@@ -63,23 +88,38 @@ + + hip_Memcpy2D copyParam; + memset(©Param, 0, sizeof(copyParam)); +- copyParam.dstMemoryType = hipMemoryTypeArray; ++#ifdef __HIP_PLATFORM_NVCC__ ++ copyParam.dstMemoryType = CU_MEMORYTYPE_ARRAY; ++ copyParam.srcMemoryType = CU_MEMORYTYPE_HOST; + copyParam.dstArray = array; ++#else ++ copyParam.dstMemoryType = hipMemoryTypeArray; + copyParam.srcMemoryType = hipMemoryTypeHost; ++ copyParam.dstArray = array; ++#endif + copyParam.srcHost = hData; + copyParam.srcPitch = width * sizeof(float); + copyParam.WidthInBytes = copyParam.srcPitch; + copyParam.Height = height; + HIP_CHECK(hipMemcpyParam2D(©Param)); +- +- textureReference* texref; +- HIP_CHECK(hipModuleGetTexRef(&texref, Module, "tex")); +- HIP_CHECK(hipTexRefSetAddressMode(texref, 0, hipAddressModeWrap)); +- HIP_CHECK(hipTexRefSetAddressMode(texref, 1, hipAddressModeWrap)); +- HIP_CHECK(hipTexRefSetFilterMode(texref, hipFilterModePoint)); +- HIP_CHECK(hipTexRefSetFlags(texref, 0)); +- HIP_CHECK(hipTexRefSetFormat(texref, HIP_AD_FORMAT_FLOAT, 1)); +- HIP_CHECK(hipTexRefSetArray(texref, array, HIP_TRSA_OVERRIDE_FORMAT)); ++ HIP_TEX_REFERENCE texref; ++#ifdef __HIP_PLATFORM_NVIDIA__ ++ HIP_CHECK(hipModuleGetTexRef(&texref, Module, "tex")); ++ HIP_CHECK(hipTexRefSetAddressMode(texref, 0, CU_TR_ADDRESS_MODE_WRAP)); ++ HIP_CHECK(hipTexRefSetAddressMode(texref, 1, CU_TR_ADDRESS_MODE_WRAP)); ++ HIP_CHECK(hipTexRefSetFilterMode(texref, HIP_TR_FILTER_MODE_POINT)); ++ HIP_CHECK(hipTexRefSetFlags(texref, 0)); ++ HIP_CHECK(hipTexRefSetFormat(texref, HIP_AD_FORMAT_FLOAT, 1)); ++ HIP_CHECK(hipTexRefSetArray(texref, array, CU_TRSA_OVERRIDE_FORMAT)); ++#else ++ HIP_CHECK(hipModuleGetTexRef(&texref, Module, "tex")); ++ HIP_CHECK(hipTexRefSetAddressMode(texref, 0, hipAddressModeWrap)); ++ HIP_CHECK(hipTexRefSetAddressMode(texref, 1, hipAddressModeWrap)); ++ HIP_CHECK(hipTexRefSetFilterMode(texref, hipFilterModePoint)); ++ HIP_CHECK(hipTexRefSetFlags(texref, 0)); ++ HIP_CHECK(hipTexRefSetFormat(texref, HIP_AD_FORMAT_FLOAT, 1)); ++ HIP_CHECK(hipTexRefSetArray(texref, array, HIP_TRSA_OVERRIDE_FORMAT)); ++#endif + + float* dData = NULL; + HIP_CHECK(hipMalloc((void**)&dData, size)); +@@ -121,9 +161,9 @@ + } + } + } +- HIP_CHECK(hipUnbindTexture(texref)); + HIP_CHECK(hipFree(dData)); +- HIP_CHECK(hipFreeArray(array)); ++ ARRAY_DESTROY(array) ++ CTX_DESTROY(); + return testResult; + } + +diff -ruN HIP/samples/2_Cookbook/13_occupancy/Makefile samples/2_Cookbook/13_occupancy/Makefile +--- HIP/samples/2_Cookbook/13_occupancy/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/13_occupancy/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,11 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif ++ + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + EXE=./occupancy + +diff -ruN HIP/samples/2_Cookbook/14_gpu_arch/gpuarch.cpp samples/2_Cookbook/14_gpu_arch/gpuarch.cpp +--- HIP/samples/2_Cookbook/14_gpu_arch/gpuarch.cpp 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/14_gpu_arch/gpuarch.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -90,9 +90,9 @@ + } + } + if (flag == false) { +- std::cout << "Error: Kernel is supported for gfx908 architecture\n"; ++ std::cout << "info: Kernel is supported only for gfx908 architecture\n"; + } else { + std::cout << "success\n"; + } + return 0; +-} +\ No newline at end of file ++} +diff -ruN HIP/samples/2_Cookbook/14_gpu_arch/Makefile samples/2_Cookbook/14_gpu_arch/Makefile +--- HIP/samples/2_Cookbook/14_gpu_arch/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/14_gpu_arch/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,17 +18,29 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif ++ + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + EXE=./gpuarch + + .PHONY: test + ++all: ++ifneq (amd, $(HIP_PLATFORM)) ++all: ++ $(info Not supported for platform $(HIP_PLATFORM)!) ++else + all: test ++endif + + $(EXE): gpuarch.cpp + $(HIPCC) $^ -o $@ +diff -ruN HIP/samples/2_Cookbook/15_static_library/device_functions/CMakeLists.txt samples/2_Cookbook/15_static_library/device_functions/CMakeLists.txt +--- HIP/samples/2_Cookbook/15_static_library/device_functions/CMakeLists.txt 1970-01-01 05:30:00.000000000 +0530 ++++ samples/2_Cookbook/15_static_library/device_functions/CMakeLists.txt 2021-07-09 10:28:19.000000000 +0530 +@@ -0,0 +1,36 @@ ++project(static_lib) ++ ++cmake_minimum_required(VERSION 3.10) ++ ++# Search for rocm in common locations ++list(APPEND CMAKE_PREFIX_PATH /opt/rocm/hip /opt/rocm) ++ ++# Find hip ++find_package(hip REQUIRED) ++ ++# Set compiler and linker ++set(CMAKE_CXX_COMPILER ${HIP_HIPCC_EXECUTABLE}) ++set(CMAKE_CXX_LINKER ${HIP_HIPCC_EXECUTABLE}) ++set(CMAKE_BUILD_TYPE Release) ++ ++# Turn static library generation ON ++option(BUILD_SHARED_LIBS "Build as a shared library" OFF) ++ ++set(CPP_SOURCES hipDevice.cpp) ++ ++# Generate static lib libHipDevice.a ++add_library(HipDevice STATIC ${CPP_SOURCES}) ++ ++target_compile_options(HipDevice PRIVATE -fgpu-rdc) ++target_link_libraries(HipDevice PRIVATE -fgpu-rdc) ++target_include_directories(HipDevice PRIVATE /opt/rocm/hsa/include) ++ ++# Create test executable that uses libHipDevice.a ++set(TEST_SOURCES ${CMAKE_SOURCE_DIR}/hipMain2.cpp) ++ ++add_executable(test_device_static ${TEST_SOURCES}) ++add_dependencies(test_device_static HipDevice) ++target_compile_options(test_device_static PRIVATE -fgpu-rdc) ++target_link_libraries(test_device_static PRIVATE HipDevice) ++target_link_libraries(test_device_static PRIVATE -fgpu-rdc hip::host) ++ +diff -ruN HIP/samples/2_Cookbook/15_static_library/device_functions/hipDevice.cpp samples/2_Cookbook/15_static_library/device_functions/hipDevice.cpp +--- HIP/samples/2_Cookbook/15_static_library/device_functions/hipDevice.cpp 1970-01-01 05:30:00.000000000 +0530 ++++ samples/2_Cookbook/15_static_library/device_functions/hipDevice.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -0,0 +1,28 @@ ++/* ++ * Copyright (c) 2020-present Advanced Micro Devices, Inc. All rights reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ * */ ++ ++#include ++ ++__device__ int square_me(int A) { ++ return A*A; ++} ++ +diff -ruN HIP/samples/2_Cookbook/15_static_library/device_functions/hipMain2.cpp samples/2_Cookbook/15_static_library/device_functions/hipMain2.cpp +--- HIP/samples/2_Cookbook/15_static_library/device_functions/hipMain2.cpp 1970-01-01 05:30:00.000000000 +0530 ++++ samples/2_Cookbook/15_static_library/device_functions/hipMain2.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -0,0 +1,69 @@ ++/* ++ * Copyright (c) 2020-present Advanced Micro Devices, Inc. All rights reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ * */ ++ ++#include ++#include ++#include ++ ++#define HIP_ASSERT(status) assert(status == hipSuccess) ++#define LEN 512 ++ ++extern __device__ int square_me(int); ++ ++__global__ void square_and_save(int* A, int* B) { ++ int tid = threadIdx.x + blockIdx.x * blockDim.x; ++ B[tid] = square_me(A[tid]); ++} ++ ++void run_test2() { ++ int *A_h, *B_h, *A_d, *B_d; ++ A_h = new int[LEN]; ++ B_h = new int[LEN]; ++ for (unsigned i = 0; i < LEN; i++) { ++ A_h[i] = i; ++ B_h[i] = 0; ++ } ++ size_t valbytes = LEN*sizeof(int); ++ ++ HIP_ASSERT(hipMalloc((void**)&A_d, valbytes)); ++ HIP_ASSERT(hipMalloc((void**)&B_d, valbytes)); ++ ++ HIP_ASSERT(hipMemcpy(A_d, A_h, valbytes, hipMemcpyHostToDevice)); ++ hipLaunchKernelGGL(square_and_save, dim3(LEN/64), dim3(64), ++ 0, 0, A_d, B_d); ++ HIP_ASSERT(hipMemcpy(B_h, B_d, valbytes, hipMemcpyDeviceToHost)); ++ ++ for (unsigned i = 0; i < LEN; i++) { ++ assert(A_h[i]*A_h[i] == B_h[i]); ++ } ++ ++ HIP_ASSERT(hipFree(A_d)); ++ HIP_ASSERT(hipFree(B_d)); ++ free(A_h); ++ free(B_h); ++ std::cout << "Test Passed!\n"; ++} ++ ++int main(){ ++ // Run test that generates static lib with ar ++ run_test2(); ++} +diff -ruN HIP/samples/2_Cookbook/15_static_library/device_functions/Makefile samples/2_Cookbook/15_static_library/device_functions/Makefile +--- HIP/samples/2_Cookbook/15_static_library/device_functions/Makefile 1970-01-01 05:30:00.000000000 +0530 ++++ samples/2_Cookbook/15_static_library/device_functions/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -0,0 +1,41 @@ ++HIP_PATH = $(wildcard /opt/rocm/hip) ++ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../../../build ++endif ++ ++ ++HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) ++ ++.PHONY: test ++ ++all: ++ifneq (amd, $(HIP_PLATFORM)) ++all: ++ $(info Not supported for platform $(HIP_PLATFORM)!) ++else ++all: $(RDC_EXE) test ++endif ++ ++STATIC_LIB_SRC=hipDevice.cpp ++STATIC_LIB=./libHipDevice.a ++STATIC_MAIN_SRC=hipMain2.cpp ++RDC_EXE=./test_device_static.out ++ ++$(STATIC_LIB): ++ $(HIPCC) $(STATIC_LIB_SRC) -c -fgpu-rdc -fPIC -o hipDevice.o ++ ar rcsD $@ hipDevice.o ++ ++# Compiles hipMain2 with hipcc and links with libHipDevice.a which contains device function. ++$(RDC_EXE): $(STATIC_LIB) ++ $(HIPCC) $(STATIC_LIB) $(STATIC_MAIN_SRC) -fgpu-rdc -o $@ ++ ++test: $(RDC_EXE) ++ $(RDC_EXE) ++ ++clean: ++ rm -f $(RDC_EXE) ++ rm -f $(STATIC_LIB) ++ rm -f *.o +diff -ruN HIP/samples/2_Cookbook/15_static_library/host_functions/CMakeLists.txt samples/2_Cookbook/15_static_library/host_functions/CMakeLists.txt +--- HIP/samples/2_Cookbook/15_static_library/host_functions/CMakeLists.txt 1970-01-01 05:30:00.000000000 +0530 ++++ samples/2_Cookbook/15_static_library/host_functions/CMakeLists.txt 2021-07-09 10:28:19.000000000 +0530 +@@ -0,0 +1,39 @@ ++project(static_lib) ++ ++cmake_minimum_required(VERSION 3.10) ++ ++# Search for rocm in common locations ++list(APPEND CMAKE_PREFIX_PATH /opt/rocm/hip /opt/rocm) ++ ++# Find hip ++find_package(hip REQUIRED) ++ ++# Set compiler and linker ++set(CMAKE_CXX_COMPILER ${HIP_HIPCC_EXECUTABLE}) ++set(CMAKE_CXX_LINKER ${HIP_HIPCC_EXECUTABLE}) ++set(CMAKE_AR ${HIP_HIPCC_EXECUTABLE}) ++set(CMAKE_BUILD_TYPE Release) ++ ++# Turn static library generation ON ++option(BUILD_SHARED_LIBS "Build as a shared library" OFF) ++ ++set(CPP_SOURCES hipOptLibrary.cpp) ++ ++# Generate static lib libHipOptLibrary.a. ++add_library(HipOptLibrary STATIC ${CPP_SOURCES}) ++ ++# Set-up the correct flags to generate the static library. ++target_link_libraries(HipOptLibrary PRIVATE --emit-static-lib) ++target_include_directories(HipOptLibrary PRIVATE /opt/rocm/hsa/include) ++get_property(link_libraries TARGET HipOptLibrary PROPERTY LINK_LIBRARIES) ++string (REPLACE ";" " " LINK_PROPS "${link_libraries}") ++set(CMAKE_CXX_ARCHIVE_CREATE " -o ${LINK_PROPS} ") ++ ++# Create test executable that uses libHipOptLibrary.a ++set(TEST_SOURCES ${CMAKE_SOURCE_DIR}/hipMain1.cpp) ++ ++add_executable(test_opt_static ${TEST_SOURCES}) ++add_dependencies(test_opt_static HipOptLibrary) ++target_link_libraries(test_opt_static PRIVATE -lHipOptLibrary -L${CMAKE_BINARY_DIR}) ++target_link_libraries(test_opt_static PRIVATE amdhip64 amd_comgr hsa-runtime64::hsa-runtime64) ++ +diff -ruN HIP/samples/2_Cookbook/15_static_library/host_functions/hipMain1.cpp samples/2_Cookbook/15_static_library/host_functions/hipMain1.cpp +--- HIP/samples/2_Cookbook/15_static_library/host_functions/hipMain1.cpp 1970-01-01 05:30:00.000000000 +0530 ++++ samples/2_Cookbook/15_static_library/host_functions/hipMain1.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -0,0 +1,28 @@ ++/* ++ * Copyright (c) 2020-present Advanced Micro Devices, Inc. All rights reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ * */ ++ ++extern void run_test1(); ++ ++int main(){ ++ // Run test that generates static lib with -emit-static-lib ++ run_test1(); ++} +diff -ruN HIP/samples/2_Cookbook/15_static_library/host_functions/hipOptLibrary.cpp samples/2_Cookbook/15_static_library/host_functions/hipOptLibrary.cpp +--- HIP/samples/2_Cookbook/15_static_library/host_functions/hipOptLibrary.cpp 1970-01-01 05:30:00.000000000 +0530 ++++ samples/2_Cookbook/15_static_library/host_functions/hipOptLibrary.cpp 2021-07-09 10:28:19.000000000 +0530 +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (c) 2020-present Advanced Micro Devices, Inc. All rights reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ * */ ++ ++#include ++#include ++#include ++ ++#define HIP_ASSERT(status) assert(status == hipSuccess) ++#define LEN 512 ++ ++__global__ void copy(uint32_t* A, uint32_t* B) { ++ size_t tid = threadIdx.x + blockIdx.x * blockDim.x; ++ B[tid] = A[tid]; ++} ++ ++void run_test1() { ++ uint32_t *A_h, *B_h, *A_d, *B_d; ++ size_t valbytes = LEN * sizeof(uint32_t); ++ ++ A_h = (uint32_t*)malloc(valbytes); ++ B_h = (uint32_t*)malloc(valbytes); ++ for (uint32_t i = 0; i < LEN; i++) { ++ A_h[i] = i; ++ B_h[i] = 0; ++ } ++ ++ HIP_ASSERT(hipMalloc((void**)&A_d, valbytes)); ++ HIP_ASSERT(hipMalloc((void**)&B_d, valbytes)); ++ ++ HIP_ASSERT(hipMemcpy(A_d, A_h, valbytes, hipMemcpyHostToDevice)); ++ hipLaunchKernelGGL(copy, dim3(LEN/64), dim3(64), 0, 0, A_d, B_d); ++ HIP_ASSERT(hipMemcpy(B_h, B_d, valbytes, hipMemcpyDeviceToHost)); ++ ++ for (uint32_t i = 0; i < LEN; i++) { ++ assert(A_h[i] == B_h[i]); ++ } ++ ++ HIP_ASSERT(hipFree(A_d)); ++ HIP_ASSERT(hipFree(B_d)); ++ free(A_h); ++ free(B_h); ++ std::cout << "Test Passed!\n"; ++} +diff -ruN HIP/samples/2_Cookbook/15_static_library/host_functions/Makefile samples/2_Cookbook/15_static_library/host_functions/Makefile +--- HIP/samples/2_Cookbook/15_static_library/host_functions/Makefile 1970-01-01 05:30:00.000000000 +0530 ++++ samples/2_Cookbook/15_static_library/host_functions/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -0,0 +1,49 @@ ++HIP_PATH = $(wildcard /opt/rocm/hip) ++ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../../../build ++endif ++ ++ ++HIPCC=$(HIP_PATH)/bin/hipcc ++GXX=g++ ++ ++EMIT_STATIC_LIB_SRC=hipOptLibrary.cpp ++EMIT_STATIC_LIB=./libHipOptLibrary.a ++EMIT_STATIC_MAIN_SRC=hipMain1.cpp ++HIPCC_EXE=./test_emit_static_hipcc_linker.out ++HOST_EXE=./test_emit_static_host_linker.out ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) ++ ++ ++.PHONY: test ++ ++all: ++ifneq (amd, $(HIP_PLATFORM)) ++all: ++ $(info Not supported for platform $(HIP_PLATFORM)!) ++else ++all: $(HIPCC_EXE) $(HOST_EXE) test ++endif ++ ++$(EMIT_STATIC_LIB): ++ $(HIPCC) $(EMIT_STATIC_LIB_SRC) --emit-static-lib -fPIC -o $@ ++ ++# Compiles hipMain1 with hipcc and links with libHipOptLibrary.a which contains host function. ++$(HIPCC_EXE): $(EMIT_STATIC_LIB) ++ $(HIPCC) $(EMIT_STATIC_MAIN_SRC) -L. -lHipOptLibrary -o $@ ++ ++# Compiles hipMain1 with g++ and links with libHipOptLibrary.a which contains host function. ++$(HOST_EXE): $(EMIT_STATIC_LIB) ++ $(GXX) $(EMIT_STATIC_MAIN_SRC) -L. -lHipOptLibrary -L$(HIP_PATH)/lib -lamdhip64 -Wl,-rpath=$(HIP_PATH)/lib -o $@ ++ ++test: $(HIPCC_EXE) $(HOST_EXE) ++ $(HIPCC_EXE) ++ $(HOST_EXE) ++ ++clean: ++ rm -f $(HIPCC_EXE) ++ rm -f $(HOST_EXE) ++ rm -f $(EMIT_STATIC_LIB) ++ rm -f *.o +diff -ruN HIP/samples/2_Cookbook/15_static_library/README.md samples/2_Cookbook/15_static_library/README.md +--- HIP/samples/2_Cookbook/15_static_library/README.md 1970-01-01 05:30:00.000000000 +0530 ++++ samples/2_Cookbook/15_static_library/README.md 2021-07-09 10:28:19.000000000 +0530 +@@ -0,0 +1,175 @@ ++# Emitting Static Library ++ ++This sample shows how to generate a static library for a simple HIP application. We will evaluate two types of static libraries: the first type exports host functions in a static library generated with --emit-static-lib and is compatible with host linkers, and second type exports device functions in a static library made with system ar. ++ ++Please refer to the hip_programming_guide for limitations. ++ ++## Static libraries with host functions ++ ++### Source files ++The static library source files may contain host functions and kernel `__global__` and `__device__` functions. Here is an example (please refer to the directory host_functions). ++ ++hipOptLibrary.cpp: ++``` ++#define HIP_ASSERT(status) assert(status == hipSuccess) ++#define LEN 512 ++ ++__global__ void copy(uint32_t* A, uint32_t* B) { ++ size_t tid = threadIdx.x + blockIdx.x * blockDim.x; ++ B[tid] = A[tid]; ++} ++ ++void run_test1() { ++ uint32_t *A_h, *B_h, *A_d, *B_d; ++ size_t valbytes = LEN * sizeof(uint32_t); ++ ++ A_h = (uint32_t*)malloc(valbytes); ++ B_h = (uint32_t*)malloc(valbytes); ++ for (uint32_t i = 0; i < LEN; i++) { ++ A_h[i] = i; ++ B_h[i] = 0; ++ } ++ ++ HIP_ASSERT(hipMalloc((void**)&A_d, valbytes)); ++ HIP_ASSERT(hipMalloc((void**)&B_d, valbytes)); ++ ++ HIP_ASSERT(hipMemcpy(A_d, A_h, valbytes, hipMemcpyHostToDevice)); ++ hipLaunchKernelGGL(copy, dim3(LEN/64), dim3(64), 0, 0, A_d, B_d); ++ HIP_ASSERT(hipMemcpy(B_h, B_d, valbytes, hipMemcpyDeviceToHost)); ++ ++ for (uint32_t i = 0; i < LEN; i++) { ++ assert(A_h[i] == B_h[i]); ++ } ++ ++ HIP_ASSERT(hipFree(A_d)); ++ HIP_ASSERT(hipFree(B_d)); ++ free(A_h); ++ free(B_h); ++ std::cout << "Test Passed!\n"; ++} ++``` ++ ++The above source file can be compiled into a static library, libHipOptLibrary.a, using the --emit-static-lib flag, like so: ++``` ++hipcc hipOptLibrary.cpp --emit-static-lib -fPIC -o libHipOptLibrary.a ++``` ++ ++### Main source files ++The main() program source file may link with the above static library using either hipcc or a host compiler (such as g++). A simple source file that calls the host function inside libHipOptLibrary.a: ++ ++hipMain1.cpp: ++``` ++extern void run_test1(); ++ ++int main(){ ++ run_test1(); ++} ++``` ++ ++To link to the static library: ++ ++Using hipcc: ++``` ++hipcc hipMain1.cpp -L. -lHipOptLibrary -o test_emit_static_hipcc_linker.out ++``` ++Using g++: ++``` ++g++ hipMain1.cpp -L. -lHipOptLibrary -L/opt/rocm/hip/lib -lamdhip64 -o test_emit_static_host_linker.out ++``` ++ ++## Static libraries with device functions ++ ++### Source files ++The static library source files which contain only `__device__` functions need to be created using ar. Here is an example (please refer to the directory device_functions). ++ ++hipDevice.cpp: ++``` ++#include ++ ++__device__ int square_me(int A) { ++ return A*A; ++} ++``` ++ ++The above source file may be compiled into a static library, libHipDevice.a, by first compiling into a relocatable object, and then placed in an archive using ar: ++``` ++hipcc hipDevice.cpp -c -fgpu-rdc -fPIC -o hipDevice.o ++ar rcsD libHipDevice.a hipDevice.o ++``` ++ ++### Main source files ++The main() program source file can link with the static library using hipcc. A simple source file that calls the device function inside libHipDevice.a: ++ ++hipMain2.cpp: ++``` ++#include ++#include ++#include ++ ++#define HIP_ASSERT(status) assert(status == hipSuccess) ++#define LEN 512 ++ ++extern __device__ int square_me(int); ++ ++__global__ void square_and_save(int* A, int* B) { ++ int tid = threadIdx.x + blockIdx.x * blockDim.x; ++ B[tid] = square_me(A[tid]); ++} ++ ++void run_test2() { ++ int *A_h, *B_h, *A_d, *B_d; ++ A_h = new int[LEN]; ++ B_h = new int[LEN]; ++ for (unsigned i = 0; i < LEN; i++) { ++ A_h[i] = i; ++ B_h[i] = 0; ++ } ++ size_t valbytes = LEN*sizeof(int); ++ ++ HIP_ASSERT(hipMalloc((void**)&A_d, valbytes)); ++ HIP_ASSERT(hipMalloc((void**)&B_d, valbytes)); ++ ++ HIP_ASSERT(hipMemcpy(A_d, A_h, valbytes, hipMemcpyHostToDevice)); ++ hipLaunchKernelGGL(square_and_save, dim3(LEN/64), dim3(64), ++ 0, 0, A_d, B_d); ++ HIP_ASSERT(hipMemcpy(B_h, B_d, valbytes, hipMemcpyDeviceToHost)); ++ ++ for (unsigned i = 0; i < LEN; i++) { ++ assert(A_h[i]*A_h[i] == B_h[i]); ++ } ++ ++ HIP_ASSERT(hipFree(A_d)); ++ HIP_ASSERT(hipFree(B_d)); ++ free(A_h); ++ free(B_h); ++ std::cout << "Test Passed!\n"; ++} ++ ++int main(){ ++ // Run test that generates static lib with ar ++ run_test2(); ++} ++``` ++ ++To link to the static library: ++``` ++hipcc libHipDevice.a hipMain2.cpp -fgpu-rdc -o test_device_static_hipcc.out ++``` ++ ++## How to build and run this sample: ++Use the make command to build the static libraries, link with it, and execute it. ++- Change directory to either host or device functions folder. ++- To build the static library and link the main executable, use `make all`. ++- To execute, run the generated executable `./test_*.out`. ++ ++Alternatively, use these CMake commands. ++``` ++cd device_functions ++mkdir -p build ++cd build ++cmake .. ++make ++./test_*.out ++``` ++ ++## For More Infomation, please refer to the HIP FAQ. +diff -ruN HIP/samples/2_Cookbook/16_assembly_to_executable/Makefile samples/2_Cookbook/16_assembly_to_executable/Makefile +--- HIP/samples/2_Cookbook/16_assembly_to_executable/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/16_assembly_to_executable/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,11 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) ++ + HIPCC=$(HIP_PATH)/bin/hipcc + CLANG=$(HIP_PATH)/../llvm/bin/clang + LLVM_MC=$(HIP_PATH)/../llvm/bin/llvm-mc +@@ -44,7 +50,13 @@ + + .PHONY: test + ++all: ++ifneq (amd, $(HIP_PLATFORM)) ++all: ++ $(info Not supported for platform $(HIP_PLATFORM)!) ++else + all: src_to_asm asm_to_exec ++endif + + src_to_asm: + $(HIPCC) -c -S --cuda-host-only -target x86_64-linux-gnu -o $(SQ_HOST_ASM) $(SRCS) +diff -ruN HIP/samples/2_Cookbook/17_llvm_ir_to_executable/Makefile samples/2_Cookbook/17_llvm_ir_to_executable/Makefile +--- HIP/samples/2_Cookbook/17_llvm_ir_to_executable/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/17_llvm_ir_to_executable/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,11 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) ++ + HIPCC=$(HIP_PATH)/bin/hipcc + CLANG=$(HIP_PATH)/../llvm/bin/clang + LLVM_MC=$(HIP_PATH)/../llvm/bin/llvm-mc +@@ -47,7 +53,13 @@ + + .PHONY: test + ++all: ++ifneq (amd, $(HIP_PLATFORM)) ++all: ++ $(info Not supported for platform $(HIP_PLATFORM)!) ++else + all: src_to_ir bc_to_ll ll_to_bc ir_to_exec ++endif + + src_to_ir: + $(HIPCC) -c -emit-llvm --cuda-host-only -target x86_64-linux-gnu -o $(SQ_HOST_BC) $(SRCS) +diff -ruN HIP/samples/2_Cookbook/1_hipEvent/Makefile samples/2_Cookbook/1_hipEvent/Makefile +--- HIP/samples/2_Cookbook/1_hipEvent/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/1_hipEvent/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,12 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + TARGET=hcc + +diff -ruN HIP/samples/2_Cookbook/3_shared_memory/Makefile samples/2_Cookbook/3_shared_memory/Makefile +--- HIP/samples/2_Cookbook/3_shared_memory/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/3_shared_memory/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,12 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + TARGET=hcc + +diff -ruN HIP/samples/2_Cookbook/4_shfl/Makefile samples/2_Cookbook/4_shfl/Makefile +--- HIP/samples/2_Cookbook/4_shfl/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/4_shfl/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,17 +18,22 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + ++HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) ++ + ifeq (gfx701, $(findstring gfx701,$(HCC_AMDGPU_TARGET))) + $(error gfx701 is not a supported device for this sample) + endif + +-HIPCC=$(HIP_PATH)/bin/hipcc +- + TARGET=hcc + + SOURCES = shfl.cpp +diff -ruN HIP/samples/2_Cookbook/5_2dshfl/Makefile samples/2_Cookbook/5_2dshfl/Makefile +--- HIP/samples/2_Cookbook/5_2dshfl/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/5_2dshfl/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,17 +18,22 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + ++HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) ++ + ifeq (gfx701, $(findstring gfx701,$(HCC_AMDGPU_TARGET))) + $(error gfx701 is not a supported device for this sample) + endif + +-HIPCC=$(HIP_PATH)/bin/hipcc +- + TARGET=hcc + + SOURCES = 2dshfl.cpp +diff -ruN HIP/samples/2_Cookbook/6_dynamic_shared/Makefile samples/2_Cookbook/6_dynamic_shared/Makefile +--- HIP/samples/2_Cookbook/6_dynamic_shared/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/6_dynamic_shared/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,12 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + TARGET=hcc + +diff -ruN HIP/samples/2_Cookbook/7_streams/Makefile samples/2_Cookbook/7_streams/Makefile +--- HIP/samples/2_Cookbook/7_streams/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/7_streams/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,12 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + TARGET=hcc + +diff -ruN HIP/samples/2_Cookbook/8_peer2peer/Makefile samples/2_Cookbook/8_peer2peer/Makefile +--- HIP/samples/2_Cookbook/8_peer2peer/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/8_peer2peer/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,12 +18,17 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + + HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) + + TARGET=hcc + +diff -ruN HIP/samples/2_Cookbook/9_unroll/Makefile samples/2_Cookbook/9_unroll/Makefile +--- HIP/samples/2_Cookbook/9_unroll/Makefile 2021-07-09 10:33:24.808268042 +0530 ++++ samples/2_Cookbook/9_unroll/Makefile 2021-07-09 10:28:19.000000000 +0530 +@@ -18,17 +18,22 @@ + # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + # THE SOFTWARE. + +-HIP_PATH?= $(wildcard /opt/rocm/hip) + ifeq (,$(HIP_PATH)) +- HIP_PATH=../../.. ++ HIP_PATH?= $(wildcard /opt/rocm/hip) ++ ifeq (,$(HIP_PATH)) ++ HIP_PATH=../../.. ++ endif + endif + ++HIPCC=$(HIP_PATH)/bin/hipcc ++HIP_PLATFORM=$(shell $(HIP_PATH)/bin/hipconfig --platform) ++HIP_COMPILER=$(shell $(HIP_PATH)/bin/hipconfig --compiler) ++HIP_RUNTIME=$(shell $(HIP_PATH)/bin/hipconfig --runtime) ++ + ifeq (gfx701, $(findstring gfx701,$(HCC_AMDGPU_TARGET))) + $(error gfx701 is not a supported device for this sample) + endif + +-HIPCC=$(HIP_PATH)/bin/hipcc +- + TARGET=hcc + + SOURCES = unroll.cpp diff --git a/src/amd/applications/hip_samples/__init__.py b/src/amd/applications/hip_samples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/applications/hip_samples/hip_samples.py b/src/amd/applications/hip_samples/hip_samples.py new file mode 100644 index 0000000..5f3fcc9 --- /dev/null +++ b/src/amd/applications/hip_samples/hip_samples.py @@ -0,0 +1,1290 @@ +from amd.TesterRepository import Tester, Test, TestData +from amd.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from amd.test_classifier import TestClassifier +from amd.applications.hip_samples.hip_samples_build import BuildRunAmd, BuildRunNvidia +from amd.common.hip_get_packages import HipPackages +from amd.common.hip_shell import execshellcmd + +import os +import re +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, path, binary, cwd): + self.cwdAbs = cwd + self.conformancePath = os.path.join(self.cwdAbs, "src/amd/conformance/") + self.hippath = os.path.join(self.conformancePath, "HIP/") + self.thistestpath = os.path.join(self.hippath, path) + self.binary = binary + self.testExecOutput = "" + self.hiprepo = "" # Default + self.hipbranch = "" + self.hipcommitId = "" + self.prepareobj = None + + def setrepoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["hip"].repo_url != None: + self.hiprepo = test_data.repos["hip"].repo_url + else: + validrepconfig &= False + if test_data.repos["hip"].branch != None: + self.hipbranch = test_data.repos["hip"].branch + if test_data.repos["hip"].commit_id != None: + self.hipcommitId = test_data.repos["hip"].commit_id + return validrepconfig + + def downloadtest(self, logFile): + return HipPackages().pull_repo(logFile, self.hiprepo, self.hipbranch,\ + self.hipcommitId, "HIP") + + def buildtest(self, logFile, platform): + isBinaryPresent = True + if platform == HIP_PLATFORM.nvidia: + self.prepareobj = BuildRunNvidia(self.thistestpath, logFile) + elif platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.thistestpath, logFile) + else: + print("Invalid Platform") + return False + self.prepareobj.buildtest() + + if not os.path.isfile(\ + os.path.join(self.thistestpath, self.binary)): + isBinaryPresent &= False + return isBinaryPresent + + def clean(self): + self.prepareobj.clean() + + def runtest(self, logFile): + cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary + self.testExecOutput += execshellcmd(cmdexc, logFile, None) + + +# Common class to parse the result of test execution +class LogParser(): + def __init__(self, numOfExpPassed, testPassCriteria): + self.numOfExpPassed = numOfExpPassed + self.testPassCriteria = testPassCriteria + + def parse_common(self, testResultLog): + countPass = testResultLog.count(self.testPassCriteria) + return countPass + + def utils_parser(self, testcase, testResultLog): + pt1=re.compile(r'(Batch dispatch latency:)\s+(\d+\.\d+)\s+us,\s+std:\s+(\d+\.\d+)\s+us') + pt2=re.compile(r' (total_time,)(\d+\.\d+)') + pt3=re.compile(r'(memInfo.total:\s+)(\d+\.\d+) GB') + pt4=re.compile('(Bidir_Time_pinned\s+\d+\w+\s+ms\s+)(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)') + if testcase in 'hipDispatchLatency': + status=re.search(pt1,testResultLog) + if status: + #if float(str(status.group(4)))!=0.0 and float(str(status.group(2)))!=0.0 and float(str(status.group(3)))!=0.0: + return 'PASS' + else: + return'FAIL' + if testcase in 'hipCommander': + status=re.search(pt2,testResultLog) + if status: + if float(str(status.group(2)))!=0.0: + return 'PASS' + else: + return'FAIL' + else: + return 'FAIL' + if testcase in 'hipInfo': + status=re.search(pt3,testResultLog) + if status: + if float(str(status.group(2)))!=0.0: + return 'PASS' + else: + return'FAIL' + else: + return 'FAIL' + + if testcase in 'hipBusBandwidth': + status=re.search(pt4,testResultLog) + if status: + if float(str(status.group(4)))!=0.0 and float(str(status.group(2)))!=0.0 and float(str(status.group(3)))!=0.0: + return 'PASS' + else: + return'FAIL' + else: + return'FAIL' + + +class SAMPLES(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"samples": matched_with_names}) + + +class INTRO(SAMPLES): + def __init__(self): + SAMPLES.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + SAMPLES.add_matched_with_names(self, {"mini-app": matched_with_names}) + +class COOKBOOK(SAMPLES): + def __init__(self): + SAMPLES.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + SAMPLES.add_matched_with_names(self, {"mini-app": matched_with_names}) + + +class UTILS(SAMPLES): + def __init__(self): + SAMPLES.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + SAMPLES.add_matched_with_names(self, {"performance": matched_with_names}) + + +# Test samples/0_Intro/bit_extract +class BitExtract(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/0_Intro/bit_extract/", "bit_extract", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = INTRO() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== BitExtract Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/BitExtract.log", 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/0_Intro/module_api/defaultDriver.hip.out +class ModuleApiDefaultDriver(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/0_Intro/module_api/", "defaultDriver.hip.out", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = INTRO() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== ModuleApiDefaultDriver Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/ModuleApiDefaultDriver.log", 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/0_Intro/module_api/launchKernelHcc.hip.out +class ModuleApiLaunchKernelHcc(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/0_Intro/module_api/", + "launchKernelHcc.hip.out", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = INTRO() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== ModuleApiLaunchKernelHcc Test ===============") + # Set repo info + if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/ModuleApiLaunchKernelHcc.log", 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + + +# Test samples/0_Intro/module_api/runKernel.hip.out +class ModuleApiRunKernel(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/0_Intro/module_api/", "runKernel.hip.out", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = INTRO() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== ModuleApiRunKernel Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/ModuleApiRunKernel.log", 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/0_Intro/module_api_global +class ModuleApiGlobal(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/0_Intro/module_api_global/", + "runKernel.hip.out", self.cwd) + LogParser.__init__(self, 2, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = INTRO() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== ModuleApiGlobal Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/ModuleApiGlobal.log", 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/0_Intro/square/ +class Square(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/0_Intro/square/", "square.out", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + intro = INTRO() + intro.add_matched_with_names() + test.classifiers = [intro] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Square Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/Square.log", 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/0_MatrixTranspose +class MatrixTranspose(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/0_MatrixTranspose/", + "MatrixTranspose", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== MatrixTranspose Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + if not os.path.exists(resultLogDir): + os.makedirs(resultLogDir) + with open(resultLogDir + '/MatrixTranspose.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/10_inline_asm +class InlineAsm(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/10_inline_asm/", "inline_asm", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== InlineAsm Test ===============") + # Set repo info + if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/InlineAsm.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + + +# Test samples/2_Cookbook/16_assembly_to_executable +class SquareAsm(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/16_assembly_to_executable/", + "square_asm.out", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== SquareAsm Test ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/SquareAsm.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + + +# Test samples/2_Cookbook/11_texture_driver +class Texture2dDrv(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/11_texture_driver/", + "texture2dDrv.out", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Texture2dDrv Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/Texture2dDrv.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/9_unroll +class Unroll(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/9_unroll/", "unroll", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Unroll Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/Unroll.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/14_gpu_arch +# class GpuArch(Tester, PrepareTest, LogParser): + # def __init__(self): + # Tester.__init__(self) + # self.cwd = os.getcwd() + # PrepareTest.__init__(self, "samples/2_Cookbook/14_gpu_arch/", "gpuarch", self.cwd) + # LogParser.__init__(self, 1, "success") # Number of expected PASSED + + # def getTests(self) -> List[Test]: + # test = Test() + # test.test_name = self.__class__.__name__ + # cookbook = COOKBOOK() + # cookbook.add_matched_with_names() + # test.classifiers = [cookbook] + # test.tester = self + # return [test] + + # def clean(self): + # PrepareTest.clean(self) + + # def test(self, test_data: HIPTestData): + # print("=============== GpuArch Test ===============") + # # Set repo info + # isrepocfgvalid = self.setrepoinfo(test_data) + # if not isrepocfgvalid: + # test_data.test_result = TestResult.ERROR + # return + # # Create the log directory + # resultLogDir = test_data.log_location + # with open(resultLogDir + '/GpuArch.log', 'w+') as testLogger: + # res = self.downloadtest(testLogger) + # if not res: + # test_data.test_result = TestResult.FAIL + # return + # res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + # if not res: + # test_data.test_result = TestResult.FAIL + # return + # self.runtest(testLogger) + # if self.numOfExpPassed == self.parse_common(self.testExecOutput): + # test_data.test_result = TestResult.PASS + # else: + # test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/4_shfl +class Shfl(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/4_shfl/", "shfl", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Shfl Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/Shfl.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/3_shared_memory +class SharedMemory(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/3_shared_memory/", "sharedMemory", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== SharedMemory Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/SharedMemory.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/17_llvm_ir_to_executable +class Square_Ir(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/17_llvm_ir_to_executable/", + "square_ir.out", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Square_Ir Test ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/Square_Ir.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + +# Test samples/2_Cookbook/5_2dshfl +class Dshfl(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/5_2dshfl/", "2dshfl", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Dshfl Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/Dshfl.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/1_hipEvent +class HipEvent(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/1_hipEvent/", "hipEvent", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== HipEvent Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/HipEvent.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/6_dynamic_shared +class Dynamic_Shared(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/6_dynamic_shared/", + "dynamic_shared", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Dynamic_Shared Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/Dynamic_Shared.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/8_peer2peer +class Peer2peer(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/8_peer2peer/", "peer2peer", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Peer2peer Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/Peer2peer.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/7_streams +class Stream(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/7_streams/", "stream", self.cwd) + LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Stream Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/Stream.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/2_Cookbook/13_occupancy +class Occupancy(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/2_Cookbook/13_occupancy/", "occupancy", self.cwd) + LogParser.__init__(self, 2, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + cookbook = COOKBOOK() + cookbook.add_matched_with_names() + test.classifiers = [cookbook] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Occupancy Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/Occupancy.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if self.numOfExpPassed == self.parse_common(self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/1_Utils/hipBusBandwidth +class HipBusBandwidth(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/1_Utils/hipBusBandwidth/", "hipBusBandwidth", self.cwd) + LogParser.__init__(self, 2, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + utils = UTILS() + utils.add_matched_with_names() + test.classifiers = [utils] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== HipBusBandwidth Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/hipBusBandwidth.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if "PASS" == self.utils_parser("hipBusBandwidth", self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/1_Utils/hipCommander +class HipCommander(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/1_Utils/hipCommander/", "hipCommander", self.cwd) + LogParser.__init__(self, 2, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + utils = UTILS() + utils.add_matched_with_names() + test.classifiers = [utils] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== HipCommander Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/hipCommander.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if "PASS" == self.utils_parser("hipCommander", self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/1_Utils/hipDispatchLatency +class HipDispatchLatency(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/1_Utils/hipDispatchLatency/", "hipDispatchLatency.out", self.cwd) + LogParser.__init__(self, 2, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + utils = UTILS() + utils.add_matched_with_names() + test.classifiers = [utils] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== HipDispatchLatency Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/hipDispatchLatency.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if "PASS" == self.utils_parser("hipDispatchLatency", self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test samples/1_Utils/hipInfo +class HipInfo(Tester, PrepareTest, LogParser): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, "samples/1_Utils/hipInfo/", "hipInfo", self.cwd) + LogParser.__init__(self, 2, "PASSED") # Number of expected PASSED + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + utils = UTILS() + utils.add_matched_with_names() + test.classifiers = [utils] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== HipInfo Test ===============") + # Set repo info + isrepocfgvalid = self.setrepoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + '/hipInfo.log', 'w+') as testLogger: + res = self.downloadtest(testLogger) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(testLogger) + if "PASS" == self.utils_parser("hipInfo", self.testExecOutput): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL diff --git a/src/amd/applications/hip_samples/hip_samples_build.py b/src/amd/applications/hip_samples/hip_samples_build.py new file mode 100644 index 0000000..e4e6308 --- /dev/null +++ b/src/amd/applications/hip_samples/hip_samples_build.py @@ -0,0 +1,85 @@ +import os +from amd.common.hip_shell import execshellcmd + +class BuildRunCommon(): + ''' + In this class insert the build and execution steps for test cases + which are identical across different platforms (amd/nvidia/intel). + ''' + def __init__(self, path, logfile): + self.thistestpath = path + self.logfile = logfile + + def buildtest(self, env = None): + cmdcd = "cd " + self.thistestpath + ";" + cmd_clean = "make clean;" + cmd_build = "make" + cmdexc = cmdcd + cmd_clean + cmd_build + execshellcmd(cmdexc, self.logfile, env) + + def clean(self): + cmdcd = "cd " + self.thistestpath + ";" + cmd_clean = "make clean;" + cmdexc = cmdcd + cmd_clean + execshellcmd(cmdexc, None, None) + + +class BuildRunAmd(BuildRunCommon): + def __init__(self, path, logfile): + BuildRunCommon.__init__(self, path, logfile) + + def buildtest(self): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + ret = BuildRunCommon.buildtest(self) + return ret + + +class BuildRunNvidia(BuildRunCommon): + def __init__(self, path, logfile): + self.hippath = os.path.join(os.getcwd(), "src/amd/conformance/HIP/") + BuildRunCommon.__init__(self, path, logfile) + + def getenvironmentvariables(self): + envtoset = os.environ.copy() + envtoset["HIP_PLATFORM"] = "nvidia" + envtoset["HIP_COMPILER"] = "nvcc" + envtoset["HIP_RUNTIME"] = "cuda" + envtoset["HIP_DIR"] = self.hippath + envtoset["HIP_PATH"] = "../../../build" + return envtoset + + def setupenvironmentfornvcc(self): + cmdcd = "cd " + self.hippath + ";" + envtoset = self.getenvironmentvariables() + cmdsetenv = "rm -Rf build/;mkdir build;cd build;" + cmdbuildinstall = "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install ..;" + cmdbuildinstall += "make install;" + cmdexc = cmdcd + cmdsetenv + cmdbuildinstall + execshellcmd(cmdexc, self.logfile, envtoset) + + def applypatch(self): # To be deleted + cmd = "cd " + self.hippath + ";" + cmd += "patch -p0 < ../../applications/hip_samples/Samples_Patch_4.2.x;" + execshellcmd(cmd, self.logfile, None) + + def buildtest(self): + buildbindirpresent = os.path.isdir(\ + os.path.join(self.hippath, "build/bin")) + buildincludedirpresent = os.path.isdir(\ + os.path.join(self.hippath, "build/include")) + buildinstalldirpresent = os.path.isdir(\ + os.path.join(self.hippath, "build/install")) + if not (buildbindirpresent & buildincludedirpresent & buildinstalldirpresent): + self.setupenvironmentfornvcc() + envtoset = self.getenvironmentvariables() + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + + # Apply Patch. This is temporary and will be removed once Hip Samples changes + # are available in HIP public repository. + self.applypatch() + ret = BuildRunCommon.buildtest(self, envtoset) + return ret diff --git a/src/amd/common/__init__.py b/src/amd/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/common/hip_get_packages.py b/src/amd/common/hip_get_packages.py new file mode 100644 index 0000000..db8bf16 --- /dev/null +++ b/src/amd/common/hip_get_packages.py @@ -0,0 +1,89 @@ +from amd.common.hip_shell import execshellcmd +import os + +# Common class to clone/pull dependent Packages +class HipPackages(): + def __init__(self): + self.cwdAbs = os.getcwd() + self.conformancePath = os.path.join(self.cwdAbs, "src/amd/conformance/") + self.hippath = os.path.join(self.conformancePath, "HIP/") + self.appPath = os.path.join(self.cwdAbs,"src/amd/applications/hip_examples/") + self.examplepath = os.path.join(self.appPath, "HIP-Examples/") + self.mixbenchpath = os.path.join(self.appPath, "mixbench/") + self.gpustrmpath = os.path.join(self.appPath, "GPU-STREAM/") + self.rocclrpath = os.path.join(self.conformancePath, "ROCclr/") + self.openclpath = os.path.join(self.conformancePath, "ROCm-OpenCL-Runtime/") + + def pull_repo(self, logFile, repo, branch, commitId, reponame): + repo_root_path = "" + repo_location = "" + repo_dir = "" + if reponame == "gpu-stream": + repo_root_path = self.gpustrmpath + repo_location = self.appPath + repo_dir = "GPU-STREAM" + elif reponame == "mixbench": + repo_root_path = self.mixbenchpath + repo_location = self.appPath + repo_dir = "mixbench" + elif reponame == "hip_examples": + repo_root_path = self.examplepath + repo_location = self.appPath + repo_dir = "HIP-Examples" + elif reponame == "HIP": + repo_root_path = self.hippath + repo_location = self.conformancePath + repo_dir = "HIP" + elif reponame == "rocclr": + repo_root_path = self.rocclrpath + repo_location = self.conformancePath + repo_dir = "ROCclr" + elif reponame == "opencl": + repo_root_path = self.openclpath + repo_location = self.conformancePath + repo_dir = "ROCm-OpenCL-Runtime" + + if os.path.isdir(repo_root_path) and os.path.isdir(repo_root_path + "/.git"): + print(reponame + " already exist") + # Check if branch and commitId of local repo matches with input branch and commitId + # if not then update local repo + cmd = "cd " + os.path.join(repo_location, repo_dir) + ";" + cmd += "git branch" + currentbranch = execshellcmd(cmd, logFile, None) + currentbranch = currentbranch.replace("* ", "") + cmd = "cd " + os.path.join(repo_location, repo_dir) + ";" + cmd += "git rev-parse HEAD" + currentcommitid = execshellcmd(cmd, logFile, None) + # Check if the local repo is upto date with input configuration + if ((branch == "") or (branch in currentbranch)) and\ + ((commitId == "") or (commitId in currentcommitid)): + print("This repo is up to date with config") + return True + else: + print(reponame + " does not exist") + + # Update the repo + print("Updating: " + reponame) + cmdcd = "cd " + repo_location + ";" + cmdcd += "rm -Rf " + repo_dir + "/;" + cmdPull = "" + if branch == "": #No branch name provided. Clone from main branch. + cmdPull = "git clone " + repo + else: + cmdPull = "git clone -b " + branch + " " + repo + print("Cloning the latest repo from branch = " + branch + "...") + cmdexc = cmdcd + cmdPull + # Clone the latest version from repo + execshellcmd(cmdexc, logFile, None) + isHipPresent = os.path.isdir(repo_root_path) + if not isHipPresent: + return False + else: + print(reponame + " cloned successfully") + # At this point check ensure git head is pointing to commitId + if commitId != "": + cmdcd = "cd " + repo_root_path + ";" + cmdexc = cmdcd + "git reset --hard " + commitId + ";" + execshellcmd(cmdexc, logFile, None) + print("Updating: " + reponame + " completed") + return True diff --git a/src/amd/common/hip_shell.py b/src/amd/common/hip_shell.py new file mode 100644 index 0000000..beeb8eb --- /dev/null +++ b/src/amd/common/hip_shell.py @@ -0,0 +1,23 @@ +import subprocess + +def execshellcmd(cmdexc, logfile, myenv): + proc = subprocess.Popen(cmdexc, shell=True, env=myenv, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + bufsize=0) + proc.wait() + stdoutstr = proc.stdout.read().decode('utf-8') + if logfile != None: + logfile.write(stdoutstr) + return stdoutstr + +def execshellcmd_largedump(cmdexc, logfile, runlog, myenv): + runlog.seek(0) + proc = subprocess.Popen(cmdexc, shell=True, env=myenv, + stdin=subprocess.PIPE, stdout=runlog, stderr=subprocess.STDOUT, + bufsize=0) + proc.wait() + runlog.seek(0) + if logfile != None: + for line in runlog: + logfile.write(line) + runlog.seek(0) diff --git a/src/amd/config_processor.py b/src/amd/config_processor.py new file mode 100644 index 0000000..01edd9c --- /dev/null +++ b/src/amd/config_processor.py @@ -0,0 +1,10 @@ +from amd.AMD import AMDObject + + +class ConfigProcessor(AMDObject): + def __init__(self): + AMDObject.__init__(self) + self.config = None + + def loadConfig(self): + pass diff --git a/src/amd/conformance/__init__.py b/src/amd/conformance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/conformance/conformance_test_classifier.py b/src/amd/conformance/conformance_test_classifier.py new file mode 100644 index 0000000..d302dc6 --- /dev/null +++ b/src/amd/conformance/conformance_test_classifier.py @@ -0,0 +1,11 @@ +from amd.test_classifier import TestClassifier + +from typing import Union + + +class CONFORMANCE(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"conformance": matched_with_names}) diff --git a/src/amd/conformance/hip_dtest.py b/src/amd/conformance/hip_dtest.py new file mode 100644 index 0000000..93953c4 --- /dev/null +++ b/src/amd/conformance/hip_dtest.py @@ -0,0 +1,167 @@ +from amd.TesterRepository import Tester +from amd.Test import HIPTestData, TestResult, Test, HIPBuildData, HIP_PLATFORM +from typing import Union, List +from amd.conformance.conformance_test_classifier import CONFORMANCE +from amd.test_classifier import TestClassifier +from amd.conformance.hip_dtest_build import BuildRunAmd, BuildRunNvidia +from amd.common.hip_get_packages import HipPackages +from amd.common.hip_shell import * + +import os + +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self): + self.hiprepo = "" # Default + self.hipbranch = "" + self.hipcommitId = "" + self.rocclrrepo = "" # Default + self.rocclrbranch = "" + self.rocclrcommitId = "" + self.openclrepo = "" # Default + self.openclbranch = "" + self.openclcommitId = "" + self.logfd = None + self.downloadresult = True + self.buildresult = True + self.buildobj = None + + # Get the GIT repo information from configuration input + def setrepoinfo(self, test_data: HIPBuildData): + if test_data.repos["hip"].repo_url != None: + self.hiprepo = test_data.repos["hip"].repo_url + else: + return False + if test_data.repos["hip"].branch != None: + self.hipbranch = test_data.repos["hip"].branch + if test_data.repos["hip"].commit_id != None: + self.hipcommitId = test_data.repos["hip"].commit_id + if test_data.repos["rocclr"].repo_url != None: + self.rocclrrepo = test_data.repos["rocclr"].repo_url + else: + return False + if test_data.repos["rocclr"].branch != None: + self.rocclrbranch = test_data.repos["rocclr"].branch + if test_data.repos["rocclr"].commit_id != None: + self.rocclrcommitId = test_data.repos["rocclr"].commit_id + if test_data.repos["opencl"].repo_url != None: + self.openclrepo = test_data.repos["opencl"].repo_url + else: + return False + if test_data.repos["opencl"].branch != None: + self.openclbranch = test_data.repos["opencl"].branch + if test_data.repos["opencl"].commit_id != None: + self.openclcommitId = test_data.repos["opencl"].commit_id + return True + + # Download the relevant packages from GIT for this test + def downloadTest(self, log, platform): + ret = HipPackages().pull_repo(log, self.hiprepo, self.hipbranch,\ + self.hipcommitId, "HIP") + if platform == HIP_PLATFORM.amd: + ret &= HipPackages().pull_repo(log, self.rocclrrepo, self.rocclrbranch,\ + self.rocclrcommitId, "rocclr") + ret &= HipPackages().pull_repo(log, self.openclrepo, self.openclbranch,\ + self.openclcommitId, "opencl") + return ret + + # Build Packages + def build_package(self, logFile, platform): + status = True + if platform == HIP_PLATFORM.nvidia: + self.buildobj = BuildRunNvidia(logFile) + elif platform == HIP_PLATFORM.amd: + self.buildobj = BuildRunAmd(logFile) + else: + print("Invalid Platform") + return False + + return self.buildobj.build_package() + + def clean(self): + self.buildobj.clean() + + # Run test + def runtest(self, logFile, testcase): + return self.buildobj.runtest(logFile, testcase) + + # Get ctest info + def get_ctest_list(self): + return self.buildobj.get_all_ctest() + + +# Test HIP Dtest/ +class Hipconformance(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + PrepareTest.__init__(self) + + def getTests(self, get_tests_data: HIPBuildData) -> List[Test]: + # Set repo info + testlist = [] + if get_tests_data.quick: + return testlist + self.logfd = open(get_tests_data.log_location + "/Hipconformance.log", 'w+') + status = self.setrepoinfo(get_tests_data) + if not status: + self.downloadresult = False + return testlist + + # Download repos + self.downloadresult = self.downloadTest(self.logfd, get_tests_data.HIP_PLATFORM) + if not self.downloadresult: + print("Rocm packages download failed!") + return testlist + + # Build Rocclr and Hip + self.buildresult = self.build_package(self.logfd, get_tests_data.HIP_PLATFORM) + if not self.buildresult: + print("Rocm packages build failed!") + return testlist + + # Fetch all the ctests + dtestnamelist = self.get_ctest_list() + + for testname in dtestnamelist: + test = Test() + test.test_name = testname + category = CONFORMANCE() + category.add_matched_with_names() + test.classifiers = [category] + test.tester = self + testlist.append(test) + return testlist + + def get_test_classifiers(self) -> Union[None, List[TestClassifier]]: + category = CONFORMANCE() + category.add_matched_with_names() + return [category] + + def test(self, test_data: HIPTestData): + if not self.downloadresult: + test_data.test_result = TestResult.FAIL + return + + if not self.buildresult: + test_data.test_result = TestResult.FAIL + return + # Build test + print("Running test: " + test_data.test.test_name + "..........") + testcase = test_data.test.test_name.split() + status = None + + with open(test_data.log_location + "/test.log", 'w+') as testLogger: + status = self.runtest(testLogger, testcase) + + if status == "PASSED": + test_data.test_result = TestResult.PASS + elif status == "SKIP": + test_data.test_result = TestResult.SKIP + elif status == "FAILED": + test_data.test_result = TestResult.FAIL + + def clean(self): + PrepareTest.clean(self) + if self.logfd != None: + self.logfd.close() + diff --git a/src/amd/conformance/hip_dtest_build.py b/src/amd/conformance/hip_dtest_build.py new file mode 100644 index 0000000..e7e4d64 --- /dev/null +++ b/src/amd/conformance/hip_dtest_build.py @@ -0,0 +1,253 @@ +import os, glob +import tempfile +import json +import re +from amd.common.hip_shell import execshellcmd_largedump, execshellcmd + +class BuildRunCommon(): + ''' + In this class insert the build and execution steps for test cases + which are identical across different platforms (amd/nvidia/intel). + ''' + def __init__(self, logfile): + self.logfile = logfile + self.hippath = os.path.join(os.getcwd(), "src/amd/conformance/HIP") + self.rclrpath = os.path.join(os.getcwd(), "src/amd/conformance/ROCclr") + self.oclpath = os.path.join(os.getcwd(), "src/amd/conformance/ROCm-OpenCL-Runtime") + + # Fetches all available dtests using ctest + def get_all_ctest(self): + buildpath = self.hippath + "/build/"; + cmdexc = "cd " + self.hippath + "/build;" + cmdexc += "ctest -N --show-only=\"json-v1\";" + with open("ctest.json", "w+") as jsonlog: + execshellcmd_largedump(cmdexc, self.logfile, jsonlog, None) + testlist = [] + with open("ctest.json", "r") as jsonlog: + json_string = jsonlog.read() + ctest_json = json.loads(json_string) + all_tests = ctest_json["tests"] + for test in all_tests: + dtest = "" + for param in test["command"]: + dtest += param + " " + dtest = re.sub(buildpath, "", dtest) + dtest = re.sub("/", ".", dtest) + testlist.append(dtest) + + os.remove("ctest.json") + return testlist + + # Parse the test result + def parsetest(self, log): + log.seek(0) + passed = 0 + failed = 0 + skipped = 0 + for line in log: + if "PASSED" in line: + passed = passed + 1 + elif "FAILED" in line: + failed = failed + 1 + elif "SKIP" in line: + skipped = skipped + 1 + else: + pass + status = None + if failed > 0: + status = "FAILED" + elif passed > 0: + status = "PASSED" + elif skipped > 0: + status = "SKIP" + else: + # No Passed, Failed or Skipped Found + # Result is indeterminate. Hence failing + # this test. + status = "FAILED" + return status + + # Execute the test case + def runtest(self, log, testcase, envtoset): + status = True + testcase[0] = re.sub("\.", "/", testcase[0]) + #Check if the test binary exists + if not os.path.isfile(os.path.join(\ + self.hippath, "build", testcase[0])): + print(testcase[0] + "does not exist") + return "FAILED" + + cmdtest = "" + for param in testcase: + cmdtest += param + " " + print("Executing command = " + cmdtest) + # run test + cmd = "cd " + os.path.join(self.hippath, "build") + ";" + cmd += cmdtest + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, log, runlogdump, envtoset) + status = self.parsetest(runlogdump) + runlogdump.close() + return status + + +class BuildRunAmd(BuildRunCommon): + ''' + In this class insert the build and execution steps specific + for AMD platform. + ''' + def __init__(self, logfile): + BuildRunCommon.__init__(self, logfile) + + # Create the environment variables to set for build and execution + def get_envvar(self): + envtoset = os.environ.copy() + envtoset["ROCclr_DIR"] = self.rclrpath + envtoset["OPENCL_DIR"] = self.oclpath + envtoset["HIP_DIR"] = self.hippath + return envtoset + + # Validate if rocclr build is successful + def validate_rocclr_build(self): + status = True + rocclr_install_inc = os.path.join(os.getcwd(), "src/amd/conformance/opt/rocm/rocclr/include") + rocclr_install_lib = os.path.join(os.getcwd(), "src/amd/conformance/opt/rocm/rocclr/lib") + status &= os.path.isdir(rocclr_install_lib) + status &= os.path.isdir(rocclr_install_inc) + rocclr_binaries = glob.glob(rocclr_install_lib + "/*.a") + if len(rocclr_binaries) > 0: + status &= True + else: + status &= False + return status + + # Validate if HIP build is successful + def validate_hip_build(self): + status = True + hip_install_inc = os.path.join(self.hippath, "build/install/include") + hip_install_lib = os.path.join(self.hippath, "build/install/lib") + hip_install_bin = os.path.join(self.hippath, "build/install/bin") + hip_install_rocclr = os.path.join(self.hippath, "build/install/rocclr") + hip_binaries = glob.glob(hip_install_lib + "/*.so*") + status &= os.path.isdir(hip_install_inc) + status &= os.path.isdir(hip_install_lib) + status &= os.path.isdir(hip_install_bin) + status &= os.path.isdir(hip_install_rocclr) + if len(hip_binaries) > 0: + status &= True + else: + status &= False + return status + + # Build Rocclr and HIP for AMD + def build_package(self, env = None): + buildSuccess = True + # Set environment + envtoset = self.get_envvar() + # build rocclr + cmd = "cd \"$ROCclr_DIR\";" + cmd += "rm -Rf build;" + cmd += "mkdir -p build;cd build;" + cmd += "cmake -DOPENCL_DIR=\"$OPENCL_DIR\" -DCMAKE_INSTALL_PREFIX=\"$ROCclr_DIR/../opt/rocm/rocclr\" ..;" + cmd += "cmake -j;make install;" + cmdexc = cmd + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) + runlogdump.close() + # Validate if rocclr build is successful + buildSuccess = self.validate_rocclr_build() + if buildSuccess == False: + print("rocclr build failed!") + return False + # Build HIP + cmd = "cd \"$HIP_DIR\";" + cmd += "rm -Rf build;" + cmd += "mkdir -p build;cd build;" + cmd += "cmake -DCMAKE_PREFIX_PATH=\"$ROCclr_DIR/build;/opt/rocm;$ROCclr_DIR/../opt/rocm/rocclr\" -DCMAKE_INSTALL_PREFIX=$PWD/install ..;" + cmd += "cmake -j;make install;" + cmd += "make -j8 build_tests" + cmdexc = cmd + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) + runlogdump.close() + # Validate if HIP build is successful + buildSuccess = self.validate_hip_build() + if buildSuccess == False: + print("HIP build failed!") + return False + return True + + # Execute test cases + def runtest(self, log, testcase): + envtoset = self.get_envvar() + return BuildRunCommon.runtest(self, log, testcase, envtoset) + + # Clean the test + def clean(self): + envtoset = self.get_envvar() + cmd = "cd \"$ROCclr_DIR\";" + cmd += "rm -Rf build;rm -Rf $ROCclr_DIR/../opt;" + cmd += "cd \"$HIP_DIR\";" + cmd += "rm -Rf build;" + execshellcmd(cmd, None, envtoset) + +class BuildRunNvidia(BuildRunCommon): + ''' + In this class insert the build and execution steps specific + for NVIDIA platform. + ''' + def __init__(self, logfile): + BuildRunCommon.__init__(self, logfile) + + # Create the environment variables to set for build and execution + def get_envvar(self): + envtoset = os.environ.copy() + envtoset["HIP_PLATFORM"] = "nvidia" + envtoset["HIP_COMPILER"] = "nvcc" + envtoset["HIP_RUNTIME"] = "cuda" + envtoset["HIP_DIR"] = self.hippath + return envtoset + + # Validate if HIP build is successful + def validate_hip_build(self): + status = True + hip_install_inc = os.path.join(self.hippath, "build/install/include") + hip_install_bin = os.path.join(self.hippath, "build/install/bin") + status &= os.path.isdir(hip_install_inc) + status &= os.path.isdir(hip_install_bin) + return status + + # Build for NVIDIA + def build_package(self, env = None): + buildSuccess = True + # Set environment + envtoset = self.get_envvar() + # Build HIP + cmd = "cd \"$HIP_DIR\";" + cmd += "rm -Rf build;" + cmd += "mkdir -p build;cd build;" + cmd += "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install ..;" + cmd += "make install;" + cmd += "make -j8 build_tests" + cmdexc = cmd + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) + runlogdump.close() + # Validate if HIP build is successful + buildSuccess = self.validate_hip_build() + if buildSuccess == False: + print("HIP build failed!") + return False + return True + + # Execute test cases + def runtest(self, log, testcase): + envtoset = self.get_envvar() + return BuildRunCommon.runtest(self, log, testcase, envtoset) + + # Clean the test + def clean(self): + envtoset = self.get_envvar() + cmd = "cd \"$HIP_DIR\";" + cmd += "rm -Rf build;" + execshellcmd(cmd, None, envtoset) diff --git a/src/amd/list_tests.py b/src/amd/list_tests.py new file mode 100644 index 0000000..19a8429 --- /dev/null +++ b/src/amd/list_tests.py @@ -0,0 +1,122 @@ +import tempfile +import typing +from typing import Union, List + +from amd.TesterRepository import TesterRepository, GetTests +from amd.Test import Test, Quick +from amd.test_classifier import TestClassifier + + +def list_tests(quick: bool, cfg): + if not quick: + print("Generating tests, please wait...") + print(); print(); print() + pretty_table_installed = False + try: + from prettytable import PrettyTable + pretty_table_installed = True + except Exception as err: + print("For better UI experience, please pip3 install -r requirements.txt") + + tester_repository: TesterRepository = TesterRepository() + tester_repository.addAllTesters() + + get_tests = GetTests(tester_repository=tester_repository) + get_tests.config = cfg + get_tests.loadConfig() + + with tempfile.TemporaryDirectory() as tmpdirname: + tests = get_tests.get_tests(log_location=tmpdirname, quick=quick) + + field_names = ["Classifiers", "Test"] + if pretty_table_installed: + test_info_table = PrettyTable() + test_info_table.field_names = field_names + else: + print(" | ".join(field_names)) + + for test in tests: + if not pretty_table_installed: + classifiers_s = get_classifiers_s(test) + print(classifiers_s, end=" | ") + print(test.test_name) + else: + classifiers_s = get_classifiers_s(test) + test_info_table.add_row([classifiers_s, test.test_name]) + + if quick: + quick_getTests_testers = list() + all_testers = tester_repository.getTesters() + + for tester in all_testers: + get_tests_t = typing.get_type_hints(tester.getTests) + get_tests_data_t = None + if get_tests_t: + if "get_tests_data" in get_tests_t: + get_tests_data_t = get_tests_t["get_tests_data"] + + if get_tests_data_t and issubclass(get_tests_data_t, Quick): + quick_getTests_testers.append(tester) + + for tester in quick_getTests_testers: + if not pretty_table_installed: + classifiers_s = get_classifiers_s_from_classifier(tester.get_test_classifiers()) + print(classifiers_s, end=" | ") + print("*") + else: + classifiers_s = get_classifiers_s_from_classifier(tester.get_test_classifiers()) + test_info_table.add_row([classifiers_s, "*"]) + + if pretty_table_installed: + print(test_info_table.get_string(title="Test Information")) + + if quick: + print("Note: Run python3 run.py -lst for expanding * tests") + print(""" + 1. If you want to run all tests for amd platform, python3 run.py + If you want to select specific test(s) e.g. for + ----------------------- + | Classifiers | Test | + |---------------------| + | c1:c2:c3 | test1 | + | c4 | test2 | + | c1:c2 | test3 | + | c1:c2:c3 | test4 | + ----------------------- + c1:c2:c3 means c3 is subset of c2 is subset of c1 + e.g. selecting c2 automatically executes tests under c2 and c3 + 2. test1 => python3 run.py -t test1 + 3. tests with python regex test.* => -t test.* + 4. all c3 => -t c3 (i.e. test1, test4) + 5. all c1 => -t c1 (i.e. test1, test3, test4) + 6. all c1:c2 => -t c1:c2 (i.e. test1, test3, test4) + 7. all c1:c2:c3 => -t c1:c2:c3 (i.e. test1, test4) + 8. test2 and test3 => -t test2 test3 + 9. c4 and test4 => -t c4 test4 (i.e. test2, test4) + """) + + +def get_classifiers_s_from_classifier(classifiers: List[TestClassifier]): + classifiers_s = '' + for ix, classifier in enumerate(classifiers): + matched_with_names = classifier.matched_with_names + classifiers_s += ':'.join(get_one_sequence(matched_with_names)) + if ix != len(classifiers) - 1: + classifiers_s += ', ' + return classifiers_s + + +def get_classifiers_s(test: Test): + return get_classifiers_s_from_classifier(test.classifiers) + + +def get_one_sequence(d: dict): + k = list(d.keys())[0] + v = list(d.values())[0] + + if v is None: + return [k] + else: + m = get_one_sequence(v) + m.insert(0, k) + return m diff --git a/src/amd/match_fun_args_call.py b/src/amd/match_fun_args_call.py new file mode 100644 index 0000000..d07e00a --- /dev/null +++ b/src/amd/match_fun_args_call.py @@ -0,0 +1,12 @@ +import typing +from typing import Callable + + +def match_fun_args_call(fun: Callable, args: dict): + fun_args = typing.get_type_hints(fun) + call_args = dict() + for fun_arg_name, fun_arg_t in fun_args.items(): + if fun_arg_name != 'return': + call_args[fun_arg_name] = args[fun_arg_name] + + return fun(**call_args) diff --git a/src/amd/stress/__init__.py b/src/amd/stress/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/targets.py b/src/amd/targets.py new file mode 100644 index 0000000..e532fd9 --- /dev/null +++ b/src/amd/targets.py @@ -0,0 +1,144 @@ +from amd.AMD import AMDObject + + +class Target: + def __init__(self): + pass + + +class AMDTarget(AMDObject, Target): + def __init__(self): + AMDObject.__init__(self) + Target.__init__(self) + + +class IntelTarget(AMDObject, Target): + def __init__(self): + AMDObject.__init__(self) + Target.__init__(self) + + +class NvidiaTarget(AMDObject, Target): + def __init__(self): + AMDObject.__init__(self) + Target.__init__(self) + + +class gfx700(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx701(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx702(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx703(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx704(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx705(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx801(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx802(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx803(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx805(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx810(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx900(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx902(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx904(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx906(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx908(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx909(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx90a(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx1010(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx1011(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx1012(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx1030(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx1031(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) + + +class gfx1032(AMDTarget): + def __init__(self): + AMDTarget.__init__(self) diff --git a/src/amd/test_classifier.py b/src/amd/test_classifier.py new file mode 100644 index 0000000..412518b --- /dev/null +++ b/src/amd/test_classifier.py @@ -0,0 +1,14 @@ +from amd.AMD import AMDObject + +from typing import Union + + +class TestClassifier(AMDObject): + def __init__(self): + self.matched_with_names = dict() + AMDObject.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + if matched_with_names is None: + matched_with_names = dict() + self.matched_with_names.update(matched_with_names) diff --git a/src/amd/test_selector.py b/src/amd/test_selector.py new file mode 100644 index 0000000..a5183da --- /dev/null +++ b/src/amd/test_selector.py @@ -0,0 +1,122 @@ +from amd.AMD import AMDObject +from amd.TesterRepository import TesterRepository, Test, GetTests +from typing import Union, List, Dict + +import re + + +class TestSelector(GetTests): + def __init__(self, tester_repository: TesterRepository): + AMDObject.__init__(self) + GetTests.__init__(self, tester_repository=tester_repository) + + def to_select_this_test(self, test: Test, test_name_regexes): + test_name = test.test_name + also_matched_with_test_names = test.also_matched_with_test_names + applicable_for_target = test.applicable_for_target + classifiers = test.classifiers + + select_this_test = False + + if test_name_regexes is None: + select_this_test = True + else: + for test_name_regex in test_name_regexes: + test_name_regex: Union[str, List[str]] = test_name_regex + + if type(test_name_regex) == str: + asked_classifiers: List[str] = test_name_regex.split(':') + else: # type(test_name_regex) == list: + asked_classifiers: List[str] = test_name_regex + + in_dicts: List[Dict] = list() + ret_is_sequence_in_dicts_obj = ret_is_sequence_in_dicts() + if classifiers: + for classifier in classifiers: + in_dicts.append(classifier.matched_with_names) + is_sequence_in_dicts(sequence=asked_classifiers, in_dicts=in_dicts, + ret_is_sequence_in_dicts_obj=ret_is_sequence_in_dicts_obj) + + if not classifiers or ret_is_sequence_in_dicts_obj.initial_matching: + if classifiers and ret_is_sequence_in_dicts_obj.last_also_matching: + select_this_test = True + else: + if re.findall(asked_classifiers[-1].lower(), test_name.lower()): + select_this_test = True + if also_matched_with_test_names: + for also_matched_with_test_name in also_matched_with_test_names: + if re.findall(asked_classifiers[-1].lower(), also_matched_with_test_name.lower()): + select_this_test = True + return select_this_test + + def select_tests(self, log_location: str) -> List[Test]: + config = self.config + tests = list() + run_tests = config.run_tests + if type(run_tests) == str: + test_name_regexes = [run_tests] + elif type(run_tests) == list: + test_name_regexes = run_tests + else: + test_name_regexes = None + + for test_of_tester in self.get_tests(log_location=log_location, quick=True): + select_this_test = self.to_select_this_test(test=test_of_tester, test_name_regexes=test_name_regexes) + if select_this_test: + tests.append(test_of_tester) + + if not tests: + for test_of_tester in self.get_tests(log_location=log_location, quick=False): + select_this_test = self.to_select_this_test(test=test_of_tester, test_name_regexes=test_name_regexes) + if select_this_test: + tests.append(test_of_tester) + + return tests + + +class ret_is_sequence_in_dicts: + def __init__(self): + self.initial_matching = False + self.last_also_matching = False + + +def next_item_process(item, sequence: List, till_matched: List): + if till_matched == sequence: + return + if sequence[len(till_matched)] == item: + till_matched.append(item) + else: + till_matched.clear() + + +def is_sequence_in_dict(sequence: List, in_dict: Dict, ret_is_sequence_in_dict_obj: ret_is_sequence_in_dicts, till_matched: List): + for k, v in in_dict.items(): + next_item_process(item=k, sequence=sequence, till_matched=till_matched) + if sequence[:-1] == till_matched: + ret_is_sequence_in_dict_obj.initial_matching = True + if sequence == till_matched: + ret_is_sequence_in_dict_obj.last_also_matching = True + ret_is_sequence_in_dict_obj.initial_matching = True + else: + if type(v) == dict: + is_sequence_in_dict(sequence=sequence, in_dict=v, ret_is_sequence_in_dict_obj=ret_is_sequence_in_dict_obj, till_matched=till_matched) + + +def is_sequence_in_dicts(sequence: List, in_dicts: List[Dict], ret_is_sequence_in_dicts_obj: ret_is_sequence_in_dicts): + initial_matching = False + last_also_matching = False + for in_dict in in_dicts: + ret_is_sequence_in_dict_obj = ret_is_sequence_in_dicts() + is_sequence_in_dict(sequence=sequence, in_dict=in_dict, ret_is_sequence_in_dict_obj=ret_is_sequence_in_dict_obj, till_matched=list()) + seq_initial_matching = ret_is_sequence_in_dict_obj.initial_matching + seq_last_also_matching = ret_is_sequence_in_dict_obj.last_also_matching + + if not initial_matching: + initial_matching = seq_initial_matching + if not last_also_matching: + last_also_matching = seq_last_also_matching + if seq_initial_matching and seq_last_also_matching: + break + + ret_is_sequence_in_dicts_obj.initial_matching = initial_matching + ret_is_sequence_in_dicts_obj.last_also_matching = last_also_matching diff --git a/src/amd/version.py b/src/amd/version.py new file mode 100644 index 0000000..73bbcec --- /dev/null +++ b/src/amd/version.py @@ -0,0 +1,3 @@ +MAJOR = 1 +MINOR = 0 +PATCH = 0 \ No newline at end of file diff --git a/temp/CTEST_parse.py b/temp/CTEST_parse.py new file mode 100644 index 0000000..a70a778 --- /dev/null +++ b/temp/CTEST_parse.py @@ -0,0 +1,116 @@ +#from parser.test.parser import Parser +import re +from parser.test.prerequisites_failed import prerequisite_failed + + +class CTESTParser:#(Parser): + + def __init__(self, buildNo=None): + self.buildNo = None + self.template = None + #Parser.__init__(self, buildNo) + + def parse(self, fileName=None, text=None): + if fileName and not text: + with open(fileName, "r") as fp: + text = fp.read() + + if not text: + raise AttributeError("filename and text empty") + + n_testsuites=self.getTotalTestSuites(text) + n_testcases=self.getTotalTestCases(text) + + if n_testcases is None: + return prerequisite_failed() + + final_status=self.getFinalStatus(text) + #total_time=self.getTotalTime(text) + + p2 = re.compile(r'(\d+) tests? from (\w+\/?\d*)$') + p1 = re.compile(r'(\d+) tests? from (\w+\/?\d*)(\,)') + p4 = re.compile(r'(\d+) tests? from (\w+\/?\d*) \(\d+ ms total\)') + testSuiteInfo=self.getTestSuitesInfo(text) + + global_testInfo = {} + global_testInfo["testSuiteInfo"]=testSuiteInfo + global_testInfo["nTestSuites"]=n_testsuites + global_testInfo["nTestCases"]=n_testcases + global_testInfo["status"]=final_status + #global_testInfo["total_time"]=str(total_time) + return global_testInfo + + + def getFinalStatus(self, text): + if '100% tests passed' not in text: + return False + else: + return True + + def getTestSuitesInfo(self, text): + testsuite_to_testcaes = {} + testsuite_to_testcaes["default"]=self.getTestSuiteInfo(text) + return testsuite_to_testcaes + + def getTestSuiteInfo(self, text): + from result.test_result import Multi_Test_Status + + # regex = r'Test\s+#\d+:\s+(\S+)\s+\.+(.+)\s*(\d+)\.(\d+) sec' + # regex = r'Test\s+#\d+:\s+(\S+)\s+\.+\s*([^\d\W]+)\s*(\d+)\.(\d+) sec' + regex = r'Test\s+#\d+:\s+(\S+)\s+\.+\s*([^\d]+)\s*(\d+)\.(\d+) sec' + m = re.findall(regex,text) + + """ + 34/229 Test #34: thrust.hip.merge_by_key .................................***Exception: Other171.94 sec + [('thrust.hip.merge_by_key', '***Exception: Other', '171', '94')] + [('hip.device_api', 'Passed', '0', '20')] + + 2/9 Test #6: hipcub.BlockLoadStore ............. Passed 10.72 sec + [('hipcub.BlockLoadStore', 'Passed ', '10', '72')] + + Start 72: directed_tests/g++/hipMalloc.tst + 72/335 Test #72: directed_tests/g++/hipMalloc.tst ........***Skipped 0.00 sec + """ + + testcases_info={} + + for testcase in m: + testcase_status = testcase[1].strip() + testcase_name = testcase[0].strip() + time = testcase[2]+'.'+testcase[3] + testcases_info[testcase_name]={} + if testcase_status=="Passed": + testcases_info[testcase_name]['status']=True + elif "Skipped" in testcase_status: + testcases_info[testcase_name]['status'] = Multi_Test_Status.SKIP + else: + testcases_info[testcase_name]['status']=False + testcases_info[testcase_name]['time']=time + return testcases_info + + + def getTotalTestSuites(self, text): + #testcase_testsuite_regex = r" Running (\d+) tests? from (\d+) test cases?." + #[(m.start(), m.end()) for m in re.finditer(testcase_testsuite_regex, text)] + #m=re.match(testcase_testsuite_regex, text) + #return m.groups()[1] + return 1 + + def getTotalTestCases(self, text): + #testcase_testsuite_regex = r" Running (\d+) tests? from (\d+) test cases?." + #m = re.match(testcase_testsuite_regex, text) + #return m.groups()[0] + m = re.findall(r'(\d+) tests failed out of (\d+)', text) + if m: + return m[0][1] + else: + return None + + def getTotalTime(self, text): + "Total Test time (real) = 100.39 sec" + m = re.findall(r'Total Test time \(real\) = (\d+)\.(\d+) sec', text) + return m[0][0]+'.'+m[0][1] + + +if __name__ == '__main__': + pass diff --git a/temp/temp.py b/temp/temp.py new file mode 100644 index 0000000..4538e2e --- /dev/null +++ b/temp/temp.py @@ -0,0 +1,31 @@ +from amd.TesterRepository import Tester +from amd.Test import HIPTestData, TestResult, Test, HIPBuildData +from typing import List + + +class Temp(Tester): + def __init__(self): + Tester.__init__(self) + + def getTests(self, get_tests_data: HIPBuildData) -> List[Test]: + print("In getTests starts") + print(get_tests_data.log_location) + print(get_tests_data.user_password) + print(get_tests_data.repos) + print(get_tests_data.HIP_PLATFORM) + + test1 = Test() + test1.test_name = "temp1" + test1.tester = self + + test2 = Test() + test2.test_name = "temp2" + test2.tester = self + + print("In getTests end") + + return [test1, test2] + + def test(self, test_data: HIPTestData): + print("called for {test_name}".format(test_name=test_data.test.test_name)) + test_data.test_result = TestResult.PASS From de068cee3fe761531596be36bca2e9e0576f72fe Mon Sep 17 00:00:00 2001 From: Rupam Chetia Date: Thu, 15 Jul 2021 13:05:18 +0530 Subject: [PATCH 02/18] Implemented the following in this patch: 1. Fixed Dtest report folder. 2. Implemented code review comments. Change-Id: I410dcae499cd78aa0756668c1f1fd4d73f785b6c --- .../applications/hip_examples/hip_examples.py | 201 ++++-------------- .../applications/hip_samples/hip_samples.py | 94 +++----- src/amd/conformance/hip_dtest.py | 4 +- src/amd/conformance/hip_dtest_build.py | 65 ++---- 4 files changed, 95 insertions(+), 269 deletions(-) diff --git a/src/amd/applications/hip_examples/hip_examples.py b/src/amd/applications/hip_examples/hip_examples.py index e334373..724514d 100644 --- a/src/amd/applications/hip_examples/hip_examples.py +++ b/src/amd/applications/hip_examples/hip_examples.py @@ -515,51 +515,6 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.FAIL -# Test openmp-helloworld/ -# class Openmp_helloworld(Tester, PrepareTest): - # def __init__(self): - # Tester.__init__(self) - # self.cwd = os.getcwd() - # PrepareTest.__init__(self, "openmp-helloworld/", self.cwd) - - # def getTests(self) -> List[Test]: - # test = Test() - # test.test_name = self.__class__.__name__ - # intro = MINIAPP() - # intro.add_matched_with_names() - # test.classifiers = [intro] - # test.tester = self - # return [test] - - # def clean(self): - # PrepareTest.clean(self, "openmp-helloworld") - - # def test(self, test_data: HIPTestData): - # print("=============== openmp-helloworld test ===============") - # # Set repo info - # isrepocfgvalid = self.set_hipex_repoinfo(test_data) - # if not isrepocfgvalid: - # test_data.test_result = TestResult.ERROR - # return - # # Create the log directory - # resultLogDir = test_data.log_location - # testid = "openmp-helloworld" - # with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: - # res = self.download_hipexample(testLogger) - # if not res: - # test_data.test_result = TestResult.FAIL - # return - # res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) - # if not res: - # test_data.test_result = TestResult.FAIL - # return - - # if True == self.runtest(testLogger, testid): - # test_data.test_result = TestResult.PASS - # else: - # test_data.test_result = TestResult.FAIL - - # Test rodinia_3.0/hip/bfs/ class Bfs(Tester, PrepareTest): def __init__(self): @@ -653,11 +608,13 @@ def test(self, test_data: HIPTestData): # Test rodinia_3.0/hip/dwt2d/ +# This test is skipped for nvidia class Dwt2d(Tester, PrepareTest): def __init__(self): Tester.__init__(self) self.cwd = os.getcwd() PrepareTest.__init__(self, "rodinia_3.0/hip/dwt2d/", self.cwd) + self.platform = None def getTests(self) -> List[Test]: test = Test() @@ -669,11 +626,15 @@ def getTests(self) -> List[Test]: return [test] def clean(self): - PrepareTest.clean(self, "rodinia_3.dwt2d") + if self.platform != HIP_PLATFORM.nvidia: + PrepareTest.clean(self, "rodinia_3.dwt2d") def test(self, test_data: HIPTestData): print("=============== rodinia_3.0/hip/dwt2d test start ===============") - if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + self.platform = test_data.HIP_PLATFORM + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + test_data.test_result = TestResult.SKIP + else: # Set repo info isrepocfgvalid = self.set_hipex_repoinfo(test_data) if not isrepocfgvalid: @@ -697,8 +658,7 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.PASS else: test_data.test_result = TestResult.FAIL - else: - test_data.test_result = TestResult.SKIP + # Test rodinia_3.0/hip/gaussian/ class Gaussian(Tester, PrepareTest): @@ -1161,11 +1121,13 @@ def test(self, test_data: HIPTestData): # Test rodinia_3.0/hip/particlefilter/ +# This test is skipped for amd. class Particlefilter(Tester, PrepareTest): def __init__(self): Tester.__init__(self) self.cwd = os.getcwd() PrepareTest.__init__(self, "rodinia_3.0/hip/particlefilter/", self.cwd) + self.platform = None def getTests(self) -> List[Test]: test = Test() @@ -1177,11 +1139,15 @@ def getTests(self) -> List[Test]: return [test] def clean(self): - PrepareTest.clean(self, "rodinia_3.particlefilter") + if self.platform != HIP_PLATFORM.amd: + PrepareTest.clean(self, "rodinia_3.particlefilter") def test(self, test_data: HIPTestData): print("=============== rodinia_3.0/hip/particlefilter test start ===============") - if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + self.platform = test_data.HIP_PLATFORM + if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + test_data.test_result = TestResult.SKIP + else: # Set repo info isrepocfgvalid = self.set_hipex_repoinfo(test_data) if not isrepocfgvalid: @@ -1205,8 +1171,6 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.PASS else: test_data.test_result = TestResult.FAIL - else: - test_data.test_result = TestResult.SKIP # Test rodinia_3.0/hip/pathfinder/ @@ -1347,58 +1311,14 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.FAIL -# Test rodinia_3.0/hip/b+tree/ -# class Btree(Tester, PrepareTest): - # def __init__(self): - # Tester.__init__(self) - # self.cwd = os.getcwd() - # PrepareTest.__init__(self, "rodinia_3.0/hip/b+tree/", self.cwd) - - # def getTests(self) -> List[Test]: - # test = Test() - # test.test_name = self.__class__.__name__ - # intro = PERFORMANCE() - # intro.add_matched_with_names() - # test.classifiers = [intro] - # test.tester = self - # return [test] - - # def clean(self): - # PrepareTest.clean(self, "rodinia_3.b+tree") - - # def test(self, test_data: HIPTestData): - # print("=============== rodinia_3.0/hip/b+tree test start ===============") - # # Set repo info - # isrepocfgvalid = self.set_hipex_repoinfo(test_data) - # if not isrepocfgvalid: - # test_data.test_result = TestResult.ERROR - # return - # # Create the log directory - # resultLogDir = test_data.log_location - # testid = "rodinia_3.b+tree" - # with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: - # res = self.download_hipexample(testLogger) - # if not res: - # test_data.test_result = TestResult.FAIL - # return - - # res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) - # if not res: - # test_data.test_result = TestResult.FAIL - # return - - # if True == self.runtest(testLogger, testid): - # test_data.test_result = TestResult.PASS - # else: - # test_data.test_result = TestResult.FAIL - - # Test rodinia_3.0/hip/backprop/ +# This test is skipped for amd class Backprop(Tester, PrepareTest): def __init__(self): Tester.__init__(self) self.cwd = os.getcwd() PrepareTest.__init__(self, "rodinia_3.0/hip/backprop/", self.cwd) + self.platform = None def getTests(self) -> List[Test]: test = Test() @@ -1410,11 +1330,15 @@ def getTests(self) -> List[Test]: return [test] def clean(self): - PrepareTest.clean(self, "rodinia_3.backprop") + if self.platform != HIP_PLATFORM.amd: + PrepareTest.clean(self, "rodinia_3.backprop") def test(self, test_data: HIPTestData): print("=============== rodinia_3.0/hip/backprop test start ===============") - if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + self.platform = test_data.HIP_PLATFORM + if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + test_data.test_result = TestResult.SKIP + else: # Set repo info isrepocfgvalid = self.set_hipex_repoinfo(test_data) if not isrepocfgvalid: @@ -1438,8 +1362,7 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.PASS else: test_data.test_result = TestResult.FAIL - else: - test_data.test_result = TestResult.SKIP + # Test HIP-Examples-Applications/BinomialOption/ class BinomialOption(Tester, PrepareTest): @@ -2039,58 +1962,14 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.FAIL -# Test GPU-STREAM Float -# class GpuStreamFloat(Tester, PrepareTest): - # def __init__(self): - # Tester.__init__(self) - # self.cwd = os.getcwd() - # PrepareTest.__init__(self, "../GPU-STREAM/", self.cwd) - - # def getTests(self) -> List[Test]: - # test = Test() - # test.test_name = self.__class__.__name__ - # intro = PERFORMANCE() - # intro.add_matched_with_names() - # test.classifiers = [intro] - # test.tester = self - # return [test] - - # def clean(self): - # PrepareTest.clean(self, "GPU-STREAM-FLOAT") - - # def test(self, test_data: HIPTestData): - # print("=============== GPU-STREAM Float start ===============") - # # Set repo info - # isrepocfgvalid = self.set_gpustr_repoinfo(test_data) - # if not isrepocfgvalid: - # test_data.test_result = TestResult.ERROR - # return - # # Create the log directory - # resultLogDir = test_data.log_location - # testid = "GPU-STREAM-FLOAT" - # with open(resultLogDir + "/" + testid + ".log", 'w+') as testLogger: - # res = self.download_gpustream(testLogger) - # if not res: - # test_data.test_result = TestResult.FAIL - # return - - # res = self.buildtest(testLogger, test_data.HIP_PLATFORM, testid) - # if not res: - # test_data.test_result = TestResult.FAIL - # return - - # if True == self.runtest(testLogger, testid): - # test_data.test_result = TestResult.PASS - # else: - # test_data.test_result = TestResult.FAIL - - # Test mixbench-hip-alt +# This test is skipped for nvidia class MixBenchAlt(Tester, PrepareTest): def __init__(self): Tester.__init__(self) self.cwd = os.getcwd() PrepareTest.__init__(self, "../mixbench/mixbench-hip/", self.cwd) + self.platform = None def getTests(self) -> List[Test]: test = Test() @@ -2102,11 +1981,15 @@ def getTests(self) -> List[Test]: return [test] def clean(self): - PrepareTest.clean(self, "mixbench-hip-alt") + if self.platform != HIP_PLATFORM.nvidia: + PrepareTest.clean(self, "mixbench-hip-alt") def test(self, test_data: HIPTestData): print("=============== mixbench-hip-alt start ===============") - if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + self.platform = test_data.HIP_PLATFORM + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + test_data.test_result = TestResult.SKIP + else: # Set repo info isrepocfgvalid = self.set_mixben_repoinfo(test_data) if not isrepocfgvalid: @@ -2130,15 +2013,16 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.PASS else: test_data.test_result = TestResult.FAIL - else: - test_data.test_result = TestResult.SKIP + # Test mixbench-hip-ro +# This test is skipped for nvidia class MixBenchRO(Tester, PrepareTest): def __init__(self): Tester.__init__(self) self.cwd = os.getcwd() PrepareTest.__init__(self, "../mixbench/mixbench-hip/", self.cwd) + self.platform = None def getTests(self) -> List[Test]: test = Test() @@ -2150,11 +2034,15 @@ def getTests(self) -> List[Test]: return [test] def clean(self): - PrepareTest.clean(self, "mixbench-hip-ro") + if self.platform != HIP_PLATFORM.nvidia: + PrepareTest.clean(self, "mixbench-hip-ro") def test(self, test_data: HIPTestData): print("=============== GPU-STREAM Float start ===============") - if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + self.platform = test_data.HIP_PLATFORM + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + test_data.test_result = TestResult.SKIP + else: # Set repo info isrepocfgvalid = self.set_mixben_repoinfo(test_data) if not isrepocfgvalid: @@ -2178,7 +2066,4 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.PASS else: test_data.test_result = TestResult.FAIL - else: - test_data.test_result = TestResult.SKIP - diff --git a/src/amd/applications/hip_samples/hip_samples.py b/src/amd/applications/hip_samples/hip_samples.py index 5f3fcc9..a99dfbc 100644 --- a/src/amd/applications/hip_samples/hip_samples.py +++ b/src/amd/applications/hip_samples/hip_samples.py @@ -236,6 +236,7 @@ def test(self, test_data: HIPTestData): # Test samples/0_Intro/module_api/launchKernelHcc.hip.out +# This test is skipped for NVIDIA class ModuleApiLaunchKernelHcc(Tester, PrepareTest, LogParser): def __init__(self): Tester.__init__(self) @@ -243,6 +244,7 @@ def __init__(self): PrepareTest.__init__(self, "samples/0_Intro/module_api/", "launchKernelHcc.hip.out", self.cwd) LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + self.platform = None def getTests(self) -> List[Test]: test = Test() @@ -254,12 +256,16 @@ def getTests(self) -> List[Test]: return [test] def clean(self): - PrepareTest.clean(self) + if self.platform != HIP_PLATFORM.nvidia: + PrepareTest.clean(self) def test(self, test_data: HIPTestData): print("=============== ModuleApiLaunchKernelHcc Test ===============") # Set repo info - if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + self.platform = test_data.HIP_PLATFORM + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + test_data.test_result = TestResult.SKIP + else: isrepocfgvalid = self.setrepoinfo(test_data) if not isrepocfgvalid: test_data.test_result = TestResult.ERROR @@ -280,8 +286,6 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.PASS else: test_data.test_result = TestResult.FAIL - else: - test_data.test_result = TestResult.SKIP # Test samples/0_Intro/module_api/runKernel.hip.out @@ -469,12 +473,14 @@ def test(self, test_data: HIPTestData): # Test samples/2_Cookbook/10_inline_asm +# This test is skipped for NVIDIA class InlineAsm(Tester, PrepareTest, LogParser): def __init__(self): Tester.__init__(self) self.cwd = os.getcwd() PrepareTest.__init__(self, "samples/2_Cookbook/10_inline_asm/", "inline_asm", self.cwd) LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + self.platform = None def getTests(self) -> List[Test]: test = Test() @@ -486,12 +492,16 @@ def getTests(self) -> List[Test]: return [test] def clean(self): - PrepareTest.clean(self) + if self.platform != HIP_PLATFORM.nvidia: + PrepareTest.clean(self) def test(self, test_data: HIPTestData): print("=============== InlineAsm Test ===============") # Set repo info - if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + self.platform = test_data.HIP_PLATFORM + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + test_data.test_result = TestResult.SKIP + else: isrepocfgvalid = self.setrepoinfo(test_data) if not isrepocfgvalid: test_data.test_result = TestResult.ERROR @@ -512,11 +522,10 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.PASS else: test_data.test_result = TestResult.FAIL - else: - test_data.test_result = TestResult.SKIP # Test samples/2_Cookbook/16_assembly_to_executable +# This test is skipped for nvidia class SquareAsm(Tester, PrepareTest, LogParser): def __init__(self): Tester.__init__(self) @@ -524,6 +533,7 @@ def __init__(self): PrepareTest.__init__(self, "samples/2_Cookbook/16_assembly_to_executable/", "square_asm.out", self.cwd) LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + self.platform = None def getTests(self) -> List[Test]: test = Test() @@ -535,11 +545,15 @@ def getTests(self) -> List[Test]: return [test] def clean(self): - PrepareTest.clean(self) + if self.platform != HIP_PLATFORM.nvidia: + PrepareTest.clean(self) def test(self, test_data: HIPTestData): print("=============== SquareAsm Test ===============") - if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + self.platform = test_data.HIP_PLATFORM + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + test_data.test_result = TestResult.SKIP + else: # Set repo info isrepocfgvalid = self.setrepoinfo(test_data) if not isrepocfgvalid: @@ -561,8 +575,6 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.PASS else: test_data.test_result = TestResult.FAIL - else: - test_data.test_result = TestResult.SKIP # Test samples/2_Cookbook/11_texture_driver @@ -656,51 +668,6 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.FAIL -# Test samples/2_Cookbook/14_gpu_arch -# class GpuArch(Tester, PrepareTest, LogParser): - # def __init__(self): - # Tester.__init__(self) - # self.cwd = os.getcwd() - # PrepareTest.__init__(self, "samples/2_Cookbook/14_gpu_arch/", "gpuarch", self.cwd) - # LogParser.__init__(self, 1, "success") # Number of expected PASSED - - # def getTests(self) -> List[Test]: - # test = Test() - # test.test_name = self.__class__.__name__ - # cookbook = COOKBOOK() - # cookbook.add_matched_with_names() - # test.classifiers = [cookbook] - # test.tester = self - # return [test] - - # def clean(self): - # PrepareTest.clean(self) - - # def test(self, test_data: HIPTestData): - # print("=============== GpuArch Test ===============") - # # Set repo info - # isrepocfgvalid = self.setrepoinfo(test_data) - # if not isrepocfgvalid: - # test_data.test_result = TestResult.ERROR - # return - # # Create the log directory - # resultLogDir = test_data.log_location - # with open(resultLogDir + '/GpuArch.log', 'w+') as testLogger: - # res = self.downloadtest(testLogger) - # if not res: - # test_data.test_result = TestResult.FAIL - # return - # res = self.buildtest(testLogger, test_data.HIP_PLATFORM) - # if not res: - # test_data.test_result = TestResult.FAIL - # return - # self.runtest(testLogger) - # if self.numOfExpPassed == self.parse_common(self.testExecOutput): - # test_data.test_result = TestResult.PASS - # else: - # test_data.test_result = TestResult.FAIL - - # Test samples/2_Cookbook/4_shfl class Shfl(Tester, PrepareTest, LogParser): def __init__(self): @@ -792,6 +759,7 @@ def test(self, test_data: HIPTestData): # Test samples/2_Cookbook/17_llvm_ir_to_executable +# This test is skipped for nvidia class Square_Ir(Tester, PrepareTest, LogParser): def __init__(self): Tester.__init__(self) @@ -799,6 +767,7 @@ def __init__(self): PrepareTest.__init__(self, "samples/2_Cookbook/17_llvm_ir_to_executable/", "square_ir.out", self.cwd) LogParser.__init__(self, 1, "PASSED") # Number of expected PASSED + self.platform = None def getTests(self) -> List[Test]: test = Test() @@ -810,11 +779,15 @@ def getTests(self) -> List[Test]: return [test] def clean(self): - PrepareTest.clean(self) + if self.platform != HIP_PLATFORM.nvidia: + PrepareTest.clean(self) def test(self, test_data: HIPTestData): print("=============== Square_Ir Test ===============") - if test_data.HIP_PLATFORM == HIP_PLATFORM.amd: + self.platform = test_data.HIP_PLATFORM + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + test_data.test_result = TestResult.SKIP + else: # Set repo info isrepocfgvalid = self.setrepoinfo(test_data) if not isrepocfgvalid: @@ -836,8 +809,7 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.PASS else: test_data.test_result = TestResult.FAIL - else: - test_data.test_result = TestResult.SKIP + # Test samples/2_Cookbook/5_2dshfl class Dshfl(Tester, PrepareTest, LogParser): diff --git a/src/amd/conformance/hip_dtest.py b/src/amd/conformance/hip_dtest.py index 93953c4..3689e4c 100644 --- a/src/amd/conformance/hip_dtest.py +++ b/src/amd/conformance/hip_dtest.py @@ -113,12 +113,14 @@ def getTests(self, get_tests_data: HIPBuildData) -> List[Test]: print("Rocm packages download failed!") return testlist + print("Building HIP package ...") # Build Rocclr and Hip self.buildresult = self.build_package(self.logfd, get_tests_data.HIP_PLATFORM) if not self.buildresult: print("Rocm packages build failed!") return testlist + print("Building HIP package completed") # Fetch all the ctests dtestnamelist = self.get_ctest_list() @@ -147,7 +149,7 @@ def test(self, test_data: HIPTestData): return # Build test print("Running test: " + test_data.test.test_name + "..........") - testcase = test_data.test.test_name.split() + testcase = test_data.test.test_name status = None with open(test_data.log_location + "/test.log", 'w+') as testLogger: diff --git a/src/amd/conformance/hip_dtest_build.py b/src/amd/conformance/hip_dtest_build.py index e7e4d64..52e5927 100644 --- a/src/amd/conformance/hip_dtest_build.py +++ b/src/amd/conformance/hip_dtest_build.py @@ -17,73 +17,40 @@ def __init__(self, logfile): # Fetches all available dtests using ctest def get_all_ctest(self): - buildpath = self.hippath + "/build/"; cmdexc = "cd " + self.hippath + "/build;" - cmdexc += "ctest -N --show-only=\"json-v1\";" - with open("ctest.json", "w+") as jsonlog: - execshellcmd_largedump(cmdexc, self.logfile, jsonlog, None) + cmdexc += "ctest -N;" + with open("ctest.txt", "w+") as ctestlog: + execshellcmd_largedump(cmdexc, self.logfile, ctestlog, None) testlist = [] - with open("ctest.json", "r") as jsonlog: - json_string = jsonlog.read() - ctest_json = json.loads(json_string) - all_tests = ctest_json["tests"] - for test in all_tests: - dtest = "" - for param in test["command"]: - dtest += param + " " - dtest = re.sub(buildpath, "", dtest) - dtest = re.sub("/", ".", dtest) - testlist.append(dtest) - - os.remove("ctest.json") + with open("ctest.txt", "r") as ctestlog: + for test in ctestlog: + if re.search("Test *#\d*:", test) != None: + dtest = re.sub("Test *#\d*: ", "", test) + dtest = re.sub("/", ".", dtest) + dtest = dtest.lstrip() + dtest = dtest.rstrip() + testlist.append(dtest) + os.remove("ctest.txt") return testlist # Parse the test result def parsetest(self, log): log.seek(0) - passed = 0 - failed = 0 - skipped = 0 - for line in log: - if "PASSED" in line: - passed = passed + 1 - elif "FAILED" in line: - failed = failed + 1 - elif "SKIP" in line: - skipped = skipped + 1 - else: - pass + text = log.read() status = None - if failed > 0: - status = "FAILED" - elif passed > 0: + if re.search("100% tests passed", text) != None: status = "PASSED" - elif skipped > 0: - status = "SKIP" else: - # No Passed, Failed or Skipped Found - # Result is indeterminate. Hence failing - # this test. status = "FAILED" return status # Execute the test case def runtest(self, log, testcase, envtoset): - status = True - testcase[0] = re.sub("\.", "/", testcase[0]) - #Check if the test binary exists - if not os.path.isfile(os.path.join(\ - self.hippath, "build", testcase[0])): - print(testcase[0] + "does not exist") - return "FAILED" - - cmdtest = "" - for param in testcase: - cmdtest += param + " " + cmdtest = "ctest -R " + testcase print("Executing command = " + cmdtest) # run test cmd = "cd " + os.path.join(self.hippath, "build") + ";" - cmd += cmdtest + cmd += cmdtest + ";" runlogdump = tempfile.TemporaryFile("w+") execshellcmd_largedump(cmd, log, runlogdump, envtoset) status = self.parsetest(runlogdump) From 699a3710e7e642db7dbbff5d769ba39431b0a9f4 Mon Sep 17 00:00:00 2001 From: HRISHIKESH THULA Date: Thu, 15 Jul 2021 19:35:52 +0530 Subject: [PATCH 03/18] examples + copyright Change-Id: Ieb07ad95008566ad5592e74b685b56ad2fa6fd9f --- cfg.py | 20 +++ examples/cfg.py | 99 +++++++++++++++ examples/examples/0.py | 39 ++++++ examples/examples/1.py | 55 +++++++++ examples/examples/2.py | 71 +++++++++++ examples/examples/3.py | 37 ++++++ examples/examples/__init__.py | 0 examples/run.py | 78 ++++++++++++ run.py | 20 +++ src/amd/AMD.py | 20 +++ src/amd/Test.py | 20 +++ src/amd/TesterRepository.py | 22 +++- src/amd/TestersExecutor.py | 20 +++ .../application_test_classifier.py | 20 +++ .../applications/hip_examples/hip_examples.py | 20 +++ .../hip_examples/hip_examples_build.py | 20 +++ .../hip_examples/hip_examples_parser.py | 22 +++- .../applications/hip_samples/hip_samples.py | 20 +++ .../hip_samples/hip_samples_build.py | 20 +++ src/amd/common/hip_get_packages.py | 20 +++ src/amd/common/hip_shell.py | 20 +++ src/amd/config_processor.py | 20 +++ .../conformance_test_classifier.py | 20 +++ src/amd/conformance/hip_dtest.py | 20 +++ src/amd/conformance/hip_dtest_build.py | 20 +++ src/amd/list_tests.py | 38 ++++-- src/amd/match_fun_args_call.py | 20 +++ src/amd/targets.py | 20 +++ src/amd/test_classifier.py | 20 +++ src/amd/test_selector.py | 20 +++ src/amd/version.py | 22 +++- temp/CTEST_parse.py | 116 ------------------ temp/temp.py | 31 ----- 33 files changed, 872 insertions(+), 158 deletions(-) create mode 100644 examples/cfg.py create mode 100644 examples/examples/0.py create mode 100644 examples/examples/1.py create mode 100644 examples/examples/2.py create mode 100644 examples/examples/3.py create mode 100644 examples/examples/__init__.py create mode 100644 examples/run.py delete mode 100644 temp/CTEST_parse.py delete mode 100644 temp/temp.py diff --git a/cfg.py b/cfg.py index 715b5c7..f7641fb 100644 --- a/cfg.py +++ b/cfg.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + version = "1.0.0" user_password = "AH64_uh1" diff --git a/examples/cfg.py b/examples/cfg.py new file mode 100644 index 0000000..f7641fb --- /dev/null +++ b/examples/cfg.py @@ -0,0 +1,99 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +version = "1.0.0" + +user_password = "AH64_uh1" +log_location = None + +# None/amd/nvidia +HIP_PLATFORM = None + +# None/0/1/2/3 +Optimization_Level = None + +# None/0/1/2/4 +HIPCC_VERBOSE = None + +# None/cuda directory +CUDA_PATH = None + +# None/rocm directory +ROCM_PATH = None + +# None/offload target +build_for_target = None + +# -I. +# None/List of paths +includes_path = None + +# -l +# None/List of -l +link_libs = None + +# L. +# None/list of -L +link_libs_path = None + +# None/(test_name/test_suite/regex OR list of test_names/test_suites/regex) +# e.g.1 run_tests = ["bitextract", "matrixtranspose"] +# e.g.2 run_tests = "hip_samples" +# e.g.3 run_tests = ".*samples.*" + +# If ts: test suite, tc: test case +# e.g.4 run_tests = [[ts1,tc1], [ts1,tc2], [tc3], [ts2,tc4], [ts3]] +# e.g.4 run_tests = [ts1:tc1, ts1:tc2, tc3, ts2:tc4, ts3] +run_tests = None + + +branch = "rocm-4.2.x" +repos = { + "hip_examples": { + "repo_url": "https://github.com/ROCm-Developer-Tools/HIP-Examples", + "branch": branch, + # "commit_id": "" + }, + "hip": { + "repo_url": "https://github.com/ROCm-Developer-Tools/HIP", + "branch": branch, + # "commit_id": "" + }, + "mixbench": { + "repo_url": "https://github.com/ekondis/mixbench.git", + "branch": None, + # "commit_id": "" + }, + "gpu_stream": { + "repo_url": "https://github.com/UoB-HPC/GPU-STREAM.git", + "branch": None, + # "commit_id": "" + }, + "rocclr": { + "repo_url": "https://github.com/ROCm-Developer-Tools/ROCclr.git", + "branch": branch, + # "commit_id": "" + }, + "opencl": { + "repo_url": "https://github.com/RadeonOpenCompute/ROCm-OpenCL-Runtime.git", + "branch": branch, + # "commit_id": "" + } +} diff --git a/examples/examples/0.py b/examples/examples/0.py new file mode 100644 index 0000000..4cb0963 --- /dev/null +++ b/examples/examples/0.py @@ -0,0 +1,39 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from pathlib import Path + +from amd.TesterRepository import Tester +from amd.Test import TestData, TestResult + + +class Test0(Tester): + """ + Simple test case, + Which tests whether rocm is installed + """ + def __init__(self): + Tester.__init__(self) + + def test(self, test_data: TestData): + if Path("/opt/rocm").exists(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL diff --git a/examples/examples/1.py b/examples/examples/1.py new file mode 100644 index 0000000..82a2a0d --- /dev/null +++ b/examples/examples/1.py @@ -0,0 +1,55 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from amd.TesterRepository import Tester +from amd.Test import TestData, TestResult, Test +from amd.test_classifier import TestClassifier + +from typing import List, Union + + +class C1(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"c1": matched_with_names}) + + +class Test1(Tester): + """ + This assigns classifier to test, so it can be invoked with c1, c1:test1, test1 + """ + def __init__(self): + Tester.__init__(self) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + c1 = C1() + c1.add_matched_with_names() + test.classifiers = [c1] + test.tester = self + + return [test] + + def test(self, test_data: TestData): + print("called for {test_name}".format(test_name=test_data.test.test_name)) + test_data.test_result = TestResult.PASS diff --git a/examples/examples/2.py b/examples/examples/2.py new file mode 100644 index 0000000..2ed69b0 --- /dev/null +++ b/examples/examples/2.py @@ -0,0 +1,71 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from amd.TesterRepository import Tester +from amd.Test import TestData, TestResult, Test +from amd.test_classifier import TestClassifier + +from typing import List, Union + + +class C1(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"c1": matched_with_names}) + + +class C2(C1): + def __init__(self): + C1.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + C1.add_matched_with_names(self, {"c2": matched_with_names}) + + +class Test2(Tester): + """ + This assigns classifier to test, so it can be invoked with c1, c1:test2_1, test2_1 like 1.py. + In addition to that, it adds test2_2, and assigns classifier c2, which is subset of c1 + """ + def __init__(self): + Tester.__init__(self) + + def getTests(self) -> List[Test]: + test2_1 = Test() + test2_1.test_name = "test2_1" + c1 = C1() + c1.add_matched_with_names() + test2_1.classifiers = [c1] + test2_1.tester = self + + test2_2 = Test() + test2_2.test_name = "test2_2" + c2 = C2() + c2.add_matched_with_names() + test2_2.classifiers = [c2] + test2_2.tester = self + + return [test2_1, test2_2] + + def test(self, test_data: TestData): + print("called for {test_name}".format(test_name=test_data.test.test_name)) + test_data.test_result = TestResult.PASS diff --git a/examples/examples/3.py b/examples/examples/3.py new file mode 100644 index 0000000..3391447 --- /dev/null +++ b/examples/examples/3.py @@ -0,0 +1,37 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from amd.TesterRepository import Tester +from amd.Test import HIPTestData, TestResult, Test +from amd.test_classifier import TestClassifier + +from typing import List, Union + + +class PlatformTest(Tester): + """ + This is example for accessing hip platform to test with + """ + def __init__(self): + Tester.__init__(self) + + def test(self, test_data: HIPTestData): + print(test_data.HIP_PLATFORM) + test_data.test_result = TestResult.PASS diff --git a/examples/examples/__init__.py b/examples/examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/run.py b/examples/run.py new file mode 100644 index 0000000..59375bd --- /dev/null +++ b/examples/run.py @@ -0,0 +1,78 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import sys +import os +import argparse + +if __name__ == "__main__": + sys.path.append(os.path.join(os.path.dirname(__file__), "..", "src")) + + +from amd.TestersExecutor import TestersExecutor +from amd.TesterRepository import TesterRepository +from amd.list_tests import list_tests +import cfg +import examples + + +def parse_args(tester_repository): + parser = argparse.ArgumentParser() + parser.add_argument('--platform', help="On which hip_platform to test? amd/nvidia/ default:amd") + parser.add_argument('-t', '--tests', nargs='+', + # required=True, + # choices=TESTS_CHOICES, + metavar='', help="Test name/Regex/Category/Category:*/List of those separated by space") + parser.add_argument('-lst', '--list_tests', default=False, action='store_true', help="List all tests") + parser.add_argument('-lstq', '--list_tests_quick', default=False, action='store_true', help="List all tests quickly, Warning: This may not list some tests which are time consuming to generate, and only category:* will be displayed for them, use -lst for listing all tests") + + args = parser.parse_args() + + if args.platform: + cfg.HIP_PLATFORM = args.platform + + if args.list_tests: + list_tests(quick=False, cfg=cfg, tester_repository=tester_repository) + return False + + if args.list_tests_quick: + list_tests(quick=True, cfg=cfg, tester_repository=tester_repository) + return False + + if args.tests: + cfg.run_tests = args.tests + + return True + + +def main(): + tester_repository = TesterRepository() + tester_repository.clearTesterFrom() + tester_repository.addTesterFrom(pkgs=[examples]) + tester_repository.addAllTesters() + tester_executor: TestersExecutor = TestersExecutor() + tester_executor.config = cfg + + if parse_args(tester_repository=tester_repository): + tester_executor.executeTests(tester_repository=tester_repository) + + +if __name__ == "__main__": + main() diff --git a/run.py b/run.py index ba3a71e..94ee600 100644 --- a/run.py +++ b/run.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import sys import os import argparse diff --git a/src/amd/AMD.py b/src/amd/AMD.py index d9738fe..b5216a2 100644 --- a/src/amd/AMD.py +++ b/src/amd/AMD.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + class AMDObject: def __init__(self): pass diff --git a/src/amd/Test.py b/src/amd/Test.py index e49139d..53300b8 100644 --- a/src/amd/Test.py +++ b/src/amd/Test.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from typing import List, Union, Set, Dict from amd.AMD import AMDObject from amd.targets import Target diff --git a/src/amd/TesterRepository.py b/src/amd/TesterRepository.py index b66ce09..f63d044 100644 --- a/src/amd/TesterRepository.py +++ b/src/amd/TesterRepository.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.Test import Test, TestData, GetTestsData, LogLocation, Quick from amd.test_classifier import TestClassifier from amd.AMD import AMDObject @@ -15,7 +35,7 @@ class Tester(AMDObject): def __init__(self): AMDObject.__init__(self) - def getTests(self, get_tests_data: GetTestsData = None) -> List[Test]: + def getTests(self, get_tests_data: GetTestsData) -> List[Test]: test = Test() test.tester = self test.test_name = self.__class__.__name__ diff --git a/src/amd/TestersExecutor.py b/src/amd/TestersExecutor.py index 949b49e..693a86c 100644 --- a/src/amd/TestersExecutor.py +++ b/src/amd/TestersExecutor.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.TesterRepository import TesterRepository, Tester, Test from amd.test_selector import TestSelector from amd.config_processor import ConfigProcessor diff --git a/src/amd/applications/application_test_classifier.py b/src/amd/applications/application_test_classifier.py index a149f4c..0c52148 100644 --- a/src/amd/applications/application_test_classifier.py +++ b/src/amd/applications/application_test_classifier.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.test_classifier import TestClassifier from typing import Union diff --git a/src/amd/applications/hip_examples/hip_examples.py b/src/amd/applications/hip_examples/hip_examples.py index 724514d..56bde47 100644 --- a/src/amd/applications/hip_examples/hip_examples.py +++ b/src/amd/applications/hip_examples/hip_examples.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.TesterRepository import Tester, Test, TestData from amd.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List diff --git a/src/amd/applications/hip_examples/hip_examples_build.py b/src/amd/applications/hip_examples/hip_examples_build.py index 52aadce..f1db14e 100644 --- a/src/amd/applications/hip_examples/hip_examples_build.py +++ b/src/amd/applications/hip_examples/hip_examples_build.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import os import tempfile diff --git a/src/amd/applications/hip_examples/hip_examples_parser.py b/src/amd/applications/hip_examples/hip_examples_parser.py index 7e2af99..24f8b0a 100644 --- a/src/amd/applications/hip_examples/hip_examples_parser.py +++ b/src/amd/applications/hip_examples/hip_examples_parser.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import re class Hip_examples_parser: @@ -244,4 +264,4 @@ def hip_examples_applications(self, logfile): if (pass_pattern1 in line) or (pass_pattern2 in line)\ or (pass_pattern3 in line) or (pass_pattern4 in line): return "Failed" - return "Passed" \ No newline at end of file + return "Passed" diff --git a/src/amd/applications/hip_samples/hip_samples.py b/src/amd/applications/hip_samples/hip_samples.py index a99dfbc..3462429 100644 --- a/src/amd/applications/hip_samples/hip_samples.py +++ b/src/amd/applications/hip_samples/hip_samples.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.TesterRepository import Tester, Test, TestData from amd.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List diff --git a/src/amd/applications/hip_samples/hip_samples_build.py b/src/amd/applications/hip_samples/hip_samples_build.py index e4e6308..1f9d669 100644 --- a/src/amd/applications/hip_samples/hip_samples_build.py +++ b/src/amd/applications/hip_samples/hip_samples_build.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import os from amd.common.hip_shell import execshellcmd diff --git a/src/amd/common/hip_get_packages.py b/src/amd/common/hip_get_packages.py index db8bf16..3f508d6 100644 --- a/src/amd/common/hip_get_packages.py +++ b/src/amd/common/hip_get_packages.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.common.hip_shell import execshellcmd import os diff --git a/src/amd/common/hip_shell.py b/src/amd/common/hip_shell.py index beeb8eb..b523193 100644 --- a/src/amd/common/hip_shell.py +++ b/src/amd/common/hip_shell.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import subprocess def execshellcmd(cmdexc, logfile, myenv): diff --git a/src/amd/config_processor.py b/src/amd/config_processor.py index 01edd9c..a484940 100644 --- a/src/amd/config_processor.py +++ b/src/amd/config_processor.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.AMD import AMDObject diff --git a/src/amd/conformance/conformance_test_classifier.py b/src/amd/conformance/conformance_test_classifier.py index d302dc6..1720ca4 100644 --- a/src/amd/conformance/conformance_test_classifier.py +++ b/src/amd/conformance/conformance_test_classifier.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.test_classifier import TestClassifier from typing import Union diff --git a/src/amd/conformance/hip_dtest.py b/src/amd/conformance/hip_dtest.py index 3689e4c..884fbdc 100644 --- a/src/amd/conformance/hip_dtest.py +++ b/src/amd/conformance/hip_dtest.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.TesterRepository import Tester from amd.Test import HIPTestData, TestResult, Test, HIPBuildData, HIP_PLATFORM from typing import Union, List diff --git a/src/amd/conformance/hip_dtest_build.py b/src/amd/conformance/hip_dtest_build.py index 52e5927..057e469 100644 --- a/src/amd/conformance/hip_dtest_build.py +++ b/src/amd/conformance/hip_dtest_build.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import os, glob import tempfile import json diff --git a/src/amd/list_tests.py b/src/amd/list_tests.py index 19a8429..7addf5a 100644 --- a/src/amd/list_tests.py +++ b/src/amd/list_tests.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import tempfile import typing from typing import Union, List @@ -7,7 +27,7 @@ from amd.test_classifier import TestClassifier -def list_tests(quick: bool, cfg): +def list_tests(quick: bool, cfg, tester_repository=None): if not quick: print("Generating tests, please wait...") print(); print(); print() @@ -18,8 +38,9 @@ def list_tests(quick: bool, cfg): except Exception as err: print("For better UI experience, please pip3 install -r requirements.txt") - tester_repository: TesterRepository = TesterRepository() - tester_repository.addAllTesters() + if tester_repository is None: + tester_repository: TesterRepository = TesterRepository() + tester_repository.addAllTesters() get_tests = GetTests(tester_repository=tester_repository) get_tests.config = cfg @@ -98,11 +119,12 @@ def list_tests(quick: bool, cfg): def get_classifiers_s_from_classifier(classifiers: List[TestClassifier]): classifiers_s = '' - for ix, classifier in enumerate(classifiers): - matched_with_names = classifier.matched_with_names - classifiers_s += ':'.join(get_one_sequence(matched_with_names)) - if ix != len(classifiers) - 1: - classifiers_s += ', ' + if classifiers: + for ix, classifier in enumerate(classifiers): + matched_with_names = classifier.matched_with_names + classifiers_s += ':'.join(get_one_sequence(matched_with_names)) + if ix != len(classifiers) - 1: + classifiers_s += ', ' return classifiers_s diff --git a/src/amd/match_fun_args_call.py b/src/amd/match_fun_args_call.py index d07e00a..2962d3b 100644 --- a/src/amd/match_fun_args_call.py +++ b/src/amd/match_fun_args_call.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import typing from typing import Callable diff --git a/src/amd/targets.py b/src/amd/targets.py index e532fd9..b9ff1b2 100644 --- a/src/amd/targets.py +++ b/src/amd/targets.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.AMD import AMDObject diff --git a/src/amd/test_classifier.py b/src/amd/test_classifier.py index 412518b..7814239 100644 --- a/src/amd/test_classifier.py +++ b/src/amd/test_classifier.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.AMD import AMDObject from typing import Union diff --git a/src/amd/test_selector.py b/src/amd/test_selector.py index a5183da..6887232 100644 --- a/src/amd/test_selector.py +++ b/src/amd/test_selector.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + from amd.AMD import AMDObject from amd.TesterRepository import TesterRepository, Test, GetTests from typing import Union, List, Dict diff --git a/src/amd/version.py b/src/amd/version.py index 73bbcec..edf9734 100644 --- a/src/amd/version.py +++ b/src/amd/version.py @@ -1,3 +1,23 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + MAJOR = 1 MINOR = 0 -PATCH = 0 \ No newline at end of file +PATCH = 0 diff --git a/temp/CTEST_parse.py b/temp/CTEST_parse.py deleted file mode 100644 index a70a778..0000000 --- a/temp/CTEST_parse.py +++ /dev/null @@ -1,116 +0,0 @@ -#from parser.test.parser import Parser -import re -from parser.test.prerequisites_failed import prerequisite_failed - - -class CTESTParser:#(Parser): - - def __init__(self, buildNo=None): - self.buildNo = None - self.template = None - #Parser.__init__(self, buildNo) - - def parse(self, fileName=None, text=None): - if fileName and not text: - with open(fileName, "r") as fp: - text = fp.read() - - if not text: - raise AttributeError("filename and text empty") - - n_testsuites=self.getTotalTestSuites(text) - n_testcases=self.getTotalTestCases(text) - - if n_testcases is None: - return prerequisite_failed() - - final_status=self.getFinalStatus(text) - #total_time=self.getTotalTime(text) - - p2 = re.compile(r'(\d+) tests? from (\w+\/?\d*)$') - p1 = re.compile(r'(\d+) tests? from (\w+\/?\d*)(\,)') - p4 = re.compile(r'(\d+) tests? from (\w+\/?\d*) \(\d+ ms total\)') - testSuiteInfo=self.getTestSuitesInfo(text) - - global_testInfo = {} - global_testInfo["testSuiteInfo"]=testSuiteInfo - global_testInfo["nTestSuites"]=n_testsuites - global_testInfo["nTestCases"]=n_testcases - global_testInfo["status"]=final_status - #global_testInfo["total_time"]=str(total_time) - return global_testInfo - - - def getFinalStatus(self, text): - if '100% tests passed' not in text: - return False - else: - return True - - def getTestSuitesInfo(self, text): - testsuite_to_testcaes = {} - testsuite_to_testcaes["default"]=self.getTestSuiteInfo(text) - return testsuite_to_testcaes - - def getTestSuiteInfo(self, text): - from result.test_result import Multi_Test_Status - - # regex = r'Test\s+#\d+:\s+(\S+)\s+\.+(.+)\s*(\d+)\.(\d+) sec' - # regex = r'Test\s+#\d+:\s+(\S+)\s+\.+\s*([^\d\W]+)\s*(\d+)\.(\d+) sec' - regex = r'Test\s+#\d+:\s+(\S+)\s+\.+\s*([^\d]+)\s*(\d+)\.(\d+) sec' - m = re.findall(regex,text) - - """ - 34/229 Test #34: thrust.hip.merge_by_key .................................***Exception: Other171.94 sec - [('thrust.hip.merge_by_key', '***Exception: Other', '171', '94')] - [('hip.device_api', 'Passed', '0', '20')] - - 2/9 Test #6: hipcub.BlockLoadStore ............. Passed 10.72 sec - [('hipcub.BlockLoadStore', 'Passed ', '10', '72')] - - Start 72: directed_tests/g++/hipMalloc.tst - 72/335 Test #72: directed_tests/g++/hipMalloc.tst ........***Skipped 0.00 sec - """ - - testcases_info={} - - for testcase in m: - testcase_status = testcase[1].strip() - testcase_name = testcase[0].strip() - time = testcase[2]+'.'+testcase[3] - testcases_info[testcase_name]={} - if testcase_status=="Passed": - testcases_info[testcase_name]['status']=True - elif "Skipped" in testcase_status: - testcases_info[testcase_name]['status'] = Multi_Test_Status.SKIP - else: - testcases_info[testcase_name]['status']=False - testcases_info[testcase_name]['time']=time - return testcases_info - - - def getTotalTestSuites(self, text): - #testcase_testsuite_regex = r" Running (\d+) tests? from (\d+) test cases?." - #[(m.start(), m.end()) for m in re.finditer(testcase_testsuite_regex, text)] - #m=re.match(testcase_testsuite_regex, text) - #return m.groups()[1] - return 1 - - def getTotalTestCases(self, text): - #testcase_testsuite_regex = r" Running (\d+) tests? from (\d+) test cases?." - #m = re.match(testcase_testsuite_regex, text) - #return m.groups()[0] - m = re.findall(r'(\d+) tests failed out of (\d+)', text) - if m: - return m[0][1] - else: - return None - - def getTotalTime(self, text): - "Total Test time (real) = 100.39 sec" - m = re.findall(r'Total Test time \(real\) = (\d+)\.(\d+) sec', text) - return m[0][0]+'.'+m[0][1] - - -if __name__ == '__main__': - pass diff --git a/temp/temp.py b/temp/temp.py deleted file mode 100644 index 4538e2e..0000000 --- a/temp/temp.py +++ /dev/null @@ -1,31 +0,0 @@ -from amd.TesterRepository import Tester -from amd.Test import HIPTestData, TestResult, Test, HIPBuildData -from typing import List - - -class Temp(Tester): - def __init__(self): - Tester.__init__(self) - - def getTests(self, get_tests_data: HIPBuildData) -> List[Test]: - print("In getTests starts") - print(get_tests_data.log_location) - print(get_tests_data.user_password) - print(get_tests_data.repos) - print(get_tests_data.HIP_PLATFORM) - - test1 = Test() - test1.test_name = "temp1" - test1.tester = self - - test2 = Test() - test2.test_name = "temp2" - test2.tester = self - - print("In getTests end") - - return [test1, test2] - - def test(self, test_data: HIPTestData): - print("called for {test_name}".format(test_name=test_data.test.test_name)) - test_data.test_result = TestResult.PASS From ab90debbee222ab23214481d66b257afe6f30d32 Mon Sep 17 00:00:00 2001 From: HRISHIKESH THULA Date: Fri, 16 Jul 2021 15:38:25 +0530 Subject: [PATCH 04/18] .log.d + example-4 + cuda Change-Id: Ia603bc84c1000d9f73dba21c10d91c2e1fc143b7 --- .gitignore | 5 ++++ examples/cfg.py | 3 +++ examples/examples/4.py | 52 ++++++++++++++++++++++++++++++++++++++ src/amd/TestersExecutor.py | 48 ++++++++++++++++++++++++++++++----- 4 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 .gitignore create mode 100644 examples/examples/4.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80fe6f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +.vscode +*.swp +*.pyc +__pycache__ diff --git a/examples/cfg.py b/examples/cfg.py index f7641fb..fcd1b47 100644 --- a/examples/cfg.py +++ b/examples/cfg.py @@ -53,6 +53,9 @@ # None/list of -L link_libs_path = None +# User Input for example 4.py +user_value = 5 + # None/(test_name/test_suite/regex OR list of test_names/test_suites/regex) # e.g.1 run_tests = ["bitextract", "matrixtranspose"] # e.g.2 run_tests = "hip_samples" diff --git a/examples/examples/4.py b/examples/examples/4.py new file mode 100644 index 0000000..05b1049 --- /dev/null +++ b/examples/examples/4.py @@ -0,0 +1,52 @@ +# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from amd.TesterRepository import Tester +from amd.Test import TestResult, TestData, LogLocation, UserAccess + + +class MyTestData(TestData, LogLocation, UserAccess): + """ + Custom Test Input Data + """ + def __init__(self): + self.user_value = None + UserAccess.__init__(self) + LogLocation.__init__(self) + TestData.__init__(self) + + def loadConfig(self): + UserAccess.loadConfig(self) + self.user_value = self.config.user_value + + +class CustomTestData(Tester): + """ + This is example for accessing creating custom test data, here of type MyTestData + that can be constructed using user input values + """ + def __init__(self): + Tester.__init__(self) + + def test(self, test_data: MyTestData): + if test_data.user_value == 5: + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL diff --git a/src/amd/TestersExecutor.py b/src/amd/TestersExecutor.py index 693a86c..bbce13e 100644 --- a/src/amd/TestersExecutor.py +++ b/src/amd/TestersExecutor.py @@ -97,7 +97,7 @@ def executeTests(self, tester_repository: TesterRepository=None): test_data.config = config test_data.loadConfig() test_data.test = test - test_data.log_location = os.path.join(timestamped_log_location, test.test_name.lower()) + test_data.log_location = os.path.join(timestamped_log_location, test.test_name.lower() + ".log.d") os.makedirs(test_data.log_location, exist_ok=True) try: @@ -108,7 +108,7 @@ def executeTests(self, tester_repository: TesterRepository=None): tests_status[test] = test_data.test_result tests_logs[test] = test_data.log_location - tests_relative_logs[test] = os.path.join(relative_timestamped_log_location, test.test_name.lower()) + tests_relative_logs[test] = os.path.join(relative_timestamped_log_location, test.test_name.lower() + ".log.d") print("Completed Test: {test_name} with result {result}".format(test_name=test.test_name.lower(), result=test_data.test_result.name)) for test in tests: @@ -136,6 +136,14 @@ def executeTests(self, tester_repository: TesterRepository=None): rocm_agents: Union[None, List[str]] = get_rocm_agents() except Exception as error: rocm_agents = None + try: + cuda_gpus: Union[None, List[str]] = get_cuda_gpus() + except Exception as error: + cuda_gpus = None + try: + cuda_rt_version: str = get_cuda_rt_version() + except Exception as error: + cuda_rt_version = None passed_tests = get_passed_tests(tests_status=tests_status) failed_tests = get_failed_tests(tests_status=tests_status) @@ -147,7 +155,7 @@ def executeTests(self, tester_repository: TesterRepository=None): # ### prettytable field_names = ["Test Name", "Result", "Log"] system_info_field_names = ["Component", "Information"] - test_cnt_field_names = ["PASS", "FAIL", "ERROR", "SKIP"] + test_cnt_field_names = ["TOTAL", "PASS", "FAIL", "ERROR", "SKIP"] try: from prettytable import PrettyTable if tests_status: @@ -166,7 +174,7 @@ def executeTests(self, tester_repository: TesterRepository=None): logger.info('\n' + summary_table.get_string(title="Summary")) - test_cnt_table.add_row([str(len(passed_tests)), str(len(failed_tests)), str(len(errored_tests)), str(len(skipped_tests))]) + test_cnt_table.add_row([str(len(tests_status)), str(len(passed_tests)), str(len(failed_tests)), str(len(errored_tests)), str(len(skipped_tests))]) logger.info('\n' + test_cnt_table.get_string(title="Metrics")) system_info_table = PrettyTable() @@ -174,6 +182,8 @@ def executeTests(self, tester_repository: TesterRepository=None): system_info_table.add_row(["OS", (os_name if os_name else "Can't get OS name!") + " " + (os_version if os_version else "Can't get OS version")]) system_info_table.add_row(["ROCm Agents", ", ".join(rocm_agents) if rocm_agents else "Can't get ROCm agents"]) system_info_table.add_row(["/opt/rocm version", opt_rocm_version if opt_rocm_version else "Can't get /opt/rocm version"]) + system_info_table.add_row(["CUDA RT Version", cuda_rt_version if cuda_rt_version else "Can't get CUDA RT Version"]) + system_info_table.add_row(["CUDA GPUs", ", ".join(cuda_gpus) if cuda_gpus else "Can't get CUDA GPUs"]) logger.info('\n' + system_info_table.get_string(title="System Information")) except Exception as error: logger.warning("For better UI experience, please pip3 install -r requirements.txt") @@ -192,13 +202,15 @@ def executeTests(self, tester_repository: TesterRepository=None): logger.info("********Metrics********") logger.info(" | ".join(test_cnt_field_names)) - logger.info(str(len(passed_tests)) + " | " + str(len(failed_tests)) + " | " + str(len(errored_tests)) + " | " + str(len(skipped_tests))) + logger.info(str(len(tests_status)) + " | " + str(len(passed_tests)) + " | " + str(len(failed_tests)) + " | " + str(len(errored_tests)) + " | " + str(len(skipped_tests))) logger.info("********System Information********") logger.info(" | ".join(system_info_field_names)) logger.info("OS" + " | " + (os_name if os_name else "Can't get OS name!") + " " + (os_version if os_version else "Can't get OS version")) logger.info("ROCm Agents" + " | " + ", ".join(rocm_agents) if rocm_agents else "Can't get ROCm agents") logger.info("/opt/rocm version" + " | " + opt_rocm_version if opt_rocm_version else "Can't get /opt/rocm version") + logger.info("CUDA RT Version" + " | " + cuda_rt_version if cuda_rt_version else "Can't get CUDA RT Version") + logger.info("CUDA GPUs" + " | " + ", ".join(cuda_gpus) if cuda_gpus else "Can't get CUDA GPUs") logger.info("Note, All log locations are relative to {log_location}".format(log_location=log_location)) @@ -210,15 +222,17 @@ def executeTests(self, tester_repository: TesterRepository=None): test_root["status"] = test_status.name test_root["log_location"] = tests_logs[test] + json_root["num_total"] = len(tests_status) json_root["num_passed"] = len(passed_tests) json_root["num_failed"] = len(failed_tests) json_root["num_errored"] = len(errored_tests) json_root["num_skipped"] = len(skipped_tests) - json_root["opt_rocm_version"] = opt_rocm_version json_root["os_name"] = os_name json_root["os_version"] = os_version json_root["rocm_agents"] = rocm_agents + json_root["cuda_gpus"] = cuda_gpus + json_root["cuda_rt_version"] = cuda_rt_version json_root["start_datetime"] = start_datetime.strftime("%Y_%m_%d_%H_%M_%S") json_root["end_datetime"] = end_datetime.strftime("%Y_%m_%d_%H_%M_%S") json_root["selected_test_filter"] = selected_test_filter @@ -272,6 +286,28 @@ def get_opt_rocm_version() -> str: return opt_rocm_version +def get_cuda_rt_version() -> str: + cuda_rt_version = None + with open('/usr/local/cuda/version.json', "r") as f: + data = json.load(f) + try: + cuda_rt_version = data['cuda_cudart']['version'] + except: + pass + return cuda_rt_version + + +def get_cuda_gpus() -> List[str]: + cmd = "nvidia-smi --query-gpu=name --format=csv,noheader" + o, e = subprocess.Popen([cmd], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True).communicate() + o = o.decode('utf-8') + if o: + cuda_gpus = o.strip('\n').split('\n') + else: + cuda_gpus = None + return cuda_gpus + + def get_passed_tests(tests_status: Dict[Test, TestResult]) -> Dict[Test, TestResult]: return get_status_filtered_tests(tests_status=tests_status, status=TestResult.PASS) From 20669a3ee73f9b9a3bfd2756e712608df179e84f Mon Sep 17 00:00:00 2001 From: Mohan Kumar Mithur Date: Tue, 20 Jul 2021 03:18:31 -0400 Subject: [PATCH 05/18] Updated README.md Change-Id: I4a6b197779a2a6dae53eebf5b7a63ef92ac41257 --- README.md | 194 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 157 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index bf6cccc..1d8097c 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,188 @@ -Test Repository -# testsuite +# HIP Testsuite -Right now the test set is composed of four high-level directories as git submodules: -- HIP-Examples (official HIP examples) +## Description +HIP Testsuite is a suite to test HIP on different platforms. At high level, testsuite helps in doing below activities +- Download required projects for tests (as mentioned in cfg.py) +- Build all/specific tests for specified platform (as provided through command line) +- Run all/specific tests +- Consolidate the reports and logs +Testsuite predominantly uses python scripts that can run with python 3.x + +## Download and execute HIP-Testsuite + +### Preconditions + +- Python 3.6 or above is installed on system +- Appropriate Rocm or Nvcc drivers are installed on system +- Git (version 2.17.1 or more) should be installed +- CMake (version 3.4 or more) should be installed + +### Steps +• Clone the TestSuite repository +``` + +$ cd hip-testsuite ``` -Since we use submodules, to clone all the submodules, you'll need to use +• One time dependency installation ``` -git clone https://github.com/mohanmithur/testsuite -cd testsuite -git submodule update --init --recursive +$ pip3 install -r requirements.txt ``` -To update the submodules to the latest: -Enter the submodule directory: +• Verify "cfg.py" contents to make sure that desired values are present +To provide input configurations like log location, platform, repos etc. "cfg.py" is used. Please check 6.3.1 for further details. + +• Use “run.py” to run the test +To start executing the tests "run.py" is used. User inputs like test filters need to be provided via command line input to "run.py". Platform name (e.g. nvidia/amd) can also be provided via the command line input. +Some of the command line for run.py are briefly mentioned here. For detailed description of these, please refer to 6.3.2 and 6.3.3. +-h: To show all options ``` -cd projB/projA +$ python3 run.py -h ``` -Pull the repo from you project A (will not update the git status of your parent, project B): +-lstq: To list all tests quickly ``` -git pull origin master +$ python3 run.py -lstq ``` -Go back to the root directory & check update: +-lst: To list all the test (Note: platform should be specified if running for other than AMD platform) ``` -cd .. -git status +$ python3 run.py -lst ``` -If the submodule updated before, it will show something like below: +Run all the tests on amd platform ``` -# Not currently on any branch. -# Changed but not updated: -# (use "git add ..." to update what will be committed) -# (use "git checkout -- ..." to discard changes in working directory) -# -# modified: projB/projA (new commits) -# +$ python3 run.py ``` -Then, commit the update: +Select the test with -t to run specific +on amd platform ``` -git add projB/projA -git commit -m "projA submodule updated" +$ python3 run.py -t ``` -(From https://stackoverflow.com/questions/8191299/update-a-submodule-to-the-latest-commit) +on Nvidia platform +``` +$ python3 run.py -t --platform nvidia +``` +Some examples to execute a category of tests +``` +$ python3 run.py -t conformance --platform amd +$ python3 run.py -t samples --platform amd +$ python3 run.py -t examples --platform amd +``` + +## Details for working with HIP-Testsuite -# Running the tests on JLSE +### Testsuite configuration -To run on gen9 on JLSE: +Test suite considers contents of “cfg.py” for configurable items. There are two types of information in cfg.py. +- Default value for some parameters that can be over-ridden through command line while running test suite +- Information like test repository/branch info etc. +| Configuration | Explanation | +| --------------- | ------------- | +| log_location | We can provide the location, in python string, where log files should be dumped. | +| | If none is provided, then logs are dumped under "report" folder in the current working directory, which is "hip-testsuite/". | +| user_password | If any password needs to be set during the execution of tests, then it can be | +| | provided using this parameter. Currently set to None. | +| HIP_PLATFORM | Platform value (amd/nvidia) can also be passed to the test using this parameter. | +| repos | This is a Python dictionary structure to provide information on all the repos required for tests. | +| | - "repo_url" should contain the GIT URL of the repository to clone. | +| | - "branch" should contain the branch name | +| | - "commit_id" should contain the GIT Commit ID to test. | +| |• If no branch is to be specified, then keep branch = None. | +| |• If no commit_id is to specified, then keep commit_id = None. | + + +### Command line options for run.py + +#### Help + +To get help on "run.py" execute +``` +$ python3 run.py -h +``` +or ``` -$ ./runSuite.sh +$ python3 run.py --help ``` +#### Test selection options -# Adding another repo as a submodule +"-lstq" or "--list_tests_quick": List all tests quickly. Note that this option generates only a partial list of test cases. For tests which are time consuming to generate only category:* will be displayed for them. To display the full list use "-lst" or "--list_tests". +"-lst" or "--list_tests": Gets the list of all test cases. + +Note: "-lst/--list_tests" will take some time to display the results as build happens in the background to fetch the list of tests for some ctest based modules like dtest. + +Note: For platforms other than "amd" be sure to provide --platform option. For example: ``` -$ git submodule add https://github.com/chaconinc/DbConnector -$ git commit -am 'Add DbConnector module' +$ python3 run.py -lst --platform nvidia ``` -# Removing a submodule +"-t": With this option we provide testcase filter. For example, by providing a filter name like "samples" (shown in section 2) we can execute all tests cases under HIP-Samples. + +Likewise, to execute all performance test cases, use "performance" as filter. + +To run all performance tests under sample, use filter "samples:performance". + +When "-t" option is not provided, all tests in testsuite are executed. +"--platform": With this option we provide the platform type ("amd"/"nvidia") where the testcases are run. If "--platform" is not provided, "amd" is taken as default. + +### Overview of filters for run.py + +All tests in the hip-testsuite are broadly classified into the following categories - "samples", "examples" and "conformance". Under "samples" and "examples" there are further 3 sub-categories - "performance", "stress", and "mini-app". HIP directed tests fall under "conformance" category while the rest of the tests use subcategories - "performance", "stress" and "mini-app". + +Below are the list of filters that can be applied currently after "-t": + +| Filter | Test cases executed | +| ------ | ------------------- | +| No filter | options provided All tests, except dtests, are executed by default. | +| “samples” | Executes all test cases under HIP-Samples | +| "samples:performance" | Executes all performance test cases under HIP-Samples | +| "samples:mini-app" | Executes all mini-app test cases under HIP-Samples | +| "examples" | Executes all test cases under HIP-Examples | +| "examples:performance" | Executes all performance test cases under HIP-Examples | +| "examples:stress" | Executes all stress test cases under HIP-Examples | +| "examples:mini-app" | Executes all mini-app test cases under HIP-Examples | +| "performance" | Executes all performance test cases in testsuite | +| "mini-app" | Executes all mini-app test cases in testsuite | +| "stress" | Executes all stress test cases in testsuite | +| "conformance" | Executes all HIP dtests | +| "testname" for e.g "BitExtract" ("testname" is typically the class name) | Executes only the specific test cases with names with “testname” | + +Executing a group of tests with similar name Use wildcards like ".*testname.*". See example 2 below + +Executing Multiple tests To execute multiple tests, specify the tests. The test names must be separated with a space. Check example 3 below. + +Examples: + +1. runs test cases "bitextract" and "directed_tests.devicelib.hip_bitextract" +``` +$ python3 run.py -t BitExtract +``` +2. We can use wildcards to filter test cases to execute. + +To execute all test cases containing "memset" name ``` -$ git rm the_submodule -$ rm -rf .git/modules/the_submodule +$ python3 run.py -t ".*memset.*" ``` +To execute only the "memset" test cases under "directed_tests.runtimeapi.memory" +``` +$ python3 run.py -t directed_tests.runtimeapi.memory.*memset.* +``` +3. Specifying multiple tests +``` +$ python3 run.py -t samples examples --platform amd +``` +4. When no "-t" option is specified all tests,except directed tests, are executed by default. +``` +$ python3 run.py --platform nvidia +``` +Use "-lst" or "--list_tests" options to get the list of all test cases. + +### Testsuite Report + +Reports are generated under the folder mentioned in parameter "log_location" in cfg.py. The report for each run is timestamped. For example, "report/2021_07_12_23_32_04/bitextract". At the end of each run, the summary report is displayed. This summary report provides the list of test cases with result, the metric and system information. The same is available under "report/" folder as report.log. The same report also will be available in JSON format as report.json + +## Adding new tests to the testsuite +Please refer to "examples" folder for example tests + From 7f7e7014a765b3d0767c436279e6b04c7695dd8e Mon Sep 17 00:00:00 2001 From: HRISHIKESH THULA Date: Tue, 20 Jul 2021 22:52:48 +0530 Subject: [PATCH 06/18] Updated copyright text Change-Id: I43db90965f4b74125507cc9e57b80f2f8d451d45 --- README.md | 2 +- cfg.py | 2 +- examples/cfg.py | 2 +- examples/examples/0.py | 2 +- examples/examples/1.py | 2 +- examples/examples/2.py | 2 +- examples/examples/3.py | 2 +- examples/examples/4.py | 2 +- examples/run.py | 2 +- run.py | 2 +- src/amd/AMD.py | 2 +- src/amd/Test.py | 2 +- src/amd/TesterRepository.py | 2 +- src/amd/TestersExecutor.py | 2 +- .../application_test_classifier.py | 2 +- .../applications/hip_examples/hip_examples.py | 2 +- .../hip_examples/hip_examples_build.py | 2 +- .../hip_examples/hip_examples_parser.py | 2 +- .../applications/hip_samples/hip_samples.py | 2 +- .../hip_samples/hip_samples_build.py | 2 +- src/amd/common/hip_get_packages.py | 2 +- src/amd/common/hip_shell.py | 2 +- src/amd/config_processor.py | 2 +- .../conformance_test_classifier.py | 2 +- src/amd/conformance/hip_dtest.py | 2 +- src/amd/conformance/hip_dtest_build.py | 2 +- src/amd/list_tests.py | 2 +- src/amd/match_fun_args_call.py | 2 +- src/amd/targets.py | 2 +- src/amd/test_classifier.py | 2 +- src/amd/test_selector.py | 20 ++++++++++++------- src/amd/version.py | 2 +- 32 files changed, 44 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 1d8097c..65c02f7 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ Below are the list of filters that can be applied currently after "-t": | Filter | Test cases executed | | ------ | ------------------- | -| No filter | options provided All tests, except dtests, are executed by default. | +| No filter options provided | All tests are executed by default. | | “samples” | Executes all test cases under HIP-Samples | | "samples:performance" | Executes all performance test cases under HIP-Samples | | "samples:mini-app" | Executes all mini-app test cases under HIP-Samples | diff --git a/cfg.py b/cfg.py index f7641fb..48b5486 100644 --- a/cfg.py +++ b/cfg.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/examples/cfg.py b/examples/cfg.py index fcd1b47..e8e0e08 100644 --- a/examples/cfg.py +++ b/examples/cfg.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/examples/examples/0.py b/examples/examples/0.py index 4cb0963..95ccb72 100644 --- a/examples/examples/0.py +++ b/examples/examples/0.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/examples/examples/1.py b/examples/examples/1.py index 82a2a0d..7ec2abb 100644 --- a/examples/examples/1.py +++ b/examples/examples/1.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/examples/examples/2.py b/examples/examples/2.py index 2ed69b0..9014de8 100644 --- a/examples/examples/2.py +++ b/examples/examples/2.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/examples/examples/3.py b/examples/examples/3.py index 3391447..c9d8ba3 100644 --- a/examples/examples/3.py +++ b/examples/examples/3.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/examples/examples/4.py b/examples/examples/4.py index 05b1049..98106e7 100644 --- a/examples/examples/4.py +++ b/examples/examples/4.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/examples/run.py b/examples/run.py index 59375bd..668d8be 100644 --- a/examples/run.py +++ b/examples/run.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/run.py b/run.py index 94ee600..880ec85 100644 --- a/run.py +++ b/run.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/AMD.py b/src/amd/AMD.py index b5216a2..32cf8c0 100644 --- a/src/amd/AMD.py +++ b/src/amd/AMD.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/Test.py b/src/amd/Test.py index 53300b8..8ac32c2 100644 --- a/src/amd/Test.py +++ b/src/amd/Test.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/TesterRepository.py b/src/amd/TesterRepository.py index f63d044..921b6a6 100644 --- a/src/amd/TesterRepository.py +++ b/src/amd/TesterRepository.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/TestersExecutor.py b/src/amd/TestersExecutor.py index bbce13e..2a12a06 100644 --- a/src/amd/TestersExecutor.py +++ b/src/amd/TestersExecutor.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/applications/application_test_classifier.py b/src/amd/applications/application_test_classifier.py index 0c52148..9dd0fd0 100644 --- a/src/amd/applications/application_test_classifier.py +++ b/src/amd/applications/application_test_classifier.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/applications/hip_examples/hip_examples.py b/src/amd/applications/hip_examples/hip_examples.py index 56bde47..34ee004 100644 --- a/src/amd/applications/hip_examples/hip_examples.py +++ b/src/amd/applications/hip_examples/hip_examples.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/applications/hip_examples/hip_examples_build.py b/src/amd/applications/hip_examples/hip_examples_build.py index f1db14e..8387da2 100644 --- a/src/amd/applications/hip_examples/hip_examples_build.py +++ b/src/amd/applications/hip_examples/hip_examples_build.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/applications/hip_examples/hip_examples_parser.py b/src/amd/applications/hip_examples/hip_examples_parser.py index 24f8b0a..bfe8e04 100644 --- a/src/amd/applications/hip_examples/hip_examples_parser.py +++ b/src/amd/applications/hip_examples/hip_examples_parser.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/applications/hip_samples/hip_samples.py b/src/amd/applications/hip_samples/hip_samples.py index 3462429..53b41e4 100644 --- a/src/amd/applications/hip_samples/hip_samples.py +++ b/src/amd/applications/hip_samples/hip_samples.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/applications/hip_samples/hip_samples_build.py b/src/amd/applications/hip_samples/hip_samples_build.py index 1f9d669..9f53da3 100644 --- a/src/amd/applications/hip_samples/hip_samples_build.py +++ b/src/amd/applications/hip_samples/hip_samples_build.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/common/hip_get_packages.py b/src/amd/common/hip_get_packages.py index 3f508d6..b91bbdc 100644 --- a/src/amd/common/hip_get_packages.py +++ b/src/amd/common/hip_get_packages.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/common/hip_shell.py b/src/amd/common/hip_shell.py index b523193..7269dd7 100644 --- a/src/amd/common/hip_shell.py +++ b/src/amd/common/hip_shell.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/config_processor.py b/src/amd/config_processor.py index a484940..1b0dafc 100644 --- a/src/amd/config_processor.py +++ b/src/amd/config_processor.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/conformance/conformance_test_classifier.py b/src/amd/conformance/conformance_test_classifier.py index 1720ca4..4a86acd 100644 --- a/src/amd/conformance/conformance_test_classifier.py +++ b/src/amd/conformance/conformance_test_classifier.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/conformance/hip_dtest.py b/src/amd/conformance/hip_dtest.py index 884fbdc..d447778 100644 --- a/src/amd/conformance/hip_dtest.py +++ b/src/amd/conformance/hip_dtest.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/conformance/hip_dtest_build.py b/src/amd/conformance/hip_dtest_build.py index 057e469..693b133 100644 --- a/src/amd/conformance/hip_dtest_build.py +++ b/src/amd/conformance/hip_dtest_build.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/list_tests.py b/src/amd/list_tests.py index 7addf5a..270326f 100644 --- a/src/amd/list_tests.py +++ b/src/amd/list_tests.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/match_fun_args_call.py b/src/amd/match_fun_args_call.py index 2962d3b..ac8db70 100644 --- a/src/amd/match_fun_args_call.py +++ b/src/amd/match_fun_args_call.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/targets.py b/src/amd/targets.py index b9ff1b2..b4be04a 100644 --- a/src/amd/targets.py +++ b/src/amd/targets.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/test_classifier.py b/src/amd/test_classifier.py index 7814239..f985175 100644 --- a/src/amd/test_classifier.py +++ b/src/amd/test_classifier.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/src/amd/test_selector.py b/src/amd/test_selector.py index 6887232..58e4d17 100644 --- a/src/amd/test_selector.py +++ b/src/amd/test_selector.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -80,16 +80,22 @@ def select_tests(self, log_location: str) -> List[Test]: else: test_name_regexes = None - for test_of_tester in self.get_tests(log_location=log_location, quick=True): - select_this_test = self.to_select_this_test(test=test_of_tester, test_name_regexes=test_name_regexes) - if select_this_test: - tests.append(test_of_tester) - - if not tests: + if test_name_regexes is None: for test_of_tester in self.get_tests(log_location=log_location, quick=False): select_this_test = self.to_select_this_test(test=test_of_tester, test_name_regexes=test_name_regexes) if select_this_test: tests.append(test_of_tester) + else: + for test_of_tester in self.get_tests(log_location=log_location, quick=True): + select_this_test = self.to_select_this_test(test=test_of_tester, test_name_regexes=test_name_regexes) + if select_this_test: + tests.append(test_of_tester) + + if not tests: + for test_of_tester in self.get_tests(log_location=log_location, quick=False): + select_this_test = self.to_select_this_test(test=test_of_tester, test_name_regexes=test_name_regexes) + if select_this_test: + tests.append(test_of_tester) return tests diff --git a/src/amd/version.py b/src/amd/version.py index edf9734..f3227cc 100644 --- a/src/amd/version.py +++ b/src/amd/version.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-present Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal From fb4ea4f040c3d446582ebc0fabf5cb46bfea3690 Mon Sep 17 00:00:00 2001 From: Rupam Chetia Date: Mon, 26 Jul 2021 15:04:39 +0530 Subject: [PATCH 07/18] Added Commit ID in cfg.py and updated prettytable version in requirements.txt Change-Id: I1fbfcbfba7b7c04dbc800c200c6c8e39cb79d6dc --- cfg.py | 4 ++-- requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cfg.py b/cfg.py index 48b5486..2a37f83 100644 --- a/cfg.py +++ b/cfg.py @@ -79,12 +79,12 @@ "mixbench": { "repo_url": "https://github.com/ekondis/mixbench.git", "branch": None, - # "commit_id": "" + "commit_id": "e1d6c00bd86d7d904b658213370ddb780a116d1f" }, "gpu_stream": { "repo_url": "https://github.com/UoB-HPC/GPU-STREAM.git", "branch": None, - # "commit_id": "" + "commit_id": "6fe81e19556ac26761a1c7247ae29fa88fb4e0ab" }, "rocclr": { "repo_url": "https://github.com/ROCm-Developer-Tools/ROCclr.git", diff --git a/requirements.txt b/requirements.txt index 04fdfa2..e8d889f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -prettytable \ No newline at end of file +prettytable==2.1.0 \ No newline at end of file From aaa6d954b7f989c575a1a99835820a03176f5548 Mon Sep 17 00:00:00 2001 From: Rupam Chetia Date: Wed, 28 Jul 2021 22:59:03 +0530 Subject: [PATCH 08/18] Updating testsuite with HIPAMD repo changes. Change-Id: I279f8e046736afc1177f6a4963cc4fadaaf7b675 --- cfg.py | 27 ++++--- .../applications/hip_examples/hip_examples.py | 16 +++- .../hip_examples/hip_examples_build.py | 15 ++-- .../applications/hip_samples/hip_samples.py | 17 ++++- .../hip_samples/hip_samples_build.py | 15 ++-- src/amd/common/hip_get_packages.py | 6 ++ src/amd/conformance/hip_dtest.py | 16 ++++ src/amd/conformance/hip_dtest_build.py | 73 ++++++++++--------- 8 files changed, 125 insertions(+), 60 deletions(-) diff --git a/cfg.py b/cfg.py index 2a37f83..4557c64 100644 --- a/cfg.py +++ b/cfg.py @@ -64,17 +64,22 @@ run_tests = None -branch = "rocm-4.2.x" +branch = None repos = { "hip_examples": { "repo_url": "https://github.com/ROCm-Developer-Tools/HIP-Examples", - "branch": branch, - # "commit_id": "" + "branch": None, + "commit_id": "8a3b04c9b10bae344c7483a63c13034869da184b" }, "hip": { "repo_url": "https://github.com/ROCm-Developer-Tools/HIP", - "branch": branch, - # "commit_id": "" + "branch": None, + "commit_id": "586165ebc281eb9461898a5b2abbc74595f5d97d" + }, + "hipamd": { + "repo_url": "https://github.com/ROCm-Developer-Tools/HIPAMD", + "branch": None, + "commit_id": "de01ce04677243116dba52b59406a130517ea4c7" }, "mixbench": { "repo_url": "https://github.com/ekondis/mixbench.git", @@ -87,13 +92,13 @@ "commit_id": "6fe81e19556ac26761a1c7247ae29fa88fb4e0ab" }, "rocclr": { - "repo_url": "https://github.com/ROCm-Developer-Tools/ROCclr.git", - "branch": branch, - # "commit_id": "" + "repo_url": "https://github.com/ROCm-Developer-Tools/ROCclr", + "branch": None, + "commit_id": "0ca4f7939969004b9080d814a30cc8e34cdac833" }, "opencl": { - "repo_url": "https://github.com/RadeonOpenCompute/ROCm-OpenCL-Runtime.git", - "branch": branch, - # "commit_id": "" + "repo_url": "https://github.com/RadeonOpenCompute/ROCm-OpenCL-Runtime", + "branch": None, + "commit_id": "bbdc87e08b322d349f82bdd7575c8ce94d31d276" } } diff --git a/src/amd/applications/hip_examples/hip_examples.py b/src/amd/applications/hip_examples/hip_examples.py index 34ee004..458385d 100644 --- a/src/amd/applications/hip_examples/hip_examples.py +++ b/src/amd/applications/hip_examples/hip_examples.py @@ -43,6 +43,9 @@ def __init__(self, path, cwd): self.hiprepo = "" # Default self.hipbranch = "" self.hipcommitId = "" + self.hipamdrepo = "" # Default + self.hipamdbranch = "" + self.hipamdcommitId = "" self.gpustrm_repo = "" self.gpustrm_branch = "" self.gpustrm_commitId = "" @@ -59,6 +62,14 @@ def set_hip_repoinfo(self, test_data: HIPTestData): self.hipbranch = test_data.repos["hip"].branch if test_data.repos["hip"].commit_id != None: self.hipcommitId = test_data.repos["hip"].commit_id + if test_data.repos["hipamd"].repo_url != None: + self.hipamdrepo = test_data.repos["hipamd"].repo_url + else: + return False + if test_data.repos["hipamd"].branch != None: + self.hipamdbranch = test_data.repos["hipamd"].branch + if test_data.repos["hipamd"].commit_id != None: + self.hipamdcommitId = test_data.repos["hipamd"].commit_id return True def set_hipex_repoinfo(self, test_data: HIPTestData): @@ -104,8 +115,11 @@ def set_gpustr_repoinfo(self, test_data: HIPTestData): return validrepconfig def download_deppackage(self, logFile): - return HipPackages().pull_repo(logFile, self.hiprepo, self.hipbranch,\ + ret = HipPackages().pull_repo(logFile, self.hiprepo, self.hipbranch,\ self.hipcommitId, "HIP") + ret = ret & HipPackages().pull_repo(logFile, self.hipamdrepo, self.hipamdbranch,\ + self.hipamdcommitId, "hipamd") + return ret def download_hipexample(self, logFile): ret = self.download_deppackage(logFile) diff --git a/src/amd/applications/hip_examples/hip_examples_build.py b/src/amd/applications/hip_examples/hip_examples_build.py index 8387da2..438940c 100644 --- a/src/amd/applications/hip_examples/hip_examples_build.py +++ b/src/amd/applications/hip_examples/hip_examples_build.py @@ -322,6 +322,7 @@ def clean(self, testid): class BuildRunNvidia(BuildRunCommon): def __init__(self, path): self.hippath = os.path.join(os.getcwd(),"src/amd/conformance/HIP/") + self.hipamdpath = os.path.join(os.getcwd(),"src/amd/conformance/HIPAMD/") BuildRunCommon.__init__(self, path) def getenvironmentvariables(self): @@ -330,25 +331,27 @@ def getenvironmentvariables(self): envtoset["HIP_COMPILER"] = "nvcc" envtoset["HIP_RUNTIME"] = "cuda" envtoset["HIP_DIR"] = self.hippath - envtoset["HIP_PATH"] = os.path.join(self.hippath, "build") + envtoset["HIP_AMD_DIR"] = self.hipamdpath + envtoset["HIP_PATH"] = os.path.join(self.hipamdpath, "build") return envtoset def setupenvironmentfornvcc(self, logFile): - cmdcd = "cd "+self.hippath+";" + cmdcd = "cd " + self.hipamdpath + ";" envtoset = self.getenvironmentvariables() cmdsetenv = "rm -Rf build/;mkdir build;cd build;" - cmdbuildinstall = "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install ..;" + cmdbuildinstall =\ + "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install -DHIP_COMMON_DIR=\"$HIP_DIR\" -DHIP_AMD_BACKEND_SOURCE_DIR=\"$HIP_AMD_DIR\" ..;" cmdbuildinstall += "make install;" cmdexc = cmdcd + cmdsetenv + cmdbuildinstall execshellcmd(cmdexc, logFile, envtoset) def buildtest(self, logFile, testid): buildbindirpresent = os.path.isdir(\ - os.path.join(self.hippath, "build/bin")) + os.path.join(self.hipamdpath, "build/bin")) buildincludedirpresent = os.path.isdir(\ - os.path.join(self.hippath, "build/include")) + os.path.join(self.hipamdpath, "build/include")) buildinstalldirpresent = os.path.isdir(\ - os.path.join(self.hippath, "build/install")) + os.path.join(self.hipamdpath, "build/install")) if not (buildbindirpresent & buildincludedirpresent & buildinstalldirpresent): self.setupenvironmentfornvcc(logFile) env = self.getenvironmentvariables() diff --git a/src/amd/applications/hip_samples/hip_samples.py b/src/amd/applications/hip_samples/hip_samples.py index 53b41e4..c400c02 100644 --- a/src/amd/applications/hip_samples/hip_samples.py +++ b/src/amd/applications/hip_samples/hip_samples.py @@ -40,6 +40,9 @@ def __init__(self, path, binary, cwd): self.hiprepo = "" # Default self.hipbranch = "" self.hipcommitId = "" + self.hipamdrepo = "" # Default + self.hipamdbranch = "" + self.hipamdcommitId = "" self.prepareobj = None def setrepoinfo(self, test_data: HIPTestData): @@ -52,11 +55,23 @@ def setrepoinfo(self, test_data: HIPTestData): self.hipbranch = test_data.repos["hip"].branch if test_data.repos["hip"].commit_id != None: self.hipcommitId = test_data.repos["hip"].commit_id + + if test_data.repos["hipamd"].repo_url != None: + self.hipamdrepo = test_data.repos["hipamd"].repo_url + else: + validrepconfig &= False + if test_data.repos["hipamd"].branch != None: + self.hipamdbranch = test_data.repos["hipamd"].branch + if test_data.repos["hipamd"].commit_id != None: + self.hipamdcommitId = test_data.repos["hipamd"].commit_id return validrepconfig def downloadtest(self, logFile): - return HipPackages().pull_repo(logFile, self.hiprepo, self.hipbranch,\ + ret = HipPackages().pull_repo(logFile, self.hiprepo, self.hipbranch,\ self.hipcommitId, "HIP") + ret = ret & HipPackages().pull_repo(logFile, self.hipamdrepo, self.hipamdbranch,\ + self.hipamdcommitId, "hipamd") + return ret def buildtest(self, logFile, platform): isBinaryPresent = True diff --git a/src/amd/applications/hip_samples/hip_samples_build.py b/src/amd/applications/hip_samples/hip_samples_build.py index 9f53da3..516be3c 100644 --- a/src/amd/applications/hip_samples/hip_samples_build.py +++ b/src/amd/applications/hip_samples/hip_samples_build.py @@ -59,6 +59,7 @@ def buildtest(self): class BuildRunNvidia(BuildRunCommon): def __init__(self, path, logfile): self.hippath = os.path.join(os.getcwd(), "src/amd/conformance/HIP/") + self.hipamdpath = os.path.join(os.getcwd(),"src/amd/conformance/HIPAMD/") BuildRunCommon.__init__(self, path, logfile) def getenvironmentvariables(self): @@ -67,14 +68,16 @@ def getenvironmentvariables(self): envtoset["HIP_COMPILER"] = "nvcc" envtoset["HIP_RUNTIME"] = "cuda" envtoset["HIP_DIR"] = self.hippath - envtoset["HIP_PATH"] = "../../../build" + envtoset["HIP_AMD_DIR"] = self.hipamdpath + envtoset["HIP_PATH"] = os.path.join(self.hipamdpath, "build") return envtoset def setupenvironmentfornvcc(self): - cmdcd = "cd " + self.hippath + ";" + cmdcd = "cd " + self.hipamdpath + ";" envtoset = self.getenvironmentvariables() cmdsetenv = "rm -Rf build/;mkdir build;cd build;" - cmdbuildinstall = "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install ..;" + cmdbuildinstall =\ + "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install -DHIP_COMMON_DIR=\"$HIP_DIR\" -DHIP_AMD_BACKEND_SOURCE_DIR=\"$HIP_AMD_DIR\" ..;" cmdbuildinstall += "make install;" cmdexc = cmdcd + cmdsetenv + cmdbuildinstall execshellcmd(cmdexc, self.logfile, envtoset) @@ -86,11 +89,11 @@ def applypatch(self): # To be deleted def buildtest(self): buildbindirpresent = os.path.isdir(\ - os.path.join(self.hippath, "build/bin")) + os.path.join(self.hipamdpath, "build/bin")) buildincludedirpresent = os.path.isdir(\ - os.path.join(self.hippath, "build/include")) + os.path.join(self.hipamdpath, "build/include")) buildinstalldirpresent = os.path.isdir(\ - os.path.join(self.hippath, "build/install")) + os.path.join(self.hipamdpath, "build/install")) if not (buildbindirpresent & buildincludedirpresent & buildinstalldirpresent): self.setupenvironmentfornvcc() envtoset = self.getenvironmentvariables() diff --git a/src/amd/common/hip_get_packages.py b/src/amd/common/hip_get_packages.py index b91bbdc..17edb87 100644 --- a/src/amd/common/hip_get_packages.py +++ b/src/amd/common/hip_get_packages.py @@ -19,6 +19,7 @@ # THE SOFTWARE. from amd.common.hip_shell import execshellcmd + import os # Common class to clone/pull dependent Packages @@ -27,6 +28,7 @@ def __init__(self): self.cwdAbs = os.getcwd() self.conformancePath = os.path.join(self.cwdAbs, "src/amd/conformance/") self.hippath = os.path.join(self.conformancePath, "HIP/") + self.hipamdpath = os.path.join(self.conformancePath, "HIPAMD/") self.appPath = os.path.join(self.cwdAbs,"src/amd/applications/hip_examples/") self.examplepath = os.path.join(self.appPath, "HIP-Examples/") self.mixbenchpath = os.path.join(self.appPath, "mixbench/") @@ -54,6 +56,10 @@ def pull_repo(self, logFile, repo, branch, commitId, reponame): repo_root_path = self.hippath repo_location = self.conformancePath repo_dir = "HIP" + elif reponame == "hipamd": + repo_root_path = self.hipamdpath + repo_location = self.conformancePath + repo_dir = "HIPAMD" elif reponame == "rocclr": repo_root_path = self.rocclrpath repo_location = self.conformancePath diff --git a/src/amd/conformance/hip_dtest.py b/src/amd/conformance/hip_dtest.py index d447778..0407dea 100644 --- a/src/amd/conformance/hip_dtest.py +++ b/src/amd/conformance/hip_dtest.py @@ -35,6 +35,9 @@ def __init__(self): self.hiprepo = "" # Default self.hipbranch = "" self.hipcommitId = "" + self.hipamdrepo = "" # Default + self.hipamdbranch = "" + self.hipamdcommitId = "" self.rocclrrepo = "" # Default self.rocclrbranch = "" self.rocclrcommitId = "" @@ -56,6 +59,16 @@ def setrepoinfo(self, test_data: HIPBuildData): self.hipbranch = test_data.repos["hip"].branch if test_data.repos["hip"].commit_id != None: self.hipcommitId = test_data.repos["hip"].commit_id + + if test_data.repos["hipamd"].repo_url != None: + self.hipamdrepo = test_data.repos["hipamd"].repo_url + else: + return False + if test_data.repos["hipamd"].branch != None: + self.hipamdbranch = test_data.repos["hipamd"].branch + if test_data.repos["hipamd"].commit_id != None: + self.hipamdcommitId = test_data.repos["hipamd"].commit_id + if test_data.repos["rocclr"].repo_url != None: self.rocclrrepo = test_data.repos["rocclr"].repo_url else: @@ -64,6 +77,7 @@ def setrepoinfo(self, test_data: HIPBuildData): self.rocclrbranch = test_data.repos["rocclr"].branch if test_data.repos["rocclr"].commit_id != None: self.rocclrcommitId = test_data.repos["rocclr"].commit_id + if test_data.repos["opencl"].repo_url != None: self.openclrepo = test_data.repos["opencl"].repo_url else: @@ -78,6 +92,8 @@ def setrepoinfo(self, test_data: HIPBuildData): def downloadTest(self, log, platform): ret = HipPackages().pull_repo(log, self.hiprepo, self.hipbranch,\ self.hipcommitId, "HIP") + ret &= HipPackages().pull_repo(log, self.hipamdrepo, self.hipamdbranch,\ + self.hipamdcommitId, "hipamd") if platform == HIP_PLATFORM.amd: ret &= HipPackages().pull_repo(log, self.rocclrrepo, self.rocclrbranch,\ self.rocclrcommitId, "rocclr") diff --git a/src/amd/conformance/hip_dtest_build.py b/src/amd/conformance/hip_dtest_build.py index 693b133..31ad369 100644 --- a/src/amd/conformance/hip_dtest_build.py +++ b/src/amd/conformance/hip_dtest_build.py @@ -32,12 +32,13 @@ class BuildRunCommon(): def __init__(self, logfile): self.logfile = logfile self.hippath = os.path.join(os.getcwd(), "src/amd/conformance/HIP") + self.hipamdpath = os.path.join(os.getcwd(),"src/amd/conformance/HIPAMD") self.rclrpath = os.path.join(os.getcwd(), "src/amd/conformance/ROCclr") self.oclpath = os.path.join(os.getcwd(), "src/amd/conformance/ROCm-OpenCL-Runtime") # Fetches all available dtests using ctest def get_all_ctest(self): - cmdexc = "cd " + self.hippath + "/build;" + cmdexc = "cd " + self.hipamdpath + "/build;" cmdexc += "ctest -N;" with open("ctest.txt", "w+") as ctestlog: execshellcmd_largedump(cmdexc, self.logfile, ctestlog, None) @@ -69,7 +70,7 @@ def runtest(self, log, testcase, envtoset): cmdtest = "ctest -R " + testcase print("Executing command = " + cmdtest) # run test - cmd = "cd " + os.path.join(self.hippath, "build") + ";" + cmd = "cd " + os.path.join(self.hipamdpath, "build") + ";" cmd += cmdtest + ";" runlogdump = tempfile.TemporaryFile("w+") execshellcmd_largedump(cmd, log, runlogdump, envtoset) @@ -92,6 +93,7 @@ def get_envvar(self): envtoset["ROCclr_DIR"] = self.rclrpath envtoset["OPENCL_DIR"] = self.oclpath envtoset["HIP_DIR"] = self.hippath + envtoset["HIP_AMD_DIR"] = self.hipamdpath return envtoset # Validate if rocclr build is successful @@ -111,15 +113,13 @@ def validate_rocclr_build(self): # Validate if HIP build is successful def validate_hip_build(self): status = True - hip_install_inc = os.path.join(self.hippath, "build/install/include") - hip_install_lib = os.path.join(self.hippath, "build/install/lib") - hip_install_bin = os.path.join(self.hippath, "build/install/bin") - hip_install_rocclr = os.path.join(self.hippath, "build/install/rocclr") + hip_install_inc = os.path.join(self.hipamdpath, "build/install/include") + hip_install_lib = os.path.join(self.hipamdpath, "build/install/lib") + hip_install_bin = os.path.join(self.hipamdpath, "build/install/bin") hip_binaries = glob.glob(hip_install_lib + "/*.so*") status &= os.path.isdir(hip_install_inc) status &= os.path.isdir(hip_install_lib) status &= os.path.isdir(hip_install_bin) - status &= os.path.isdir(hip_install_rocclr) if len(hip_binaries) > 0: status &= True else: @@ -131,37 +131,33 @@ def build_package(self, env = None): buildSuccess = True # Set environment envtoset = self.get_envvar() - # build rocclr - cmd = "cd \"$ROCclr_DIR\";" - cmd += "rm -Rf build;" - cmd += "mkdir -p build;cd build;" - cmd += "cmake -DOPENCL_DIR=\"$OPENCL_DIR\" -DCMAKE_INSTALL_PREFIX=\"$ROCclr_DIR/../opt/rocm/rocclr\" ..;" - cmd += "cmake -j;make install;" - cmdexc = cmd - runlogdump = tempfile.TemporaryFile("w+") - execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) - runlogdump.close() - # Validate if rocclr build is successful - buildSuccess = self.validate_rocclr_build() - if buildSuccess == False: - print("rocclr build failed!") - return False # Build HIP - cmd = "cd \"$HIP_DIR\";" + cmd = "cd \"$HIP_AMD_DIR\";" cmd += "rm -Rf build;" cmd += "mkdir -p build;cd build;" - cmd += "cmake -DCMAKE_PREFIX_PATH=\"$ROCclr_DIR/build;/opt/rocm;$ROCclr_DIR/../opt/rocm/rocclr\" -DCMAKE_INSTALL_PREFIX=$PWD/install ..;" - cmd += "cmake -j;make install;" - cmd += "make -j8 build_tests" + cmd += "cmake -DHIP_COMPILER=clang -DHIP_PLATFORM=rocclr -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX=$PWD/install" +\ + " -DROCCLR_PATH=\"$ROCclr_DIR\" -DAMD_OPENCL_PATH=\"$OPENCL_DIR\" -DHIP_COMMON_DIR=\"$HIP_DIR\"" +\ + " -DCMAKE_PREFIX_PATH=\"/opt/rocm/lib/cmake/hsa-runtime64;/opt/rocm/lib/cmake/amd_comgr\" ..;" + cmd += "make -j8;make install;" + cmdexc = cmd runlogdump = tempfile.TemporaryFile("w+") execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) runlogdump.close() + # Validate if HIP build is successful buildSuccess = self.validate_hip_build() if buildSuccess == False: print("HIP build failed!") return False + + # Build Dtest + cmd = "cd \"$HIP_AMD_DIR\";cd build;" + cmd += "make -j8 build_tests;" + cmdexc = cmd + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) + runlogdump.close() return True # Execute test cases @@ -172,9 +168,7 @@ def runtest(self, log, testcase): # Clean the test def clean(self): envtoset = self.get_envvar() - cmd = "cd \"$ROCclr_DIR\";" - cmd += "rm -Rf build;rm -Rf $ROCclr_DIR/../opt;" - cmd += "cd \"$HIP_DIR\";" + cmd = "cd \"$HIP_AMD_DIR\";" cmd += "rm -Rf build;" execshellcmd(cmd, None, envtoset) @@ -193,13 +187,14 @@ def get_envvar(self): envtoset["HIP_COMPILER"] = "nvcc" envtoset["HIP_RUNTIME"] = "cuda" envtoset["HIP_DIR"] = self.hippath + envtoset["HIP_AMD_DIR"] = self.hipamdpath return envtoset # Validate if HIP build is successful def validate_hip_build(self): status = True - hip_install_inc = os.path.join(self.hippath, "build/install/include") - hip_install_bin = os.path.join(self.hippath, "build/install/bin") + hip_install_inc = os.path.join(self.hipamdpath, "build/install/include") + hip_install_bin = os.path.join(self.hipamdpath, "build/install/bin") status &= os.path.isdir(hip_install_inc) status &= os.path.isdir(hip_install_bin) return status @@ -210,12 +205,12 @@ def build_package(self, env = None): # Set environment envtoset = self.get_envvar() # Build HIP - cmd = "cd \"$HIP_DIR\";" + cmd = "cd \"$HIP_AMD_DIR\";" cmd += "rm -Rf build;" cmd += "mkdir -p build;cd build;" - cmd += "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install ..;" + cmd += "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install -DHIP_COMMON_DIR=\"$HIP_DIR\"" + \ + " -DHIP_AMD_BACKEND_SOURCE_DIR=\"$HIP_AMD_DIR\" .. ;" cmd += "make install;" - cmd += "make -j8 build_tests" cmdexc = cmd runlogdump = tempfile.TemporaryFile("w+") execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) @@ -225,6 +220,14 @@ def build_package(self, env = None): if buildSuccess == False: print("HIP build failed!") return False + # Build Dtest + cmd = "cd \"$HIP_AMD_DIR\";cd build;" + cmd += "make -j8 build_tests;" + cmdexc = cmd + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) + runlogdump.close() + return True # Execute test cases @@ -235,6 +238,6 @@ def runtest(self, log, testcase): # Clean the test def clean(self): envtoset = self.get_envvar() - cmd = "cd \"$HIP_DIR\";" + cmd = "cd \"$HIP_AMD_DIR\";" cmd += "rm -Rf build;" execshellcmd(cmd, None, envtoset) From 3f69620fdb985250954edb029559076fd4cb5ffd Mon Sep 17 00:00:00 2001 From: Rupam Chetia Date: Tue, 28 Sep 2021 11:35:35 +0530 Subject: [PATCH 09/18] ANL MS4 changes 1. Added Apps - Quicksilver, cuda_grep, cuda_memtest, keccaktreegpu and mgbench 2. HIP build optimisation 3. Folder renaming from amd to testsuite 4. Splitting of build/run for AMD/nvidia 5. Fixing of issue for multiple test cases in filters 6. Supporting Catch2 build with rocm libs. 7. Added Gridtools and Kokkos apps. 8. Added Laghos app. Change-Id: Iad46b2ebd9db634136874cfabf100006bc491b90 --- cfg.py | 59 +- run.py | 4 +- .../hip_samples/hip_samples_build.py | 108 -- src/amd/conformance/hip_dtest_build.py | 243 ---- src/{amd => testsuite}/AMD.py | 0 src/{amd => testsuite}/Test.py | 16 +- src/{amd => testsuite}/TesterRepository.py | 14 +- src/{amd => testsuite}/TestersExecutor.py | 8 +- src/{amd => testsuite}/__init__.py | 0 .../applications/__init__.py | 0 .../application_test_classifier.py | 2 +- .../applications/cuda_grep}/__init__.py | 0 .../applications/cuda_grep/cuda_grep.py | 151 +++ .../cuda_grep/cuda_grep_build_amd.py | 66 ++ .../cuda_grep/cuda_grep_build_nvidia.py | 75 ++ .../cuda_grep/cuda_grep_parser_common.py | 35 + .../applications/cuda_memtest}/__init__.py | 0 .../applications/cuda_memtest/cuda_memtest.py | 602 ++++++++++ .../cuda_memtest/cuda_memtest_build_amd.py | 65 ++ .../cuda_memtest/cuda_memtest_build_nvidia.py | 74 ++ .../cuda_memtest_parser_common.py | 34 + .../cuda_memtest/cuda_memtest_patch | 11 + .../applications/hip_examples}/__init__.py | 0 .../applications/hip_examples/hip_examples.py | 67 +- .../hip_examples/hip_examples_build_amd.py | 49 + .../hip_examples_build_common.py} | 83 +- .../hip_examples/hip_examples_build_nvidia.py | 61 + .../hip_examples/hip_examples_parser.py | 0 .../hip_samples/Samples_Patch_4.2.x | 0 .../applications/hip_samples}/__init__.py | 0 .../applications/hip_samples/hip_samples.py | 31 +- .../hip_samples/hip_samples_build_amd.py | 37 + .../hip_samples/hip_samples_build_common.py | 44 + .../hip_samples/hip_samples_build_nvidia.py | 57 + .../keccaktreegpu/KeccakTreeGpu/KeccakF.c | 393 +++++++ .../keccaktreegpu/KeccakTreeGpu/KeccakF.h | 38 + .../keccaktreegpu/KeccakTreeGpu/KeccakTree.h | 29 + .../KeccakTreeGpu/KeccakTreeCPU.c | 195 ++++ .../KeccakTreeGpu/KeccakTreeCPU.h | 24 + .../KeccakTreeGpu/KeccakTreeGPU.cu | 1003 ++++++++++++++++ .../KeccakTreeGpu/KeccakTreeGPU.h | 72 ++ .../keccaktreegpu/KeccakTreeGpu/KeccakTypes.h | 15 + .../keccaktreegpu/KeccakTreeGpu/Makefile | 50 + .../keccaktreegpu/KeccakTreeGpu/README.txt | 25 + .../keccaktreegpu/KeccakTreeGpu/Test.c | 1023 +++++++++++++++++ .../keccaktreegpu/KeccakTreeGpu/Test.h | 65 ++ .../VCKeccakTree/Release/BuildLog.htm | Bin 0 -> 8432 bytes .../VCKeccakTree/Release/KeccakF.obj | Bin 0 -> 21597 bytes .../VCKeccakTree/Release/KeccakTreeCPU.obj | Bin 0 -> 12524 bytes .../VCKeccakTree/Release/KeccakTreeGPU.cu.obj | Bin 0 -> 273038 bytes .../VCKeccakTree/Release/Test.obj | Bin 0 -> 65168 bytes .../Release/VCKeccakTree.ex.renameit | Bin 0 -> 314880 bytes .../VCKeccakTree.exe.intermediate.manifest | 10 + .../VCKeccakTree/Release/VCKeccakTree.pdb | Bin 0 -> 1084416 bytes .../VCKeccakTree/Release/cudart32_30_14.dll | Bin 0 -> 285800 bytes .../VCKeccakTree/Release/main.obj | Bin 0 -> 31734 bytes .../KeccakTreeGpu/VCKeccakTree/Release/mt.dep | 1 + .../VCKeccakTree/Release/vc90.idb | Bin 0 -> 93184 bytes .../VCKeccakTree/Release/vc90.pdb | Bin 0 -> 69632 bytes .../VCKeccakTree/VCKeccakTree.ncb | Bin 0 -> 1190912 bytes .../VCKeccakTree/VCKeccakTree.sln | 20 + .../VCKeccakTree/VCKeccakTree.suo | Bin 0 -> 33792 bytes .../VCKeccakTree/VCKeccakTree.vcproj | 243 ++++ .../VCKeccakTree.vcproj.DELLM24.guigui.user | 65 ++ .../KeccakTreeGpu/VCKeccakTree/vc90.pdb | Bin 0 -> 143360 bytes .../keccaktreegpu/KeccakTreeGpu/main.c | 68 ++ .../applications/keccaktreegpu}/__init__.py | 0 .../keccaktreegpu/keccaktreegpu.py | 119 ++ .../keccaktreegpu/keccaktreegpu_build_amd.py | 64 ++ .../keccaktreegpu_build_nvidia.py | 67 ++ .../keccaktreegpu_parser_common.py | 43 + .../applications/mgbench/__init__.py | 0 src/testsuite/applications/mgbench/mgbench.py | 242 ++++ .../applications/mgbench/mgbench_build_amd.py | 74 ++ .../mgbench/mgbench_build_nvidia.py | 82 ++ .../mgbench/mgbench_parser_common.py | 52 + src/testsuite/common/__init__.py | 0 .../common/hip_get_packages.py | 80 +- src/{amd => testsuite}/common/hip_shell.py | 11 + src/{amd => testsuite}/config_processor.py | 2 +- src/testsuite/conformance/CMakePatch | 9 + src/testsuite/conformance/__init__.py | 0 .../conformance_test_classifier.py | 2 +- .../conformance/hip_dtest.py | 74 +- .../conformance/hip_dtest_build_amd.py | 67 ++ .../conformance/hip_dtest_build_common.py | 90 ++ .../conformance/hip_dtest_build_nvidia.py | 75 ++ src/testsuite/hpc_apps/__init__.py | 0 src/testsuite/hpc_apps/gridtools/__init__.py | 0 src/testsuite/hpc_apps/gridtools/gridtools.py | 242 ++++ .../hpc_apps/gridtools/gridtools_build_amd.py | 119 ++ .../gridtools/gridtools_parser_common.py | 48 + .../hpc_apps/gridtools/gtbench.patch | 120 ++ src/testsuite/hpc_apps/kokkos/__init__.py | 0 src/testsuite/hpc_apps/kokkos/kokkos.py | 202 ++++ .../hpc_apps/kokkos/kokkos_build_amd.py | 72 ++ .../hpc_apps/kokkos/kokkos_parser_common.py | 36 + src/testsuite/hpc_apps/laghos/__init__.py | 0 src/testsuite/hpc_apps/laghos/laghos.py | 226 ++++ .../hpc_apps/laghos/laghos_build_amd.py | 225 ++++ .../hpc_apps/laghos/laghos_parser_common.py | 39 + .../hpc_apps/quicksilver/__init__.py | 0 .../hpc_apps/quicksilver/quicksilver.py | 154 +++ .../quicksilver/quicksilver_build_amd.py | 60 + .../quicksilver/quicksilver_diff_patch | 46 + .../quicksilver/quicksilver_parser_common.py | 42 + src/{amd => testsuite}/list_tests.py | 6 +- src/{amd => testsuite}/match_fun_args_call.py | 0 src/testsuite/stress/__init__.py | 0 src/{amd => testsuite}/targets.py | 2 +- src/{amd => testsuite}/test_classifier.py | 2 +- src/{amd => testsuite}/test_selector.py | 54 +- src/testsuite/thirdparty/__init__.py | 0 src/{amd => testsuite}/version.py | 0 114 files changed, 7578 insertions(+), 605 deletions(-) delete mode 100644 src/amd/applications/hip_samples/hip_samples_build.py delete mode 100644 src/amd/conformance/hip_dtest_build.py rename src/{amd => testsuite}/AMD.py (100%) rename src/{amd => testsuite}/Test.py (92%) rename src/{amd => testsuite}/TesterRepository.py (93%) rename src/{amd => testsuite}/TestersExecutor.py (98%) rename src/{amd => testsuite}/__init__.py (100%) rename src/{amd => testsuite}/applications/__init__.py (100%) rename src/{amd => testsuite}/applications/application_test_classifier.py (96%) rename src/{amd/applications/hip_examples => testsuite/applications/cuda_grep}/__init__.py (100%) create mode 100644 src/testsuite/applications/cuda_grep/cuda_grep.py create mode 100644 src/testsuite/applications/cuda_grep/cuda_grep_build_amd.py create mode 100644 src/testsuite/applications/cuda_grep/cuda_grep_build_nvidia.py create mode 100644 src/testsuite/applications/cuda_grep/cuda_grep_parser_common.py rename src/{amd/applications/hip_samples => testsuite/applications/cuda_memtest}/__init__.py (100%) create mode 100644 src/testsuite/applications/cuda_memtest/cuda_memtest.py create mode 100644 src/testsuite/applications/cuda_memtest/cuda_memtest_build_amd.py create mode 100644 src/testsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py create mode 100644 src/testsuite/applications/cuda_memtest/cuda_memtest_parser_common.py create mode 100644 src/testsuite/applications/cuda_memtest/cuda_memtest_patch rename src/{amd/common => testsuite/applications/hip_examples}/__init__.py (100%) rename src/{amd => testsuite}/applications/hip_examples/hip_examples.py (96%) create mode 100644 src/testsuite/applications/hip_examples/hip_examples_build_amd.py rename src/{amd/applications/hip_examples/hip_examples_build.py => testsuite/applications/hip_examples/hip_examples_build_common.py} (81%) create mode 100644 src/testsuite/applications/hip_examples/hip_examples_build_nvidia.py rename src/{amd => testsuite}/applications/hip_examples/hip_examples_parser.py (100%) rename src/{amd => testsuite}/applications/hip_samples/Samples_Patch_4.2.x (100%) rename src/{amd/conformance => testsuite/applications/hip_samples}/__init__.py (100%) rename src/{amd => testsuite}/applications/hip_samples/hip_samples.py (97%) create mode 100644 src/testsuite/applications/hip_samples/hip_samples_build_amd.py create mode 100644 src/testsuite/applications/hip_samples/hip_samples_build_common.py create mode 100644 src/testsuite/applications/hip_samples/hip_samples_build_nvidia.py create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.c create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.h create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTree.h create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.c create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.h create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.cu create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.h create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTypes.h create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Makefile create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/README.txt create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.c create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.h create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/BuildLog.htm create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakF.obj create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeCPU.obj create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeGPU.cu.obj create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/Test.obj create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.ex.renameit create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.exe.intermediate.manifest create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.pdb create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/cudart32_30_14.dll create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/main.obj create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/mt.dep create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.idb create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.pdb create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.ncb create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.sln create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.suo create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj.DELLM24.guigui.user create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/vc90.pdb create mode 100644 src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/main.c rename src/{amd/stress => testsuite/applications/keccaktreegpu}/__init__.py (100%) create mode 100644 src/testsuite/applications/keccaktreegpu/keccaktreegpu.py create mode 100644 src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py create mode 100644 src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py create mode 100644 src/testsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py create mode 100644 src/testsuite/applications/mgbench/__init__.py create mode 100644 src/testsuite/applications/mgbench/mgbench.py create mode 100644 src/testsuite/applications/mgbench/mgbench_build_amd.py create mode 100644 src/testsuite/applications/mgbench/mgbench_build_nvidia.py create mode 100644 src/testsuite/applications/mgbench/mgbench_parser_common.py create mode 100644 src/testsuite/common/__init__.py rename src/{amd => testsuite}/common/hip_get_packages.py (55%) rename src/{amd => testsuite}/common/hip_shell.py (86%) rename src/{amd => testsuite}/config_processor.py (97%) create mode 100644 src/testsuite/conformance/CMakePatch create mode 100644 src/testsuite/conformance/__init__.py rename src/{amd => testsuite}/conformance/conformance_test_classifier.py (96%) rename src/{amd => testsuite}/conformance/hip_dtest.py (66%) create mode 100644 src/testsuite/conformance/hip_dtest_build_amd.py create mode 100644 src/testsuite/conformance/hip_dtest_build_common.py create mode 100644 src/testsuite/conformance/hip_dtest_build_nvidia.py create mode 100644 src/testsuite/hpc_apps/__init__.py create mode 100644 src/testsuite/hpc_apps/gridtools/__init__.py create mode 100644 src/testsuite/hpc_apps/gridtools/gridtools.py create mode 100644 src/testsuite/hpc_apps/gridtools/gridtools_build_amd.py create mode 100644 src/testsuite/hpc_apps/gridtools/gridtools_parser_common.py create mode 100644 src/testsuite/hpc_apps/gridtools/gtbench.patch create mode 100644 src/testsuite/hpc_apps/kokkos/__init__.py create mode 100644 src/testsuite/hpc_apps/kokkos/kokkos.py create mode 100644 src/testsuite/hpc_apps/kokkos/kokkos_build_amd.py create mode 100644 src/testsuite/hpc_apps/kokkos/kokkos_parser_common.py create mode 100644 src/testsuite/hpc_apps/laghos/__init__.py create mode 100644 src/testsuite/hpc_apps/laghos/laghos.py create mode 100644 src/testsuite/hpc_apps/laghos/laghos_build_amd.py create mode 100644 src/testsuite/hpc_apps/laghos/laghos_parser_common.py create mode 100644 src/testsuite/hpc_apps/quicksilver/__init__.py create mode 100644 src/testsuite/hpc_apps/quicksilver/quicksilver.py create mode 100644 src/testsuite/hpc_apps/quicksilver/quicksilver_build_amd.py create mode 100644 src/testsuite/hpc_apps/quicksilver/quicksilver_diff_patch create mode 100644 src/testsuite/hpc_apps/quicksilver/quicksilver_parser_common.py rename src/{amd => testsuite}/list_tests.py (97%) rename src/{amd => testsuite}/match_fun_args_call.py (100%) create mode 100644 src/testsuite/stress/__init__.py rename src/{amd => testsuite}/targets.py (99%) rename src/{amd => testsuite}/test_classifier.py (97%) rename src/{amd => testsuite}/test_selector.py (74%) create mode 100644 src/testsuite/thirdparty/__init__.py rename src/{amd => testsuite}/version.py (100%) diff --git a/cfg.py b/cfg.py index 4557c64..62774ba 100644 --- a/cfg.py +++ b/cfg.py @@ -74,12 +74,7 @@ "hip": { "repo_url": "https://github.com/ROCm-Developer-Tools/HIP", "branch": None, - "commit_id": "586165ebc281eb9461898a5b2abbc74595f5d97d" - }, - "hipamd": { - "repo_url": "https://github.com/ROCm-Developer-Tools/HIPAMD", - "branch": None, - "commit_id": "de01ce04677243116dba52b59406a130517ea4c7" + "commit_id": "865b40d8bd8b26a7dfe4d9719e8dcf26f4b3afc6" }, "mixbench": { "repo_url": "https://github.com/ekondis/mixbench.git", @@ -91,14 +86,54 @@ "branch": None, "commit_id": "6fe81e19556ac26761a1c7247ae29fa88fb4e0ab" }, - "rocclr": { - "repo_url": "https://github.com/ROCm-Developer-Tools/ROCclr", + "mgbench": { + "repo_url": "https://github.com/tbennun/mgbench.git", + "branch": None, + "commit_id": "6f12d3848020af8f718074a30c68e6f0b232bfb3" + }, + "cuda_grep": { + "repo_url": "https://github.com/bkase/CUDA-grep.git", + "branch": None, + "commit_id": "fa6630eb0c3e782620ec3eaf989873dadc9a036b" + }, + "cuda_memtest": { + "repo_url": "https://github.com/ComputationalRadiationPhysics/cuda_memtest.git", + "branch": None, + "commit_id": "0cd3a996ce82682fcf50fa6f433b6f1f2ce1353d" + }, + "quicksilver": { + "repo_url": "https://github.com/LLNL/Quicksilver.git", + "branch": "AMD-HIP", + "commit_id": "bf073887bf73ef34de8025adaba51c6ad7fb15be" + }, + "gridtools": { + "repo_url": "https://github.com/GridTools/gridtools.git", + "branch": None, + "commit_id": "d33fa6fecee0a7bd9e080212c1038f0dbd31fe97" + }, + "gtbench": { + "repo_url": "https://github.com/GridTools/gtbench.git", + "branch": None, + "commit_id": "42687cce5085e0e175cb02fefe87fbb3d952fd5c" + }, + "kokkos": { + "repo_url": "https://github.com/kokkos/kokkos.git", + "branch": None, + "commit_id": "c28a8b03288b185f846ddfb1b7c08213e12e2634" + }, + "mfem": { + "repo_url": "https://github.com/mfem/mfem.git ./mfem", "branch": None, - "commit_id": "0ca4f7939969004b9080d814a30cc8e34cdac833" + "commit_id": "a3f0a5bb7ca874ec260d6f85afa3693cd6542497" }, - "opencl": { - "repo_url": "https://github.com/RadeonOpenCompute/ROCm-OpenCL-Runtime", + "Laghos": { + "repo_url": "https://github.com/CEED/Laghos.git", "branch": None, - "commit_id": "bbdc87e08b322d349f82bdd7575c8ce94d31d276" + "commit_id": "a7f6123d42847f6bdbdb614f5af876541f49cd16" + }, + "openmpi": { + "repo_url": "http://github.com/open-mpi/ompi.git openmpi", + "branch": "v4.0.x", + "commit_id": "4dc196d8c60aadeb34b0df9d226ab4c341004704" } } diff --git a/run.py b/run.py index 880ec85..1484323 100644 --- a/run.py +++ b/run.py @@ -26,8 +26,8 @@ sys.path.append(os.path.join(os.path.dirname(__file__), "src")) -from amd.TestersExecutor import TestersExecutor -from amd.list_tests import list_tests +from testsuite.TestersExecutor import TestersExecutor +from testsuite.list_tests import list_tests import cfg diff --git a/src/amd/applications/hip_samples/hip_samples_build.py b/src/amd/applications/hip_samples/hip_samples_build.py deleted file mode 100644 index 516be3c..0000000 --- a/src/amd/applications/hip_samples/hip_samples_build.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import os -from amd.common.hip_shell import execshellcmd - -class BuildRunCommon(): - ''' - In this class insert the build and execution steps for test cases - which are identical across different platforms (amd/nvidia/intel). - ''' - def __init__(self, path, logfile): - self.thistestpath = path - self.logfile = logfile - - def buildtest(self, env = None): - cmdcd = "cd " + self.thistestpath + ";" - cmd_clean = "make clean;" - cmd_build = "make" - cmdexc = cmdcd + cmd_clean + cmd_build - execshellcmd(cmdexc, self.logfile, env) - - def clean(self): - cmdcd = "cd " + self.thistestpath + ";" - cmd_clean = "make clean;" - cmdexc = cmdcd + cmd_clean - execshellcmd(cmdexc, None, None) - - -class BuildRunAmd(BuildRunCommon): - def __init__(self, path, logfile): - BuildRunCommon.__init__(self, path, logfile) - - def buildtest(self): - # In this function put the build steps for test cases - # which differ across platforms (amd/nvidia/intel) else - # invoke BuildRunCommon.buildtest - ret = BuildRunCommon.buildtest(self) - return ret - - -class BuildRunNvidia(BuildRunCommon): - def __init__(self, path, logfile): - self.hippath = os.path.join(os.getcwd(), "src/amd/conformance/HIP/") - self.hipamdpath = os.path.join(os.getcwd(),"src/amd/conformance/HIPAMD/") - BuildRunCommon.__init__(self, path, logfile) - - def getenvironmentvariables(self): - envtoset = os.environ.copy() - envtoset["HIP_PLATFORM"] = "nvidia" - envtoset["HIP_COMPILER"] = "nvcc" - envtoset["HIP_RUNTIME"] = "cuda" - envtoset["HIP_DIR"] = self.hippath - envtoset["HIP_AMD_DIR"] = self.hipamdpath - envtoset["HIP_PATH"] = os.path.join(self.hipamdpath, "build") - return envtoset - - def setupenvironmentfornvcc(self): - cmdcd = "cd " + self.hipamdpath + ";" - envtoset = self.getenvironmentvariables() - cmdsetenv = "rm -Rf build/;mkdir build;cd build;" - cmdbuildinstall =\ - "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install -DHIP_COMMON_DIR=\"$HIP_DIR\" -DHIP_AMD_BACKEND_SOURCE_DIR=\"$HIP_AMD_DIR\" ..;" - cmdbuildinstall += "make install;" - cmdexc = cmdcd + cmdsetenv + cmdbuildinstall - execshellcmd(cmdexc, self.logfile, envtoset) - - def applypatch(self): # To be deleted - cmd = "cd " + self.hippath + ";" - cmd += "patch -p0 < ../../applications/hip_samples/Samples_Patch_4.2.x;" - execshellcmd(cmd, self.logfile, None) - - def buildtest(self): - buildbindirpresent = os.path.isdir(\ - os.path.join(self.hipamdpath, "build/bin")) - buildincludedirpresent = os.path.isdir(\ - os.path.join(self.hipamdpath, "build/include")) - buildinstalldirpresent = os.path.isdir(\ - os.path.join(self.hipamdpath, "build/install")) - if not (buildbindirpresent & buildincludedirpresent & buildinstalldirpresent): - self.setupenvironmentfornvcc() - envtoset = self.getenvironmentvariables() - # In this function put the build steps for test cases - # which differ across platforms (amd/nvidia/intel) else - # invoke BuildRunCommon.buildtest - - # Apply Patch. This is temporary and will be removed once Hip Samples changes - # are available in HIP public repository. - self.applypatch() - ret = BuildRunCommon.buildtest(self, envtoset) - return ret diff --git a/src/amd/conformance/hip_dtest_build.py b/src/amd/conformance/hip_dtest_build.py deleted file mode 100644 index 31ad369..0000000 --- a/src/amd/conformance/hip_dtest_build.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import os, glob -import tempfile -import json -import re -from amd.common.hip_shell import execshellcmd_largedump, execshellcmd - -class BuildRunCommon(): - ''' - In this class insert the build and execution steps for test cases - which are identical across different platforms (amd/nvidia/intel). - ''' - def __init__(self, logfile): - self.logfile = logfile - self.hippath = os.path.join(os.getcwd(), "src/amd/conformance/HIP") - self.hipamdpath = os.path.join(os.getcwd(),"src/amd/conformance/HIPAMD") - self.rclrpath = os.path.join(os.getcwd(), "src/amd/conformance/ROCclr") - self.oclpath = os.path.join(os.getcwd(), "src/amd/conformance/ROCm-OpenCL-Runtime") - - # Fetches all available dtests using ctest - def get_all_ctest(self): - cmdexc = "cd " + self.hipamdpath + "/build;" - cmdexc += "ctest -N;" - with open("ctest.txt", "w+") as ctestlog: - execshellcmd_largedump(cmdexc, self.logfile, ctestlog, None) - testlist = [] - with open("ctest.txt", "r") as ctestlog: - for test in ctestlog: - if re.search("Test *#\d*:", test) != None: - dtest = re.sub("Test *#\d*: ", "", test) - dtest = re.sub("/", ".", dtest) - dtest = dtest.lstrip() - dtest = dtest.rstrip() - testlist.append(dtest) - os.remove("ctest.txt") - return testlist - - # Parse the test result - def parsetest(self, log): - log.seek(0) - text = log.read() - status = None - if re.search("100% tests passed", text) != None: - status = "PASSED" - else: - status = "FAILED" - return status - - # Execute the test case - def runtest(self, log, testcase, envtoset): - cmdtest = "ctest -R " + testcase - print("Executing command = " + cmdtest) - # run test - cmd = "cd " + os.path.join(self.hipamdpath, "build") + ";" - cmd += cmdtest + ";" - runlogdump = tempfile.TemporaryFile("w+") - execshellcmd_largedump(cmd, log, runlogdump, envtoset) - status = self.parsetest(runlogdump) - runlogdump.close() - return status - - -class BuildRunAmd(BuildRunCommon): - ''' - In this class insert the build and execution steps specific - for AMD platform. - ''' - def __init__(self, logfile): - BuildRunCommon.__init__(self, logfile) - - # Create the environment variables to set for build and execution - def get_envvar(self): - envtoset = os.environ.copy() - envtoset["ROCclr_DIR"] = self.rclrpath - envtoset["OPENCL_DIR"] = self.oclpath - envtoset["HIP_DIR"] = self.hippath - envtoset["HIP_AMD_DIR"] = self.hipamdpath - return envtoset - - # Validate if rocclr build is successful - def validate_rocclr_build(self): - status = True - rocclr_install_inc = os.path.join(os.getcwd(), "src/amd/conformance/opt/rocm/rocclr/include") - rocclr_install_lib = os.path.join(os.getcwd(), "src/amd/conformance/opt/rocm/rocclr/lib") - status &= os.path.isdir(rocclr_install_lib) - status &= os.path.isdir(rocclr_install_inc) - rocclr_binaries = glob.glob(rocclr_install_lib + "/*.a") - if len(rocclr_binaries) > 0: - status &= True - else: - status &= False - return status - - # Validate if HIP build is successful - def validate_hip_build(self): - status = True - hip_install_inc = os.path.join(self.hipamdpath, "build/install/include") - hip_install_lib = os.path.join(self.hipamdpath, "build/install/lib") - hip_install_bin = os.path.join(self.hipamdpath, "build/install/bin") - hip_binaries = glob.glob(hip_install_lib + "/*.so*") - status &= os.path.isdir(hip_install_inc) - status &= os.path.isdir(hip_install_lib) - status &= os.path.isdir(hip_install_bin) - if len(hip_binaries) > 0: - status &= True - else: - status &= False - return status - - # Build Rocclr and HIP for AMD - def build_package(self, env = None): - buildSuccess = True - # Set environment - envtoset = self.get_envvar() - # Build HIP - cmd = "cd \"$HIP_AMD_DIR\";" - cmd += "rm -Rf build;" - cmd += "mkdir -p build;cd build;" - cmd += "cmake -DHIP_COMPILER=clang -DHIP_PLATFORM=rocclr -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX=$PWD/install" +\ - " -DROCCLR_PATH=\"$ROCclr_DIR\" -DAMD_OPENCL_PATH=\"$OPENCL_DIR\" -DHIP_COMMON_DIR=\"$HIP_DIR\"" +\ - " -DCMAKE_PREFIX_PATH=\"/opt/rocm/lib/cmake/hsa-runtime64;/opt/rocm/lib/cmake/amd_comgr\" ..;" - cmd += "make -j8;make install;" - - cmdexc = cmd - runlogdump = tempfile.TemporaryFile("w+") - execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) - runlogdump.close() - - # Validate if HIP build is successful - buildSuccess = self.validate_hip_build() - if buildSuccess == False: - print("HIP build failed!") - return False - - # Build Dtest - cmd = "cd \"$HIP_AMD_DIR\";cd build;" - cmd += "make -j8 build_tests;" - cmdexc = cmd - runlogdump = tempfile.TemporaryFile("w+") - execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) - runlogdump.close() - return True - - # Execute test cases - def runtest(self, log, testcase): - envtoset = self.get_envvar() - return BuildRunCommon.runtest(self, log, testcase, envtoset) - - # Clean the test - def clean(self): - envtoset = self.get_envvar() - cmd = "cd \"$HIP_AMD_DIR\";" - cmd += "rm -Rf build;" - execshellcmd(cmd, None, envtoset) - -class BuildRunNvidia(BuildRunCommon): - ''' - In this class insert the build and execution steps specific - for NVIDIA platform. - ''' - def __init__(self, logfile): - BuildRunCommon.__init__(self, logfile) - - # Create the environment variables to set for build and execution - def get_envvar(self): - envtoset = os.environ.copy() - envtoset["HIP_PLATFORM"] = "nvidia" - envtoset["HIP_COMPILER"] = "nvcc" - envtoset["HIP_RUNTIME"] = "cuda" - envtoset["HIP_DIR"] = self.hippath - envtoset["HIP_AMD_DIR"] = self.hipamdpath - return envtoset - - # Validate if HIP build is successful - def validate_hip_build(self): - status = True - hip_install_inc = os.path.join(self.hipamdpath, "build/install/include") - hip_install_bin = os.path.join(self.hipamdpath, "build/install/bin") - status &= os.path.isdir(hip_install_inc) - status &= os.path.isdir(hip_install_bin) - return status - - # Build for NVIDIA - def build_package(self, env = None): - buildSuccess = True - # Set environment - envtoset = self.get_envvar() - # Build HIP - cmd = "cd \"$HIP_AMD_DIR\";" - cmd += "rm -Rf build;" - cmd += "mkdir -p build;cd build;" - cmd += "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install -DHIP_COMMON_DIR=\"$HIP_DIR\"" + \ - " -DHIP_AMD_BACKEND_SOURCE_DIR=\"$HIP_AMD_DIR\" .. ;" - cmd += "make install;" - cmdexc = cmd - runlogdump = tempfile.TemporaryFile("w+") - execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) - runlogdump.close() - # Validate if HIP build is successful - buildSuccess = self.validate_hip_build() - if buildSuccess == False: - print("HIP build failed!") - return False - # Build Dtest - cmd = "cd \"$HIP_AMD_DIR\";cd build;" - cmd += "make -j8 build_tests;" - cmdexc = cmd - runlogdump = tempfile.TemporaryFile("w+") - execshellcmd_largedump(cmdexc, self.logfile, runlogdump, envtoset) - runlogdump.close() - - return True - - # Execute test cases - def runtest(self, log, testcase): - envtoset = self.get_envvar() - return BuildRunCommon.runtest(self, log, testcase, envtoset) - - # Clean the test - def clean(self): - envtoset = self.get_envvar() - cmd = "cd \"$HIP_AMD_DIR\";" - cmd += "rm -Rf build;" - execshellcmd(cmd, None, envtoset) diff --git a/src/amd/AMD.py b/src/testsuite/AMD.py similarity index 100% rename from src/amd/AMD.py rename to src/testsuite/AMD.py diff --git a/src/amd/Test.py b/src/testsuite/Test.py similarity index 92% rename from src/amd/Test.py rename to src/testsuite/Test.py index 8ac32c2..eed9beb 100644 --- a/src/amd/Test.py +++ b/src/testsuite/Test.py @@ -19,17 +19,17 @@ # THE SOFTWARE. from typing import List, Union, Set, Dict -from amd.AMD import AMDObject -from amd.targets import Target +from testsuite.AMD import AMDObject +from testsuite.targets import Target from enum import Enum, auto -from amd.test_classifier import TestClassifier -from amd.config_processor import ConfigProcessor +from testsuite.test_classifier import TestClassifier +from testsuite.config_processor import ConfigProcessor class Test(AMDObject): def __init__(self): # Cycle Import - from amd.TesterRepository import Tester + from testsuite.TesterRepository import Tester AMDObject.__init__(self) self.test_name: Union[None, str] = None @@ -143,10 +143,16 @@ def __init__(self): def loadConfig(self): if self.config.HIP_PLATFORM == "amd": + print("Platform selected: amd") self.HIP_PLATFORM = HIP_PLATFORM.amd elif self.config.HIP_PLATFORM == "nvidia": + print("Platform selected: nvidia") self.HIP_PLATFORM = HIP_PLATFORM.nvidia + elif self.config.HIP_PLATFORM is None: + print("Platform selected: amd") + self.HIP_PLATFORM = HIP_PLATFORM.amd else: + print("Warning: Platform " + self.config.HIP_PLATFORM + " is not supported, defaulting to AMD") self.HIP_PLATFORM = HIP_PLATFORM.amd if self.config.Optimization_Level == 0: diff --git a/src/amd/TesterRepository.py b/src/testsuite/TesterRepository.py similarity index 93% rename from src/amd/TesterRepository.py rename to src/testsuite/TesterRepository.py index 921b6a6..2081302 100644 --- a/src/amd/TesterRepository.py +++ b/src/testsuite/TesterRepository.py @@ -18,12 +18,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.Test import Test, TestData, GetTestsData, LogLocation, Quick -from amd.test_classifier import TestClassifier -from amd.AMD import AMDObject -from amd.config_processor import ConfigProcessor -from amd.match_fun_args_call import match_fun_args_call -import amd +from testsuite.Test import Test, TestData, GetTestsData, LogLocation, Quick +from testsuite.test_classifier import TestClassifier +from testsuite.AMD import AMDObject +from testsuite.config_processor import ConfigProcessor +from testsuite.match_fun_args_call import match_fun_args_call +import testsuite from typing import List, Union import pkgutil @@ -54,7 +54,7 @@ def clean(self): class TesterRepository(AMDObject): def __init__(self): AMDObject.__init__(self) - self.getTestersFrom = [amd] + self.getTestersFrom = [testsuite] self.testers: Union[None, List[Tester]] = None def getTesters(self) -> List[Tester]: diff --git a/src/amd/TestersExecutor.py b/src/testsuite/TestersExecutor.py similarity index 98% rename from src/amd/TestersExecutor.py rename to src/testsuite/TestersExecutor.py index 2a12a06..a33f368 100644 --- a/src/amd/TestersExecutor.py +++ b/src/testsuite/TestersExecutor.py @@ -18,10 +18,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.TesterRepository import TesterRepository, Tester, Test -from amd.test_selector import TestSelector -from amd.config_processor import ConfigProcessor -from amd.Test import TestResult +from testsuite.TesterRepository import TesterRepository, Tester, Test +from testsuite.test_selector import TestSelector +from testsuite.config_processor import ConfigProcessor +from testsuite.Test import TestResult import os import traceback diff --git a/src/amd/__init__.py b/src/testsuite/__init__.py similarity index 100% rename from src/amd/__init__.py rename to src/testsuite/__init__.py diff --git a/src/amd/applications/__init__.py b/src/testsuite/applications/__init__.py similarity index 100% rename from src/amd/applications/__init__.py rename to src/testsuite/applications/__init__.py diff --git a/src/amd/applications/application_test_classifier.py b/src/testsuite/applications/application_test_classifier.py similarity index 96% rename from src/amd/applications/application_test_classifier.py rename to src/testsuite/applications/application_test_classifier.py index 9dd0fd0..21c0ea4 100644 --- a/src/amd/applications/application_test_classifier.py +++ b/src/testsuite/applications/application_test_classifier.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.test_classifier import TestClassifier +from testsuite.test_classifier import TestClassifier from typing import Union diff --git a/src/amd/applications/hip_examples/__init__.py b/src/testsuite/applications/cuda_grep/__init__.py similarity index 100% rename from src/amd/applications/hip_examples/__init__.py rename to src/testsuite/applications/cuda_grep/__init__.py diff --git a/src/testsuite/applications/cuda_grep/cuda_grep.py b/src/testsuite/applications/cuda_grep/cuda_grep.py new file mode 100644 index 0000000..f43cc27 --- /dev/null +++ b/src/testsuite/applications/cuda_grep/cuda_grep.py @@ -0,0 +1,151 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.TesterRepository import Tester, Test, TestData +from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from testsuite.test_classifier import TestClassifier +from testsuite.applications.cuda_grep.cuda_grep_build_amd import BuildRunAmd +from testsuite.applications.cuda_grep.cuda_grep_build_nvidia import BuildRunNvidia +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import execshellcmd + +import os +import re +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, cwd, binary): + self.cwdAbs = cwd + self.binary = binary + self.app_path = os.path.join(self.cwdAbs,\ + "src/testsuite/applications/cuda_grep/") + self.app_root = os.path.join(self.app_path, "CUDA-grep/") + self.thistestpath = self.app_root + self.prepareobj = None + self.apprepo = "" # Default + self.appbranch = "" + self.appcommitId = "" + + def set_cudagrep_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["cuda_grep"].repo_url != None: + self.apprepo = test_data.repos["cuda_grep"].repo_url + else: + validrepconfig &= False + if test_data.repos["cuda_grep"].branch != None: + self.appbranch = test_data.repos["cuda_grep"].branch + if test_data.repos["cuda_grep"].commit_id != None: + self.appcommitId = test_data.repos["cuda_grep"].commit_id + return validrepconfig + + def downloadtest(self, logFile, test_data: HIPTestData): + ret = HipPackages().pull_repo(logFile, self.apprepo,\ + self.appbranch, self.appcommitId, "cudagrep") + return ret + + def buildtest(self, logFile, platform): + isBinaryPresent = True + if platform == HIP_PLATFORM.nvidia: + self.prepareobj = BuildRunNvidia(self.thistestpath, logFile, self.binary) + elif platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.thistestpath, logFile, self.binary) + else: + print("Invalid Platform") + return False + buildstatus = self.prepareobj.buildtest() + if buildstatus == False: + return False + # Check if test binary is created + if not os.path.isfile(\ + os.path.join(self.thistestpath, self.binary)): + isBinaryPresent &= False + return isBinaryPresent + + def clean(self): + if self.prepareobj != None: + self.prepareobj.clean() + + def runtest(self): + if self.prepareobj != None: + self.prepareobj.runtest() + + def parse_result(self): + if self.prepareobj != None: + return self.prepareobj.parse_result() + return False + +class CUDA_GREP(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"cuda_grep": matched_with_names}) + +class MINI_APP(CUDA_GREP): + def __init__(self): + CUDA_GREP.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + CUDA_GREP.add_matched_with_names(self, {"mini-app": matched_with_names}) + + +# Test CUDA-grep +class cudagrep(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "nfa") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + mini_app = MINI_APP() + mini_app.add_matched_with_names() + test.classifiers = [mini_app] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== CUDA-grep Test ===============") + # Set repo info + isrepocfgvalid = self.set_cudagrep_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/CUDA-grep.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest() + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL diff --git a/src/testsuite/applications/cuda_grep/cuda_grep_build_amd.py b/src/testsuite/applications/cuda_grep/cuda_grep_build_amd.py new file mode 100644 index 0000000..528384e --- /dev/null +++ b/src/testsuite/applications/cuda_grep/cuda_grep_build_amd.py @@ -0,0 +1,66 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.applications.cuda_grep.cuda_grep_parser_common import CudaGrepParser + +class BuildRunAmd(): + def __init__(self, thistestpath, logFile, binary): + self.thistestpath = thistestpath + self.logFile = logFile + self.runpath = os.path.join(self.thistestpath, "testbench") + self.runlog = os.path.join(self.thistestpath, "testbench/RESULTS") + self.binary = binary + + def buildtest(self): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + print("Building cuda_grep..") + cmdcd = "cd " + self.thistestpath + ";" + cmd_hipify = "" + cmd_modify = "" + if not os.path.isfile(os.path.join(self.thistestpath, "hipified")): + cmd_hipify = "find . -type f \( -iname \*.h -o -iname \*.cpp -o -iname \*.cu -o -iname \*.cuh \) | sed 's|^./||'\ + | xargs -t -I % sh -c '/opt/rocm/bin/hipify-perl % > hip_%; rm %; mv hip_% %;';" + cmd_modify = "sed -i 's/#include //g' putil.cu; touch hipified;" + cmd_build = "/opt/rocm/bin/hipcc -O3 -m64 pnfa.cu putil.cu nfa.cpp nfautil.cpp regex.cpp -o " + self.binary + ";" + cmdexc = cmdcd + cmd_hipify + cmd_modify + cmd_build + execshellcmd(cmdexc, self.logFile, None) + return True + + def runtest(self): + print("Running cuda_grep..") + cmdexc = "cd " + self.runpath + ";" + "./runtests.sh;" + execshellcmd(cmdexc, self.logFile, None) + + def clean(self): + print("Cleaning cuda_grep..") + cmdcd = "cd " + self.thistestpath + ";" + cmdrm = "rm -f " + self.binary + ";" + cmdrmres = "rm -f " + self.runlog + ";" + cmdexc = cmdcd + cmdrm + cmdrmres + execshellcmd(cmdexc, None, None) + + def parse_result(self): + return CudaGrepParser(self.runlog).parse() diff --git a/src/testsuite/applications/cuda_grep/cuda_grep_build_nvidia.py b/src/testsuite/applications/cuda_grep/cuda_grep_build_nvidia.py new file mode 100644 index 0000000..897dfd5 --- /dev/null +++ b/src/testsuite/applications/cuda_grep/cuda_grep_build_nvidia.py @@ -0,0 +1,75 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.applications.cuda_grep.cuda_grep_parser_common import CudaGrepParser + +class BuildRunNvidia(): + def __init__(self, thistestpath, logFile, binary): + self.thistestpath = thistestpath + self.logFile = logFile + self.runpath = os.path.join(self.thistestpath, "testbench") + self.runlog = os.path.join(self.thistestpath, "testbench/RESULTS") + self.binary = binary + + def getenvironmentvariables(self): + envtoset = os.environ.copy() + envtoset["HIP_PLATFORM"] = "nvidia" + envtoset["HIP_COMPILER"] = "nvcc" + envtoset["HIP_RUNTIME"] = "cuda" + return envtoset + + def buildtest(self): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + print("Building cuda_grep..") + env = self.getenvironmentvariables() + cmdcd = "cd " + self.thistestpath + ";" + cmd_hipify = "" + cmd_modify = "" + if not os.path.isfile(os.path.join(self.thistestpath, "hipified")): + cmd_hipify = "find . -type f \( -iname \*.h -o -iname \*.cpp -o -iname \*.cu -o -iname \*.cuh \) | sed 's|^./||'\ + | xargs -t -I % sh -c '/opt/rocm/bin/hipify-perl % > hip_%; rm %; mv hip_% %;';" + cmd_modify = "sed -i 's/#include //g' putil.cu; touch hipified;" + cmd_build = "/opt/rocm/bin/hipcc -O3 -m64 pnfa.cu putil.cu nfa.cpp nfautil.cpp regex.cpp -o " + self.binary + ";" + cmdexc = cmdcd + cmd_hipify + cmd_modify + cmd_build + execshellcmd(cmdexc, self.logFile, env) + return True + + def runtest(self): + print("Running cuda_grep..") + env = self.getenvironmentvariables() + cmdexc = "cd " + self.runpath + ";" + "./runtests.sh;" + execshellcmd(cmdexc, self.logFile, env) + + def clean(self): + print("Cleaning cuda_grep..") + cmdcd = "cd " + self.thistestpath + ";" + cmdrm = "rm -f " + self.binary + ";" + cmdrmres = "rm -f " + self.runlog + ";" + cmdexc = cmdcd + cmdrm + cmdrmres + execshellcmd(cmdexc, None, None) + + def parse_result(self): + return CudaGrepParser(self.runlog).parse() diff --git a/src/testsuite/applications/cuda_grep/cuda_grep_parser_common.py b/src/testsuite/applications/cuda_grep/cuda_grep_parser_common.py new file mode 100644 index 0000000..4ff0a75 --- /dev/null +++ b/src/testsuite/applications/cuda_grep/cuda_grep_parser_common.py @@ -0,0 +1,35 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.common.hip_shell import execshellcmd + +class CudaGrepParser(): + def __init__(self, results): + self.results = results + + def parse(self): + passed = False + count = 0 + with open(self.results, 'r') as testlog: + resulttxt = testlog.read() + count = resulttxt.count("All tests passed") + if count > 0: + passed = True + return passed diff --git a/src/amd/applications/hip_samples/__init__.py b/src/testsuite/applications/cuda_memtest/__init__.py similarity index 100% rename from src/amd/applications/hip_samples/__init__.py rename to src/testsuite/applications/cuda_memtest/__init__.py diff --git a/src/testsuite/applications/cuda_memtest/cuda_memtest.py b/src/testsuite/applications/cuda_memtest/cuda_memtest.py new file mode 100644 index 0000000..6d73f9b --- /dev/null +++ b/src/testsuite/applications/cuda_memtest/cuda_memtest.py @@ -0,0 +1,602 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.TesterRepository import Tester, Test, TestData +from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from testsuite.test_classifier import TestClassifier +from testsuite.applications.cuda_memtest.cuda_memtest_build_amd import BuildRunAmd +from testsuite.applications.cuda_memtest.cuda_memtest_build_nvidia import BuildRunNvidia +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import execshellcmd + +import os +import re +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, cwd, binary): + self.cwdAbs = cwd + self.binary = binary + self.app_path = os.path.join(self.cwdAbs,\ + "src/testsuite/applications/cuda_memtest/") + self.app_root = os.path.join(self.app_path, "cuda_memtest/") + self.thistestpath = self.app_root + self.prepareobj = None + self.apprepo = "" # Default + self.appbranch = "" + self.appcommitId = "" + + def set_cudamemtest_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["cuda_memtest"].repo_url != None: + self.apprepo = test_data.repos["cuda_memtest"].repo_url + else: + validrepconfig &= False + if test_data.repos["cuda_memtest"].branch != None: + self.appbranch = test_data.repos["cuda_memtest"].branch + if test_data.repos["cuda_memtest"].commit_id != None: + self.appcommitId = test_data.repos["cuda_memtest"].commit_id + return validrepconfig + + def downloadtest(self, logFile, test_data: HIPTestData): + ret = HipPackages().pull_repo(logFile, self.apprepo,\ + self.appbranch, self.appcommitId, "cudamemtest") + return ret + + def buildtest(self, logFile, platform): + isBinaryPresent = True + if platform == HIP_PLATFORM.nvidia: + self.prepareobj = BuildRunNvidia(self.thistestpath, logFile, self.binary) + elif platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.thistestpath, logFile, self.binary) + else: + print("Invalid Platform") + return False + buildstatus = self.prepareobj.buildtest() + if buildstatus == False: + return False + # Check if test binary is created + if not os.path.isfile(\ + os.path.join(self.thistestpath, self.binary)): + isBinaryPresent &= False + return isBinaryPresent + + def clean(self): + if self.prepareobj != None: + self.prepareobj.clean() + + def runtest(self, testnum): + if self.prepareobj != None: + self.prepareobj.runtest(testnum) + + def parse_result(self): + if self.prepareobj != None: + return self.prepareobj.parse_result() + return False + +class CUDA_MEMTEST(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"cuda_memtest": matched_with_names}) + +class STRESS(CUDA_MEMTEST): + def __init__(self): + CUDA_MEMTEST.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + CUDA_MEMTEST.add_matched_with_names(self, {"stress": matched_with_names}) + + +# Test0 cuda_memtest +class cudamemtest0(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest0 ===============") + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest0.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(0) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + +# Test1 cuda_memtest +class cudamemtest1(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest1 ===============") + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest1.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(1) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + +# Test2 cuda_memtest +class cudamemtest2(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest2 ===============") + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest2.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(2) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + +# Test3 cuda_memtest +class cudamemtest3(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest3 ===============") + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest3.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(3) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + +# Test4 cuda_memtest +class cudamemtest4(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest4 ===============") + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest4.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(4) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test5 cuda_memtest +class cudamemtest5(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest5 ===============") + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest5.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(5) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test6 cuda_memtest +class cudamemtest6(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest6 ===============") + if False: + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest6.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(6) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + +# Test7 cuda_memtest +class cudamemtest7(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest7 ===============") + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest7.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(7) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test8 cuda_memtest +class cudamemtest8(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest8 ===============") + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest8.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(8) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test9 cuda_memtest +class cudamemtest9(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest9 ===============") + if False: + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest9.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(9) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + else: + test_data.test_result = TestResult.SKIP + +# Test10 cuda_memtest +class cudamemtest10(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "cuda_memtest") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = STRESS() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== cudamemtest10 ===============") + # Set repo info + isrepocfgvalid = self.set_cudamemtest_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cudamemtest10.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(10) + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + diff --git a/src/testsuite/applications/cuda_memtest/cuda_memtest_build_amd.py b/src/testsuite/applications/cuda_memtest/cuda_memtest_build_amd.py new file mode 100644 index 0000000..78d6e55 --- /dev/null +++ b/src/testsuite/applications/cuda_memtest/cuda_memtest_build_amd.py @@ -0,0 +1,65 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.applications.cuda_memtest.cuda_memtest_parser_common import CudaMemtestParser + +class BuildRunAmd(): + def __init__(self, thistestpath, logFile, binary): + self.thistestpath = thistestpath + self.logFile = logFile + self.runlog = "" + self.binary = binary + + def buildtest(self): + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + print("Building cuda_memtest..") + cmdcd = "cd " + self.thistestpath + ";" + cmd_hipify = "" + cmd_modify = "" + if not os.path.isfile(os.path.join(self.thistestpath, "hipified")): + cmd_hipify = "ls cuda_memtest.* misc.* tests.cu | xargs -t -I % sh -c '/opt/rocm/bin/hipify-perl % > hip_%; rm %; mv hip_% %;';" + cmd_modify = "patch -p0 < ../cuda_memtest_patch; touch hipified;" + cmd_build = "/opt/rocm/bin/hipcc -DENABLE_NVML=0 cuda_memtest.cu misc.cpp tests.cu -o " + self.binary + ";" + cmdexc = cmdcd + cmd_hipify + cmd_modify + cmd_build + execshellcmd(cmdexc, self.logFile, None) + return True + + def runtest(self, testnum): + print("Running cuda_memtest " + str(testnum) + "..") + cmdcd = "cd " + self.thistestpath + ";" + cmdrun = "./" + self.binary + " --disable_all --enable_test " + str(testnum) + " --num_passes 1" + cmdexc = cmdcd + cmdrun + self.runlog = execshellcmd(cmdexc, self.logFile, None) + + def clean(self): + print("Cleaning cuda_memtest..") + cmdcd = "cd " + self.thistestpath + ";" + cmdrm = "rm -f " + self.binary + ";" + cmdexc = cmdcd + cmdrm + execshellcmd(cmdexc, None, None) + + def parse_result(self): + return CudaMemtestParser(self.runlog).parse() diff --git a/src/testsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py b/src/testsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py new file mode 100644 index 0000000..408839e --- /dev/null +++ b/src/testsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py @@ -0,0 +1,74 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.applications.cuda_memtest.cuda_memtest_parser_common import CudaMemtestParser + +class BuildRunNvidia(): + def __init__(self, thistestpath, logFile, binary): + self.thistestpath = thistestpath + self.logFile = logFile + self.runlog = "" + self.binary = binary + + def getenvironmentvariables(self): + envtoset = os.environ.copy() + envtoset["HIP_PLATFORM"] = "nvidia" + envtoset["HIP_COMPILER"] = "nvcc" + envtoset["HIP_RUNTIME"] = "cuda" + return envtoset + + def buildtest(self): + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + print("Building cuda_memtest..") + env = self.getenvironmentvariables() + cmdcd = "cd " + self.thistestpath + ";" + cmd_hipify = "" + cmd_modify = "" + if not os.path.isfile(os.path.join(self.thistestpath, "hipified")): + cmd_hipify = "ls cuda_memtest.* misc.* tests.cu | xargs -t -I % sh -c '/opt/rocm/bin/hipify-perl % > hip_%; rm %; mv hip_% %;';" + cmd_modify = "patch -p0 < ../cuda_memtest_patch; touch hipified;" + cmd_build = "/opt/rocm/bin/hipcc -DENABLE_NVML=0 cuda_memtest.cu misc.cpp tests.cu -o " + self.binary + ";" + cmdexc = cmdcd + cmd_hipify + cmd_modify + cmd_build + execshellcmd(cmdexc, self.logFile, env) + return True + + def runtest(self, testnum): + print("Running cuda_memtest " + str(testnum) + "..") + env = self.getenvironmentvariables() + cmdcd = "cd " + self.thistestpath + ";" + cmdrun = "./" + self.binary + " --disable_all --enable_test " + str(testnum) + " --num_passes 1" + cmdexc = cmdcd + cmdrun + self.runlog = execshellcmd(cmdexc, self.logFile, env) + + def clean(self): + print("Cleaning cuda_memtest..") + cmdcd = "cd " + self.thistestpath + ";" + cmdrm = "rm -f " + self.binary + ";" + cmdexc = cmdcd + cmdrm + execshellcmd(cmdexc, None, None) + + def parse_result(self): + return CudaMemtestParser(self.runlog).parse() diff --git a/src/testsuite/applications/cuda_memtest/cuda_memtest_parser_common.py b/src/testsuite/applications/cuda_memtest/cuda_memtest_parser_common.py new file mode 100644 index 0000000..d3eea70 --- /dev/null +++ b/src/testsuite/applications/cuda_memtest/cuda_memtest_parser_common.py @@ -0,0 +1,34 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.common.hip_shell import execshellcmd +import re + +class CudaMemtestParser(): + def __init__(self, results): + self.results = results + + def parse(self): + passed = False + statement1lst = re.findall("Attached to device \d+ successfully\.", self.results) + statement2lst = re.findall("Test\d+ finished in \d+\.\d+ seconds", self.results) + if len(statement1lst) == len(statement2lst): + passed = True + return passed diff --git a/src/testsuite/applications/cuda_memtest/cuda_memtest_patch b/src/testsuite/applications/cuda_memtest/cuda_memtest_patch new file mode 100644 index 0000000..5a09723 --- /dev/null +++ b/src/testsuite/applications/cuda_memtest/cuda_memtest_patch @@ -0,0 +1,11 @@ +--- cuda_memtest.cu 2021-09-21 20:23:48.117207109 +0530 ++++ cuda_memtest_modified.cu 2021-09-21 20:20:52.553149253 +0530 +@@ -168,7 +168,7 @@ + { + //create cuda mapped memory + hipHostMalloc((void**)&mappedHostPtr,tot_num_blocks* BLOCKSIZE,hipHostMallocMapped); +- hipHostGetDevicePointer(&ptr,mappedHostPtr,0); ++ hipHostGetDevicePointer((void **)&ptr,mappedHostPtr,0); + } + else + { diff --git a/src/amd/common/__init__.py b/src/testsuite/applications/hip_examples/__init__.py similarity index 100% rename from src/amd/common/__init__.py rename to src/testsuite/applications/hip_examples/__init__.py diff --git a/src/amd/applications/hip_examples/hip_examples.py b/src/testsuite/applications/hip_examples/hip_examples.py similarity index 96% rename from src/amd/applications/hip_examples/hip_examples.py rename to src/testsuite/applications/hip_examples/hip_examples.py index 458385d..04a8351 100644 --- a/src/amd/applications/hip_examples/hip_examples.py +++ b/src/testsuite/applications/hip_examples/hip_examples.py @@ -18,13 +18,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.TesterRepository import Tester, Test, TestData -from amd.Test import HIPTestData, TestResult, HIP_PLATFORM +from testsuite.TesterRepository import Tester, Test, TestData +from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from amd.test_classifier import TestClassifier -from amd.applications.hip_examples.hip_examples_build import BuildRunAmd, BuildRunNvidia -from amd.common.hip_get_packages import HipPackages -from amd.common.hip_shell import * +from testsuite.test_classifier import TestClassifier +from testsuite.applications.hip_examples.hip_examples_build_amd import BuildRunAmd +from testsuite.applications.hip_examples.hip_examples_build_nvidia import BuildRunNvidia +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import * import os @@ -33,19 +34,13 @@ class PrepareTest(): def __init__(self, path, cwd): self.cwdAbs = cwd self.appPath = os.path.join(self.cwdAbs,\ - "src/amd/applications/hip_examples/") + "src/testsuite/applications/hip_examples/") self.examplepath = os.path.join(self.appPath, "HIP-Examples/") self.thistestpath = os.path.join(self.examplepath, path) self.prepareobj = None self.apprepo = "" # Default self.appbranch = "" self.appcommitId = "" - self.hiprepo = "" # Default - self.hipbranch = "" - self.hipcommitId = "" - self.hipamdrepo = "" # Default - self.hipamdbranch = "" - self.hipamdcommitId = "" self.gpustrm_repo = "" self.gpustrm_branch = "" self.gpustrm_commitId = "" @@ -53,25 +48,6 @@ def __init__(self, path, cwd): self.mixbn_branch = "" self.mixbn_commitId = "" - def set_hip_repoinfo(self, test_data: HIPTestData): - if test_data.repos["hip"].repo_url != None: - self.hiprepo = test_data.repos["hip"].repo_url - else: - return False - if test_data.repos["hip"].branch != None: - self.hipbranch = test_data.repos["hip"].branch - if test_data.repos["hip"].commit_id != None: - self.hipcommitId = test_data.repos["hip"].commit_id - if test_data.repos["hipamd"].repo_url != None: - self.hipamdrepo = test_data.repos["hipamd"].repo_url - else: - return False - if test_data.repos["hipamd"].branch != None: - self.hipamdbranch = test_data.repos["hipamd"].branch - if test_data.repos["hipamd"].commit_id != None: - self.hipamdcommitId = test_data.repos["hipamd"].commit_id - return True - def set_hipex_repoinfo(self, test_data: HIPTestData): validrepconfig = True if test_data.repos["hip_examples"].repo_url != None: @@ -83,7 +59,6 @@ def set_hipex_repoinfo(self, test_data: HIPTestData): if test_data.repos["hip_examples"].commit_id != None: self.appcommitId = test_data.repos["hip_examples"].commit_id - validrepconfig &= self.set_hip_repoinfo(test_data) return validrepconfig def set_mixben_repoinfo(self, test_data: HIPTestData): @@ -97,7 +72,6 @@ def set_mixben_repoinfo(self, test_data: HIPTestData): if test_data.repos["mixbench"].commit_id != None: self.mixbn_commitId = test_data.repos["mixbench"].commit_id - validrepconfig &= self.set_hip_repoinfo(test_data) return validrepconfig def set_gpustr_repoinfo(self, test_data: HIPTestData): @@ -111,31 +85,20 @@ def set_gpustr_repoinfo(self, test_data: HIPTestData): if test_data.repos["gpu_stream"].commit_id != None: self.gpustrm_commitId = test_data.repos["gpu_stream"].commit_id - validrepconfig &= self.set_hip_repoinfo(test_data) return validrepconfig - def download_deppackage(self, logFile): - ret = HipPackages().pull_repo(logFile, self.hiprepo, self.hipbranch,\ - self.hipcommitId, "HIP") - ret = ret & HipPackages().pull_repo(logFile, self.hipamdrepo, self.hipamdbranch,\ - self.hipamdcommitId, "hipamd") - return ret - def download_hipexample(self, logFile): - ret = self.download_deppackage(logFile) - ret &= HipPackages().pull_repo(logFile, self.apprepo,\ + ret = HipPackages().pull_repo(logFile, self.apprepo,\ self.appbranch, self.appcommitId, "hip_examples") return ret def download_gpustream(self, logFile): - ret = self.download_deppackage(logFile) - ret &= HipPackages().pull_repo(logFile, self.gpustrm_repo,\ + ret = HipPackages().pull_repo(logFile, self.gpustrm_repo,\ self.gpustrm_branch, self.gpustrm_commitId, "gpu-stream") return ret def download_mixbench(self, logFile): - ret = self.download_deppackage(logFile) - ret &= HipPackages().pull_repo(logFile, self.mixbn_repo,\ + ret = HipPackages().pull_repo(logFile, self.mixbn_repo,\ self.mixbn_branch, self.mixbn_commitId, "mixbench") return ret @@ -150,10 +113,14 @@ def buildtest(self, logFile, platform, testid): return self.prepareobj.buildtest(logFile, testid) def clean(self, testid): - self.prepareobj.clean(testid) + if self.prepareobj != None: + self.prepareobj.clean(testid) def runtest(self, logFile, testid): - return self.prepareobj.runtest(logFile, testid) + status = False + if self.prepareobj != None: + status = self.prepareobj.runtest(logFile, testid) + return status class EXAMPLES(TestClassifier): diff --git a/src/testsuite/applications/hip_examples/hip_examples_build_amd.py b/src/testsuite/applications/hip_examples/hip_examples_build_amd.py new file mode 100644 index 0000000..4ad70ec --- /dev/null +++ b/src/testsuite/applications/hip_examples/hip_examples_build_amd.py @@ -0,0 +1,49 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.applications.hip_examples.hip_examples_parser import Hip_examples_parser +from testsuite.applications.hip_examples.hip_examples_build_common import BuildRunCommon +from testsuite.common.hip_shell import * + +class BuildRunAmd(BuildRunCommon): + def __init__(self, path): + BuildRunCommon.__init__(self, path) + + def buildtest(self, logFile, testid): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + ret = BuildRunCommon.buildtest(self, logFile, testid) + return ret + + def runtest(self, logFile, testid): + # In this function put the execution steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + ret = BuildRunCommon.runtest(self, logFile, testid) + return ret + + # Clean all generated binaries + def clean(self, testid): + BuildRunCommon.clean(self, testid) diff --git a/src/amd/applications/hip_examples/hip_examples_build.py b/src/testsuite/applications/hip_examples/hip_examples_build_common.py similarity index 81% rename from src/amd/applications/hip_examples/hip_examples_build.py rename to src/testsuite/applications/hip_examples/hip_examples_build_common.py index 438940c..5952beb 100644 --- a/src/amd/applications/hip_examples/hip_examples_build.py +++ b/src/testsuite/applications/hip_examples/hip_examples_build_common.py @@ -21,8 +21,8 @@ import os import tempfile -from amd.applications.hip_examples.hip_examples_parser import Hip_examples_parser -from amd.common.hip_shell import * +from testsuite.applications.hip_examples.hip_examples_parser import Hip_examples_parser +from testsuite.common.hip_shell import * class BuildRunCommon(): ''' @@ -295,82 +295,3 @@ def clean(self, testid): for binary in self.binarydic[testid]: if os.path.exists(self.thistestpath + binary): os.remove(self.thistestpath + binary) - -class BuildRunAmd(BuildRunCommon): - def __init__(self, path): - BuildRunCommon.__init__(self, path) - - def buildtest(self, logFile, testid): - # In this function put the build steps for test cases - # which differ across platforms (amd/nvidia/intel) else - # invoke BuildRunCommon.buildtest - ret = BuildRunCommon.buildtest(self, logFile, testid) - return ret - - def runtest(self, logFile, testid): - # In this function put the execution steps for test cases - # which differ across platforms (amd/nvidia/intel) else - # invoke BuildRunCommon.buildtest - ret = BuildRunCommon.runtest(self, logFile, testid) - return ret - - # Clean all generated binaries - def clean(self, testid): - BuildRunCommon.clean(self, testid) - - -class BuildRunNvidia(BuildRunCommon): - def __init__(self, path): - self.hippath = os.path.join(os.getcwd(),"src/amd/conformance/HIP/") - self.hipamdpath = os.path.join(os.getcwd(),"src/amd/conformance/HIPAMD/") - BuildRunCommon.__init__(self, path) - - def getenvironmentvariables(self): - envtoset = os.environ.copy() - envtoset["HIP_PLATFORM"] = "nvidia" - envtoset["HIP_COMPILER"] = "nvcc" - envtoset["HIP_RUNTIME"] = "cuda" - envtoset["HIP_DIR"] = self.hippath - envtoset["HIP_AMD_DIR"] = self.hipamdpath - envtoset["HIP_PATH"] = os.path.join(self.hipamdpath, "build") - return envtoset - - def setupenvironmentfornvcc(self, logFile): - cmdcd = "cd " + self.hipamdpath + ";" - envtoset = self.getenvironmentvariables() - cmdsetenv = "rm -Rf build/;mkdir build;cd build;" - cmdbuildinstall =\ - "cmake -DHIP_PLATFORM=nvidia -DCMAKE_INSTALL_PREFIX=$PWD/install -DHIP_COMMON_DIR=\"$HIP_DIR\" -DHIP_AMD_BACKEND_SOURCE_DIR=\"$HIP_AMD_DIR\" ..;" - cmdbuildinstall += "make install;" - cmdexc = cmdcd + cmdsetenv + cmdbuildinstall - execshellcmd(cmdexc, logFile, envtoset) - - def buildtest(self, logFile, testid): - buildbindirpresent = os.path.isdir(\ - os.path.join(self.hipamdpath, "build/bin")) - buildincludedirpresent = os.path.isdir(\ - os.path.join(self.hipamdpath, "build/include")) - buildinstalldirpresent = os.path.isdir(\ - os.path.join(self.hipamdpath, "build/install")) - if not (buildbindirpresent & buildincludedirpresent & buildinstalldirpresent): - self.setupenvironmentfornvcc(logFile) - env = self.getenvironmentvariables() - # In this function put the build steps for test cases - # which differ across platforms (amd/nvidia/intel) else - # invoke BuildRunCommon.buildtest - ret = BuildRunCommon.buildtest(self, logFile, testid, env) - return ret - - def runtest(self, logFile, testid): - res = True - env = self.getenvironmentvariables() - # In this function put the execution steps for test cases - # which differ across platforms (amd/nvidia/intel) else - # invoke BuildRunCommon.buildtest - ret = BuildRunCommon.runtest(self, logFile, testid, env) - return ret - - # Clean all generated binaries - def clean(self, testid): - BuildRunCommon.clean(self, testid) - diff --git a/src/testsuite/applications/hip_examples/hip_examples_build_nvidia.py b/src/testsuite/applications/hip_examples/hip_examples_build_nvidia.py new file mode 100644 index 0000000..fa15172 --- /dev/null +++ b/src/testsuite/applications/hip_examples/hip_examples_build_nvidia.py @@ -0,0 +1,61 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os + +from testsuite.applications.hip_examples.hip_examples_parser import Hip_examples_parser +from testsuite.applications.hip_examples.hip_examples_build_common import BuildRunCommon +from testsuite.common.hip_shell import * + +class BuildRunNvidia(BuildRunCommon): + def __init__(self, path): + self.hippath = os.path.join(os.getcwd(),"src/testsuite/conformance/HIP/") + BuildRunCommon.__init__(self, path) + + def getenvironmentvariables(self): + envtoset = os.environ.copy() + envtoset["HIP_PLATFORM"] = "nvidia" + envtoset["HIP_COMPILER"] = "nvcc" + envtoset["HIP_RUNTIME"] = "cuda" + envtoset["HIP_PATH"] = "/opt/rocm/hip" + return envtoset + + def buildtest(self, logFile, testid): + env = self.getenvironmentvariables() + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + ret = BuildRunCommon.buildtest(self, logFile, testid, env) + return ret + + def runtest(self, logFile, testid): + env = self.getenvironmentvariables() + # In this function put the execution steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + ret = BuildRunCommon.runtest(self, logFile, testid, env) + return ret + + # Clean all generated binaries + def clean(self, testid): + BuildRunCommon.clean(self, testid) diff --git a/src/amd/applications/hip_examples/hip_examples_parser.py b/src/testsuite/applications/hip_examples/hip_examples_parser.py similarity index 100% rename from src/amd/applications/hip_examples/hip_examples_parser.py rename to src/testsuite/applications/hip_examples/hip_examples_parser.py diff --git a/src/amd/applications/hip_samples/Samples_Patch_4.2.x b/src/testsuite/applications/hip_samples/Samples_Patch_4.2.x similarity index 100% rename from src/amd/applications/hip_samples/Samples_Patch_4.2.x rename to src/testsuite/applications/hip_samples/Samples_Patch_4.2.x diff --git a/src/amd/conformance/__init__.py b/src/testsuite/applications/hip_samples/__init__.py similarity index 100% rename from src/amd/conformance/__init__.py rename to src/testsuite/applications/hip_samples/__init__.py diff --git a/src/amd/applications/hip_samples/hip_samples.py b/src/testsuite/applications/hip_samples/hip_samples.py similarity index 97% rename from src/amd/applications/hip_samples/hip_samples.py rename to src/testsuite/applications/hip_samples/hip_samples.py index c400c02..8325b0e 100644 --- a/src/amd/applications/hip_samples/hip_samples.py +++ b/src/testsuite/applications/hip_samples/hip_samples.py @@ -18,13 +18,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.TesterRepository import Tester, Test, TestData -from amd.Test import HIPTestData, TestResult, HIP_PLATFORM +from testsuite.TesterRepository import Tester, Test, TestData +from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from amd.test_classifier import TestClassifier -from amd.applications.hip_samples.hip_samples_build import BuildRunAmd, BuildRunNvidia -from amd.common.hip_get_packages import HipPackages -from amd.common.hip_shell import execshellcmd +from testsuite.test_classifier import TestClassifier +from testsuite.applications.hip_samples.hip_samples_build_amd import BuildRunAmd +from testsuite.applications.hip_samples.hip_samples_build_nvidia import BuildRunNvidia +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import execshellcmd import os import re @@ -32,7 +33,7 @@ class PrepareTest(): def __init__(self, path, binary, cwd): self.cwdAbs = cwd - self.conformancePath = os.path.join(self.cwdAbs, "src/amd/conformance/") + self.conformancePath = os.path.join(self.cwdAbs, "src/testsuite/conformance/") self.hippath = os.path.join(self.conformancePath, "HIP/") self.thistestpath = os.path.join(self.hippath, path) self.binary = binary @@ -40,9 +41,6 @@ def __init__(self, path, binary, cwd): self.hiprepo = "" # Default self.hipbranch = "" self.hipcommitId = "" - self.hipamdrepo = "" # Default - self.hipamdbranch = "" - self.hipamdcommitId = "" self.prepareobj = None def setrepoinfo(self, test_data: HIPTestData): @@ -56,21 +54,11 @@ def setrepoinfo(self, test_data: HIPTestData): if test_data.repos["hip"].commit_id != None: self.hipcommitId = test_data.repos["hip"].commit_id - if test_data.repos["hipamd"].repo_url != None: - self.hipamdrepo = test_data.repos["hipamd"].repo_url - else: - validrepconfig &= False - if test_data.repos["hipamd"].branch != None: - self.hipamdbranch = test_data.repos["hipamd"].branch - if test_data.repos["hipamd"].commit_id != None: - self.hipamdcommitId = test_data.repos["hipamd"].commit_id return validrepconfig def downloadtest(self, logFile): ret = HipPackages().pull_repo(logFile, self.hiprepo, self.hipbranch,\ self.hipcommitId, "HIP") - ret = ret & HipPackages().pull_repo(logFile, self.hipamdrepo, self.hipamdbranch,\ - self.hipamdcommitId, "hipamd") return ret def buildtest(self, logFile, platform): @@ -90,7 +78,8 @@ def buildtest(self, logFile, platform): return isBinaryPresent def clean(self): - self.prepareobj.clean() + if self.prepareobj != None: + self.prepareobj.clean() def runtest(self, logFile): cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary diff --git a/src/testsuite/applications/hip_samples/hip_samples_build_amd.py b/src/testsuite/applications/hip_samples/hip_samples_build_amd.py new file mode 100644 index 0000000..c72fc71 --- /dev/null +++ b/src/testsuite/applications/hip_samples/hip_samples_build_amd.py @@ -0,0 +1,37 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.applications.hip_samples.hip_samples_build_common import BuildRunCommon + +class BuildRunAmd(BuildRunCommon): + def __init__(self, path, logfile): + BuildRunCommon.__init__(self, path, logfile) + + def buildtest(self): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) else + # invoke BuildRunCommon.buildtest + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + ret = BuildRunCommon.buildtest(self) + return ret diff --git a/src/testsuite/applications/hip_samples/hip_samples_build_common.py b/src/testsuite/applications/hip_samples/hip_samples_build_common.py new file mode 100644 index 0000000..deb7855 --- /dev/null +++ b/src/testsuite/applications/hip_samples/hip_samples_build_common.py @@ -0,0 +1,44 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd + +class BuildRunCommon(): + ''' + In this class insert the build and execution steps for test cases + which are identical across different platforms (amd/nvidia/intel). + ''' + def __init__(self, path, logfile): + self.thistestpath = path + self.logfile = logfile + + def buildtest(self, env = None): + cmdcd = "cd " + self.thistestpath + ";" + cmd_clean = "make clean;" + cmd_build = "make" + cmdexc = cmdcd + cmd_clean + cmd_build + execshellcmd(cmdexc, self.logfile, env) + + def clean(self): + cmdcd = "cd " + self.thistestpath + ";" + cmd_clean = "make clean;" + cmdexc = cmdcd + cmd_clean + execshellcmd(cmdexc, None, None) diff --git a/src/testsuite/applications/hip_samples/hip_samples_build_nvidia.py b/src/testsuite/applications/hip_samples/hip_samples_build_nvidia.py new file mode 100644 index 0000000..d456b69 --- /dev/null +++ b/src/testsuite/applications/hip_samples/hip_samples_build_nvidia.py @@ -0,0 +1,57 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.applications.hip_samples.hip_samples_build_common import BuildRunCommon + +class BuildRunNvidia(BuildRunCommon): + def __init__(self, path, logfile): + self.hippath = os.path.join(os.getcwd(), "src/testsuite/conformance/HIP/") + BuildRunCommon.__init__(self, path, logfile) + + def getenvironmentvariables(self): + envtoset = os.environ.copy() + envtoset["HIP_PLATFORM"] = "nvidia" + envtoset["HIP_COMPILER"] = "nvcc" + envtoset["HIP_RUNTIME"] = "cuda" + envtoset["HIP_PATH"] = "/opt/rocm/hip" + return envtoset + + def applypatch(self): # To be deleted + if not os.path.isfile(\ + os.path.join(self.hippath, "patched")): + cmd = "cd " + self.hippath + ";" + cmd += "patch -p0 < ../../applications/hip_samples/Samples_Patch_4.2.x; touch patched;" + execshellcmd(cmd, self.logfile, None) + + def buildtest(self): + # In this function put the build steps for test cases + # which differ across platforms (testsuite/nvidia/intel) else + # invoke BuildRunCommon.buildtest + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + # Apply Patch. This is temporary and will be removed once Hip Samples changes + # are available in HIP public repository. + envtoset = self.getenvironmentvariables() + self.applypatch() + ret = BuildRunCommon.buildtest(self, envtoset) + return ret diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.c b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.c new file mode 100644 index 0000000..0d65981 --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.c @@ -0,0 +1,393 @@ +/* +GPU Implementation of Keccak by Guillaume Sevestre, 2010 + +This code is hereby put in the public domain. +It is given as is, without any guarantee. +*/ + +#include +#include + +#include "KeccakF.h" +#include "KeccakTree.h" + +// 22 rounds constants +// Study constant memory best placement with Cuda (textures ?) + + + +const tKeccakLane KeccakF_RoundConstantsCPU[22] = +{ + (tKeccakLane)0x00000001 , + (tKeccakLane)0x00008082 , + (tKeccakLane)0x0000808a , + (tKeccakLane)0x80008000 , + (tKeccakLane)0x0000808b , + (tKeccakLane)0x80000001 , + (tKeccakLane)0x80008081 , + (tKeccakLane)0x00008009 , + (tKeccakLane)0x0000008a , + (tKeccakLane)0x00000088 , + (tKeccakLane)0x80008009 , + (tKeccakLane)0x8000000a , + (tKeccakLane)0x8000808b , + (tKeccakLane)0x0000008b , + (tKeccakLane)0x00008089 , + (tKeccakLane)0x00008003 , + (tKeccakLane)0x00008002 , + (tKeccakLane)0x00000080 , + (tKeccakLane)0x0000800a , + (tKeccakLane)0x8000000a , + (tKeccakLane)0x80008081 , + (tKeccakLane)0x00008080 +}; + + + +//INFO It could be more optimized to use unsigned char on an 8-bit CPU +const unsigned int KeccakF_RotationConstants[25] = +{ + // 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 + 1, 3, 6, 10, 15, 21, 28, 4, 13, 23, 2, 14, 27, 9, 24, 8, 25, 11, 30, 18, 7, 29 , 20, 12 +}; + +//INFO It could be more optimized to use unsigned char on an 8-bit CPU +const unsigned int KeccakF_PiLane[25] = +{ + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 +}; + +//INFO It could be more optimized to use unsigned char on an 8-bit CPU +const unsigned int KeccakF_Mod5[10] = +{ + 0, 1, 2, 3, 4, 0, 1, 2, 3, 4 +}; + + + + void KeccakF( tKeccakLane * state) +{ + unsigned int x,y,round; //try to avoid to many registers + tKeccakLane BC[5]; + tKeccakLane temp; + + for ( round = 0; round < cKeccakNumberOfRounds; ++round ) + { + // Theta + for ( x = 0; x < 5; ++x ) // derouler + { + BC[x] = state[x] ^ state[5 + x] ^ state[10 + x] ^ state[15 + x] ^ state[20 + x]; + } + for ( x = 0; x < 5; ++x ) // derouler + { + temp = BC[KeccakF_Mod5[x+4]] ^ ROL32(BC[KeccakF_Mod5[x+1]], 1); //expliciter + for ( y = 0; y < 25; y += 5 ) // derouler + { + state[y + x] ^= temp; + } + } + + // Rho Pi + temp = state[1]; + for ( x = 0; x < 24; ++x ) //expliciter + rotation modulo 32 + { + BC[0] = state[KeccakF_PiLane[x]]; + state[KeccakF_PiLane[x]] = ROL32( temp, KeccakF_RotationConstants[x] ); + temp = BC[0]; + } + + // Chi + for ( y = 0; y < 25; y += 5 ) + { + BC[0] = state[y + 0]; + BC[1] = state[y + 1]; + BC[2] = state[y + 2]; + BC[3] = state[y + 3]; + BC[4] = state[y + 4]; + for ( x = 0; x < 5; ++x ) + { + state[y + x] = BC[x] ^((~BC[KeccakF_Mod5[x+1]]) & BC[KeccakF_Mod5[x+2]]); + } + } + + // Iota + state[0] ^= KeccakF_RoundConstantsCPU[round]; + } + + +} + + void KeccakF_CPU( tKeccakLane * state ) +{ + unsigned int round; //try to avoid to many registers + tKeccakLane BC[5]; + tKeccakLane temp; + + for ( round = 0; round < cKeccakNumberOfRounds; ++round ) + { + + { + // Theta + BC[0] = state[0] ^ state[5] ^ state[10] ^ state[15] ^ state[20]; + BC[1] = state[1] ^ state[6] ^ state[11] ^ state[16] ^ state[21]; + BC[2] = state[2] ^ state[7] ^ state[12] ^ state[17] ^ state[22]; + BC[3] = state[3] ^ state[8] ^ state[13] ^ state[18] ^ state[23]; + BC[4] = state[4] ^ state[9] ^ state[14] ^ state[19] ^ state[24]; + + temp = BC[4] ^ ROL32(BC[1], 1);//x=0 + state[0] ^= temp; + state[5] ^= temp; + state[10] ^= temp; + state[15] ^= temp; + state[20] ^= temp; + temp = BC[0] ^ ROL32(BC[2], 1);//x=1 + state[1] ^= temp; + state[6] ^= temp; + state[11] ^= temp; + state[16] ^= temp; + state[21] ^= temp; + temp = BC[1] ^ ROL32(BC[3], 1);//x=2 + state[2] ^= temp; + state[7] ^= temp; + state[12] ^= temp; + state[17] ^= temp; + state[22] ^= temp; + temp = BC[2] ^ ROL32(BC[4], 1);//x=0 + state[3] ^= temp; + state[8] ^= temp; + state[13] ^= temp; + state[18] ^= temp; + state[23] ^= temp; + temp = BC[3] ^ ROL32(BC[0], 1);//x=0 + state[4] ^= temp; + state[9] ^= temp; + state[14] ^= temp; + state[19] ^= temp; + state[24] ^= temp; + }//end Theta + + { + // Rho Pi + temp = state[1]; + BC[0] = state[10]; + state[10] = ROL32( temp, 1); + temp = BC[0];//x=0 + BC[0] = state[7]; + state[7] = ROL32( temp, 3); + temp = BC[0]; + BC[0] = state[11]; + state[11] = ROL32( temp, 6); + temp = BC[0]; + BC[0] = state[17]; + state[17] = ROL32( temp,10); + temp = BC[0]; + BC[0] = state[18]; + state[18] = ROL32( temp,15); + temp = BC[0]; + BC[0] = state[3]; + state[3] = ROL32( temp,21); + temp = BC[0];//x=5 + BC[0] = state[5]; + state[5] = ROL32( temp,28); + temp = BC[0]; + BC[0] = state[16]; + state[16] = ROL32( temp, 4); + temp = BC[0]; + BC[0] = state[8]; + state[8] = ROL32( temp,13); + temp = BC[0]; + BC[0] = state[21]; + state[21] = ROL32( temp,23); + temp = BC[0]; + BC[0] = state[24]; + state[24] = ROL32( temp, 2); + temp = BC[0];//x=10 + BC[0] = state[4]; + state[4] = ROL32( temp,14); + temp = BC[0]; + BC[0] = state[15]; + state[15] = ROL32( temp,27); + temp = BC[0]; + BC[0] = state[23]; + state[23] = ROL32( temp, 9); + temp = BC[0]; + BC[0] = state[19]; + state[19] = ROL32( temp,24); + temp = BC[0]; + BC[0] = state[13]; + state[13] = ROL32( temp, 8); + temp = BC[0];//x=15 + BC[0] = state[12]; + state[12] = ROL32( temp,25); + temp = BC[0]; + BC[0] = state[2]; + state[2] = ROL32( temp,11); + temp = BC[0]; + BC[0] = state[20]; + state[20] = ROL32( temp,30); + temp = BC[0]; + BC[0] = state[14]; + state[14] = ROL32( temp,18); + temp = BC[0]; + BC[0] = state[22]; + state[22] = ROL32( temp, 7); + temp = BC[0];//x=20 + BC[0] = state[9]; + state[9] = ROL32( temp,29); + temp = BC[0]; + BC[0] = state[6]; + state[6] = ROL32( temp,20); + temp = BC[0]; + BC[0] = state[1]; + state[1] = ROL32( temp,12); + temp = BC[0];//x=23 + }//end Rho Pi + + { + // Chi + BC[0] = state[0]; + BC[1] = state[1]; + BC[2] = state[2]; + BC[3] = state[3]; + BC[4] = state[4]; + state[0] = BC[0] ^((~BC[1]) & BC[2]); + state[1] = BC[1] ^((~BC[2]) & BC[3]); + state[2] = BC[2] ^((~BC[3]) & BC[4]); + state[3] = BC[3] ^((~BC[4]) & BC[0]); + state[4] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[5]; + BC[1] = state[6]; + BC[2] = state[7]; + BC[3] = state[8]; + BC[4] = state[9]; + state[5] = BC[0] ^((~BC[1]) & BC[2]); + state[6] = BC[1] ^((~BC[2]) & BC[3]); + state[7] = BC[2] ^((~BC[3]) & BC[4]); + state[8] = BC[3] ^((~BC[4]) & BC[0]); + state[9] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[10]; + BC[1] = state[11]; + BC[2] = state[12]; + BC[3] = state[13]; + BC[4] = state[14]; + state[10] = BC[0] ^((~BC[1]) & BC[2]); + state[11] = BC[1] ^((~BC[2]) & BC[3]); + state[12] = BC[2] ^((~BC[3]) & BC[4]); + state[13] = BC[3] ^((~BC[4]) & BC[0]); + state[14] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[15]; + BC[1] = state[16]; + BC[2] = state[17]; + BC[3] = state[18]; + BC[4] = state[19]; + state[15] = BC[0] ^((~BC[1]) & BC[2]); + state[16] = BC[1] ^((~BC[2]) & BC[3]); + state[17] = BC[2] ^((~BC[3]) & BC[4]); + state[18] = BC[3] ^((~BC[4]) & BC[0]); + state[19] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[20]; + BC[1] = state[21]; + BC[2] = state[22]; + BC[3] = state[23]; + BC[4] = state[24]; + state[20] = BC[0] ^((~BC[1]) & BC[2]); + state[21] = BC[1] ^((~BC[2]) & BC[3]); + state[22] = BC[2] ^((~BC[3]) & BC[4]); + state[23] = BC[3] ^((~BC[4]) & BC[0]); + state[24] = BC[4] ^((~BC[0]) & BC[1]); + }//end Chi + + // Iota + state[0] ^= KeccakF_RoundConstantsCPU[round]; + } + +} + + + + + +// Absorb blocks in top of tree keccak hash function +// inBuffer supposed to have block_number * output_block_size of data + void Keccak_top(tKeccakLane * Kstate, tKeccakLane *inBuffer , int block_number) +{ + int ind_word,k; + + for (k=0;k>(32-offset) ) ) + +//implementation of Keccak function on CPU +void KeccakF( tKeccakLane * state ); + +//implementation of Keccak function on CPU, unrolled +void KeccakF_CPU( tKeccakLane * state ); + +//set the state to zero +void zeroize( tKeccakLane * state ); + +//Keccak final node hashing results of previous nodes in sequential mode +// inBuffer supposed to have block_number * output_block_size of data +void Keccak_top(tKeccakLane * Kstate, tKeccakLane *inBuffer , int block_number); + +//test equility of 2 keccak states +int isEqual_KS(tKeccakLane * Ks1, tKeccakLane * Ks2); + +//print functions +void print_KS(tKeccakLane * state); +void print_KS_256(tKeccakLane * state); + +#endif // KECCAKF_H_INCLUDED diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTree.h b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTree.h new file mode 100644 index 0000000..7193fe3 --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTree.h @@ -0,0 +1,29 @@ +/* +GPU Implementation of Keccak by Guillaume Sevestre, 2010 + +This code is hereby put in the public domain. +It is given as is, without any guarantee. +*/ + +#ifndef KECCAKTREE_H_INCLUDED +#define KECCAKTREE_H_INCLUDED + +#define NB_THREADS 64 // 96 // 192 // Numbers of threads PER BLOCK MUST BE a multiple of NB_SNCD_STAGE_NODES + //MUST BE > 8 for streamcipher mode + +#define NB_THREADS_BLOCKS 64 // 64 // 32 + +#define NB_STREAMS 2 // 4 // 2 MUST DIVIDE NB_THREADS_BLOCKS + +#define INPUT_BLOCK_SIZE_B 32 // 256 bits in : 32 Bytes MUST BE multiple of 4 +#define OUTPUT_BLOCK_SIZE_B 32 // 256 bits out of each keccak hash MUST BE multiple of 4 +#define NB_INPUT_BLOCK 64 // 128 // 64 number of input block of 256 bits + +// 2 stage Treehash +#define NB_SCND_STAGE_THREADS 16 // MUST DIVIDE NB_THREADS +#define NB_INPUT_BLOCK_SNCD_STAGE 2*NB_THREADS/NB_SCND_STAGE_THREADS // + +//StreamCipher +#define SC_NB_OUTPUT_BLOCK 64 // number of output blocks in stream cipher mode + +#endif // KECCAKTREE_H_INCLUDED diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.c b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.c new file mode 100644 index 0000000..f45e039 --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.c @@ -0,0 +1,195 @@ +/* +GPU Implementation of Keccak by Guillaume Sevestre, 2010 + +This code is hereby put in the public domain. +It is given as is, without any guarantee. +*/ + +#include +#include +#include + +#include "KeccakTreeCPU.h" +#include "KeccakF.h" + +void KeccakTreeCPU(tKeccakLane * inBuffer, tKeccakLane * outBuffer) +{ + + //int thread_i, + int thrIdx,blkIdx; + int k,ind_word; + + + for(blkIdx=0;blkIdx +#include +#include + + +//#include "KeccakTreeGPU.h" + +#include "KeccakTree.h" +#include "KeccakF.h" + + +extern "C" +__host__ void checkCUDAError(const char *msg) +{ + hipError_t err = hipGetLastError(); + if( hipSuccess != err) + { + fprintf(stderr, "Cuda error: %s: %s.\n", msg, + hipGetErrorString( err) ); + exit(EXIT_FAILURE); + } +} + +//GPU constants + __constant__ tKeccakLane KeccakF_RoundConstants[22] = +{ + (tKeccakLane)0x00000001 , + (tKeccakLane)0x00008082 , + (tKeccakLane)0x0000808a , + (tKeccakLane)0x80008000 , + (tKeccakLane)0x0000808b , + (tKeccakLane)0x80000001 , + (tKeccakLane)0x80008081 , + (tKeccakLane)0x00008009 , + (tKeccakLane)0x0000008a , + (tKeccakLane)0x00000088 , + (tKeccakLane)0x80008009 , + (tKeccakLane)0x8000000a , + (tKeccakLane)0x8000808b , + (tKeccakLane)0x0000008b , + (tKeccakLane)0x00008089 , + (tKeccakLane)0x00008003 , + (tKeccakLane)0x00008002 , + (tKeccakLane)0x00000080 , + (tKeccakLane)0x0000800a , + (tKeccakLane)0x8000000a , + (tKeccakLane)0x80008081 , + (tKeccakLane)0x00008080 +}; + +//host constants + tKeccakLane KeccakF_RoundConstants_h[22] = +{ + (tKeccakLane)0x00000001 , + (tKeccakLane)0x00008082 , + (tKeccakLane)0x0000808a , + (tKeccakLane)0x80008000 , + (tKeccakLane)0x0000808b , + (tKeccakLane)0x80000001 , + (tKeccakLane)0x80008081 , + (tKeccakLane)0x00008009 , + (tKeccakLane)0x0000008a , + (tKeccakLane)0x00000088 , + (tKeccakLane)0x80008009 , + (tKeccakLane)0x8000000a , + (tKeccakLane)0x8000808b , + (tKeccakLane)0x0000008b , + (tKeccakLane)0x00008089 , + (tKeccakLane)0x00008003 , + (tKeccakLane)0x00008002 , + (tKeccakLane)0x00000080 , + (tKeccakLane)0x0000800a , + (tKeccakLane)0x8000000a , + (tKeccakLane)0x80008081 , + (tKeccakLane)0x00008080 +}; + +// Device (GPU) Keccak-f function implementation +// unrolled +__device__ void KeccakFunr( tKeccakLane * state ) +{ + unsigned int round; //try to avoid to many registers + tKeccakLane BC[5]; + tKeccakLane temp; + + for ( round = 0; round < cKeccakNumberOfRounds; ++round ) + { + + { + // Theta + BC[0] = state[0] ^ state[5] ^ state[10] ^ state[15] ^ state[20]; + BC[1] = state[1] ^ state[6] ^ state[11] ^ state[16] ^ state[21]; + BC[2] = state[2] ^ state[7] ^ state[12] ^ state[17] ^ state[22]; + BC[3] = state[3] ^ state[8] ^ state[13] ^ state[18] ^ state[23]; + BC[4] = state[4] ^ state[9] ^ state[14] ^ state[19] ^ state[24]; + + temp = BC[4] ^ ROL32(BC[1], 1);//x=0 + state[0] ^= temp; + state[5] ^= temp; + state[10] ^= temp; + state[15] ^= temp; + state[20] ^= temp; + temp = BC[0] ^ ROL32(BC[2], 1);//x=1 + state[1] ^= temp; + state[6] ^= temp; + state[11] ^= temp; + state[16] ^= temp; + state[21] ^= temp; + temp = BC[1] ^ ROL32(BC[3], 1);//x=2 + state[2] ^= temp; + state[7] ^= temp; + state[12] ^= temp; + state[17] ^= temp; + state[22] ^= temp; + temp = BC[2] ^ ROL32(BC[4], 1);//x=3 + state[3] ^= temp; + state[8] ^= temp; + state[13] ^= temp; + state[18] ^= temp; + state[23] ^= temp; + temp = BC[3] ^ ROL32(BC[0], 1);//x=4 + state[4] ^= temp; + state[9] ^= temp; + state[14] ^= temp; + state[19] ^= temp; + state[24] ^= temp; + }//end Theta + + { + // Rho Pi + temp = state[1]; + BC[0] = state[10]; + state[10] = ROL32( temp, 1); + temp = BC[0];//x=0 + BC[0] = state[7]; + state[7] = ROL32( temp, 3); + temp = BC[0]; + BC[0] = state[11]; + state[11] = ROL32( temp, 6); + temp = BC[0]; + BC[0] = state[17]; + state[17] = ROL32( temp,10); + temp = BC[0]; + BC[0] = state[18]; + state[18] = ROL32( temp,15); + temp = BC[0]; + BC[0] = state[3]; + state[3] = ROL32( temp,21); + temp = BC[0];//x=5 + BC[0] = state[5]; + state[5] = ROL32( temp,28); + temp = BC[0]; + BC[0] = state[16]; + state[16] = ROL32( temp, 4); + temp = BC[0]; + BC[0] = state[8]; + state[8] = ROL32( temp,13); + temp = BC[0]; + BC[0] = state[21]; + state[21] = ROL32( temp,23); + temp = BC[0]; + BC[0] = state[24]; + state[24] = ROL32( temp, 2); + temp = BC[0];//x=10 + BC[0] = state[4]; + state[4] = ROL32( temp,14); + temp = BC[0]; + BC[0] = state[15]; + state[15] = ROL32( temp,27); + temp = BC[0]; + BC[0] = state[23]; + state[23] = ROL32( temp, 9); + temp = BC[0]; + BC[0] = state[19]; + state[19] = ROL32( temp,24); + temp = BC[0]; + BC[0] = state[13]; + state[13] = ROL32( temp, 8); + temp = BC[0];//x=15 + BC[0] = state[12]; + state[12] = ROL32( temp,25); + temp = BC[0]; + BC[0] = state[2]; + state[2] = ROL32( temp,11); + temp = BC[0]; + BC[0] = state[20]; + state[20] = ROL32( temp,30); + temp = BC[0]; + BC[0] = state[14]; + state[14] = ROL32( temp,18); + temp = BC[0]; + BC[0] = state[22]; + state[22] = ROL32( temp, 7); + temp = BC[0];//x=20 + BC[0] = state[9]; + state[9] = ROL32( temp,29); + temp = BC[0]; + BC[0] = state[6]; + state[6] = ROL32( temp,20); + temp = BC[0]; + BC[0] = state[1]; + state[1] = ROL32( temp,12); + temp = BC[0];//x=23 + }//end Rho Pi + + { + // Chi + BC[0] = state[0]; + BC[1] = state[1]; + BC[2] = state[2]; + BC[3] = state[3]; + BC[4] = state[4]; + state[0] = BC[0] ^((~BC[1]) & BC[2]); + state[1] = BC[1] ^((~BC[2]) & BC[3]); + state[2] = BC[2] ^((~BC[3]) & BC[4]); + state[3] = BC[3] ^((~BC[4]) & BC[0]); + state[4] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[5]; + BC[1] = state[6]; + BC[2] = state[7]; + BC[3] = state[8]; + BC[4] = state[9]; + state[5] = BC[0] ^((~BC[1]) & BC[2]); + state[6] = BC[1] ^((~BC[2]) & BC[3]); + state[7] = BC[2] ^((~BC[3]) & BC[4]); + state[8] = BC[3] ^((~BC[4]) & BC[0]); + state[9] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[10]; + BC[1] = state[11]; + BC[2] = state[12]; + BC[3] = state[13]; + BC[4] = state[14]; + state[10] = BC[0] ^((~BC[1]) & BC[2]); + state[11] = BC[1] ^((~BC[2]) & BC[3]); + state[12] = BC[2] ^((~BC[3]) & BC[4]); + state[13] = BC[3] ^((~BC[4]) & BC[0]); + state[14] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[15]; + BC[1] = state[16]; + BC[2] = state[17]; + BC[3] = state[18]; + BC[4] = state[19]; + state[15] = BC[0] ^((~BC[1]) & BC[2]); + state[16] = BC[1] ^((~BC[2]) & BC[3]); + state[17] = BC[2] ^((~BC[3]) & BC[4]); + state[18] = BC[3] ^((~BC[4]) & BC[0]); + state[19] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[20]; + BC[1] = state[21]; + BC[2] = state[22]; + BC[3] = state[23]; + BC[4] = state[24]; + state[20] = BC[0] ^((~BC[1]) & BC[2]); + state[21] = BC[1] ^((~BC[2]) & BC[3]); + state[22] = BC[2] ^((~BC[3]) & BC[4]); + state[23] = BC[3] ^((~BC[4]) & BC[0]); + state[24] = BC[4] ^((~BC[0]) & BC[1]); + }//end Chi + + // Iota + state[0] ^= KeccakF_RoundConstants[round]; + } + +} +//end unrolled + +//Host Keccak-f function (pb with using the same constants between host and device) +//unrolled +__host__ void KeccakFunr_h( tKeccakLane * state ) +{ + unsigned int round; //try to avoid to many registers + tKeccakLane BC[5]; + tKeccakLane temp; + + for ( round = 0; round < cKeccakNumberOfRounds; ++round ) + { + + { + // Theta + BC[0] = state[0] ^ state[5] ^ state[10] ^ state[15] ^ state[20]; + BC[1] = state[1] ^ state[6] ^ state[11] ^ state[16] ^ state[21]; + BC[2] = state[2] ^ state[7] ^ state[12] ^ state[17] ^ state[22]; + BC[3] = state[3] ^ state[8] ^ state[13] ^ state[18] ^ state[23]; + BC[4] = state[4] ^ state[9] ^ state[14] ^ state[19] ^ state[24]; + + temp = BC[4] ^ ROL32(BC[1], 1);//x=0 + state[0] ^= temp; + state[5] ^= temp; + state[10] ^= temp; + state[15] ^= temp; + state[20] ^= temp; + temp = BC[0] ^ ROL32(BC[2], 1);//x=1 + state[1] ^= temp; + state[6] ^= temp; + state[11] ^= temp; + state[16] ^= temp; + state[21] ^= temp; + temp = BC[1] ^ ROL32(BC[3], 1);//x=2 + state[2] ^= temp; + state[7] ^= temp; + state[12] ^= temp; + state[17] ^= temp; + state[22] ^= temp; + temp = BC[2] ^ ROL32(BC[4], 1);//x=3 + state[3] ^= temp; + state[8] ^= temp; + state[13] ^= temp; + state[18] ^= temp; + state[23] ^= temp; + temp = BC[3] ^ ROL32(BC[0], 1);//x=4 + state[4] ^= temp; + state[9] ^= temp; + state[14] ^= temp; + state[19] ^= temp; + state[24] ^= temp; + }//end Theta + + { + // Rho Pi + temp = state[1]; + BC[0] = state[10]; + state[10] = ROL32( temp, 1); + temp = BC[0];//x=0 + BC[0] = state[7]; + state[7] = ROL32( temp, 3); + temp = BC[0]; + BC[0] = state[11]; + state[11] = ROL32( temp, 6); + temp = BC[0]; + BC[0] = state[17]; + state[17] = ROL32( temp,10); + temp = BC[0]; + BC[0] = state[18]; + state[18] = ROL32( temp,15); + temp = BC[0]; + BC[0] = state[3]; + state[3] = ROL32( temp,21); + temp = BC[0];//x=5 + BC[0] = state[5]; + state[5] = ROL32( temp,28); + temp = BC[0]; + BC[0] = state[16]; + state[16] = ROL32( temp, 4); + temp = BC[0]; + BC[0] = state[8]; + state[8] = ROL32( temp,13); + temp = BC[0]; + BC[0] = state[21]; + state[21] = ROL32( temp,23); + temp = BC[0]; + BC[0] = state[24]; + state[24] = ROL32( temp, 2); + temp = BC[0];//x=10 + BC[0] = state[4]; + state[4] = ROL32( temp,14); + temp = BC[0]; + BC[0] = state[15]; + state[15] = ROL32( temp,27); + temp = BC[0]; + BC[0] = state[23]; + state[23] = ROL32( temp, 9); + temp = BC[0]; + BC[0] = state[19]; + state[19] = ROL32( temp,24); + temp = BC[0]; + BC[0] = state[13]; + state[13] = ROL32( temp, 8); + temp = BC[0];//x=15 + BC[0] = state[12]; + state[12] = ROL32( temp,25); + temp = BC[0]; + BC[0] = state[2]; + state[2] = ROL32( temp,11); + temp = BC[0]; + BC[0] = state[20]; + state[20] = ROL32( temp,30); + temp = BC[0]; + BC[0] = state[14]; + state[14] = ROL32( temp,18); + temp = BC[0]; + BC[0] = state[22]; + state[22] = ROL32( temp, 7); + temp = BC[0];//x=20 + BC[0] = state[9]; + state[9] = ROL32( temp,29); + temp = BC[0]; + BC[0] = state[6]; + state[6] = ROL32( temp,20); + temp = BC[0]; + BC[0] = state[1]; + state[1] = ROL32( temp,12); + temp = BC[0];//x=23 + }//end Rho Pi + + { + // Chi + BC[0] = state[0]; + BC[1] = state[1]; + BC[2] = state[2]; + BC[3] = state[3]; + BC[4] = state[4]; + state[0] = BC[0] ^((~BC[1]) & BC[2]); + state[1] = BC[1] ^((~BC[2]) & BC[3]); + state[2] = BC[2] ^((~BC[3]) & BC[4]); + state[3] = BC[3] ^((~BC[4]) & BC[0]); + state[4] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[5]; + BC[1] = state[6]; + BC[2] = state[7]; + BC[3] = state[8]; + BC[4] = state[9]; + state[5] = BC[0] ^((~BC[1]) & BC[2]); + state[6] = BC[1] ^((~BC[2]) & BC[3]); + state[7] = BC[2] ^((~BC[3]) & BC[4]); + state[8] = BC[3] ^((~BC[4]) & BC[0]); + state[9] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[10]; + BC[1] = state[11]; + BC[2] = state[12]; + BC[3] = state[13]; + BC[4] = state[14]; + state[10] = BC[0] ^((~BC[1]) & BC[2]); + state[11] = BC[1] ^((~BC[2]) & BC[3]); + state[12] = BC[2] ^((~BC[3]) & BC[4]); + state[13] = BC[3] ^((~BC[4]) & BC[0]); + state[14] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[15]; + BC[1] = state[16]; + BC[2] = state[17]; + BC[3] = state[18]; + BC[4] = state[19]; + state[15] = BC[0] ^((~BC[1]) & BC[2]); + state[16] = BC[1] ^((~BC[2]) & BC[3]); + state[17] = BC[2] ^((~BC[3]) & BC[4]); + state[18] = BC[3] ^((~BC[4]) & BC[0]); + state[19] = BC[4] ^((~BC[0]) & BC[1]); + BC[0] = state[20]; + BC[1] = state[21]; + BC[2] = state[22]; + BC[3] = state[23]; + BC[4] = state[24]; + state[20] = BC[0] ^((~BC[1]) & BC[2]); + state[21] = BC[1] ^((~BC[2]) & BC[3]); + state[22] = BC[2] ^((~BC[3]) & BC[4]); + state[23] = BC[3] ^((~BC[4]) & BC[0]); + state[24] = BC[4] ^((~BC[0]) & BC[1]); + }//end Chi + + // Iota + state[0] ^= KeccakF_RoundConstants_h[round]; + } + +} +//end unrolled + +//Keccak final node hashing results of previous nodes in sequential mode +__host__ void Keccak_top_GPU(tKeccakLane * Kstate, tKeccakLane *inBuffer , int block_number) +{ + int ind_word,k; + + for (k=0;k 8 + if(threadIdx.x <8) + { + Key[threadIdx.x]= d_KeyNonce_inBuffer[threadIdx.x]; + Nonce[threadIdx.x]=d_KeyNonce_inBuffer[threadIdx.x + 8 ]; + } + __syncthreads(); + + //zeroize the state + for(ind_word=0; ind_word<25; ind_word++) {Kstate[ind_word]=0; } + + //input the key + //xor Key into state from shared mem + for (ind_word=0; ind_word<8; ind_word++) + { + Kstate[ind_word] ^=Key[ind_word]; + } + //apply GPU Keccak permutation + KeccakFunr(Kstate); + + //input the Nonce + //xor Key into state from shared mem + for (ind_word=0; ind_word<8; ind_word++) + { + Kstate[ind_word] ^=Nonce[ind_word]; + } + //apply GPU Keccak permutation + KeccakFunr(Kstate); + + //input the threadIdx.x AND blockIdx.x to have different KeyStreams + Kstate[0]^= threadIdx.x ; + Kstate[1]^= blockIdx.x ; + KeccakFunr(Kstate); + + + for (k=0;k= 8800 GT), even without the cuda toolkit should be ok with the executable (to be renamed in .exe) and the runtime dll included in release folder of VS project. + + +Compiling under Linux : +After installing Cuda SDK and toolkit, use the included Makefile. the Path to cuda SDK and toolkit must be changed in this Makefile. Beware of the 64 bits target used (could be easily changed to 32 bits). + +Description of Source files: + +KeccakF.c : Implementation of Keccak function on CPU + +KeccakTree.h : Definition of constants (number of threads, size of input blocks, output blocks, etc...) + +KeccakTreeCPU.c : Implementation of Keccak tree hash mode on CPU only + +KeccakTreeGPU.cu : Cuda code of implementation of Keccak tree hash on GPU + +KeccakTypes.h : Types definition + +main.c : main file + +Test.c : Implementation of test functions of different modes, performance mesure. + diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.c b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.c new file mode 100644 index 0000000..71ee7c0 --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.c @@ -0,0 +1,1023 @@ +/* +GPU Implementation of Keccak by Guillaume Sevestre, 2010 + +This code is hereby put in the public domain. +It is given as is, without any guarantee. +*/ + +#include +#include +#include + +#include + +//Cuda +#include +#include + + + +#include "KeccakF.h" +#include "KeccakTreeCPU.h" +#include "KeccakTreeGPU.h" + +#define IMAX 1600 //2400 // 1600 for high speed mesures // iteration for speed mesure loops + + +//debug print function +void print_out(tKeccakLane * h_outBuffer,int nb_threads) +{ +printf("%08x ",h_outBuffer[0]);printf("%08x ",h_outBuffer[1]); +printf("%08x ",h_outBuffer[nb_threads]);printf("%08x ",h_outBuffer[nb_threads +1]); +printf("\n\n"); +} + + + +void TestCPU(int reduc) +{ + time_t t1,t2; + double speed1; + int i; + + tKeccakLane *h_inBuffer;// Host in buffer for data to be hashed + tKeccakLane *h_outBuffer;// Host out buffer + + tKeccakLane Kstate[25]; //Keccak State for top node + memset(Kstate, 0, 25 * sizeof(tKeccakLane)); + + + //init host inBuffer + h_inBuffer=(tKeccakLane *) malloc( INPUT_BLOCK_SIZE_B * NB_THREADS*NB_THREADS_BLOCKS * NB_INPUT_BLOCK ); + memset(h_inBuffer, 0, INPUT_BLOCK_SIZE_B * NB_THREADS*NB_THREADS_BLOCKS * NB_INPUT_BLOCK); + + //init host outBuffer + h_outBuffer=(tKeccakLane *) malloc( OUTPUT_BLOCK_SIZE_B * NB_THREADS*NB_THREADS_BLOCKS ); + memset(h_outBuffer, 0, OUTPUT_BLOCK_SIZE_B * NB_THREADS*NB_THREADS_BLOCKS ); + + //*************************** + //init h_inBuffer with values + for(i=0;i1pVfZ6e`bR-=~NAI~v@_+w6bBAu|j$PON>>jxdUSR8H|Z~`aSD&tMQ#YsI&*8$!#mfZ<`53caGmA(3ToZ8 zZH!&`^eOq0*_7>9azDW587=yJ14~INN*#o=3P|nKzC}xozQemqjRC2@jryIN4@XmA zP8~{{lyB0$9m+d@!A-s8=`R|y?>)+;XYd#<>bJ`?K-wBb2l=-d$!wxbB~6~VES{6* zrqLuNqqdi2QgW-T`6`l;Os46lZoMvrBbHol&aDie=(!EI$JR@Fg%3@>N!boef_>gC zX1BFbxqrI)&-e;Z8Bg_zV_k9qc)oyVnc(qeAW z93rmj;Q^b{m(fU1W3y;0?~x;&3=ECOAVO9~mf{06Va(g6DbkCshDANGb*#BvR-pm@ zYT!||PvOubw{FNgB(+OgR(YOZY=MP-wec$|i>Fbvpv_|SW~jRZpR;hi=b$I`Af1b} zqBd4vY8ib{eiwa{b@k7Jd&&ss=v&X6%P||-EBOZ59ogfom+EaJFWBhtRCUD(766tJNyNMt=DlcLF_NbJ2zc@5-t zMp%}sILMmh`RN_m2gNq|F}$|u=LI8D+_4+l%q*=;`6lJ*^~>7B>*SC#>m(iOqeY6{ z6iq9iBQJl}!A5y2n3Y*}M0<={vP#yIr#%sB>Bk)C@LnqRKwOAxBIkR=`XQ*w%o}!kxrec*7YReD+hlxLrmr47yh&-o z?fFEJWsx=A`@}rgLY~6HvI`ZwP6hi> zF*{KLxjUrwE`b51WP!GgD(g{wh}<%}v!@>&xoIys#IESw2bBZtgHBE4LDT+slx-RB zRXo0zIFEahvNf{)*=XQq_UF33CXGKP-GLpOkGc`f zcCLMcXxfWZL)tV^kWOQ=XBd~`3XlILBRoxM_@w$Yb%)L{ZjJ-ZIy$TU{Lh;!32zaE zXXj0D^zLE~59iG{yIIMeH~X_GohK`5Z4)8t5i;U9v|;&8t8sLx-Wr;yJyLYz;m@tL zyOEBHQ`y>dun@Y1lT~P6+opuKu3PQs4l20hzvHp$j^Xo1v35uUkle-6J=~P&CU5dv zyyVTDKZliPlNZu0>b{-W#QsO8)c($75v1tm^lsdm#8U7#!#OlexANY$kBeLbyU)>$ zZG39|4O08o?lDD|B>U#|rOm$VcHSZ|E~9SFWdn3as*x^pRxrbcUjeEc|HI*bXjFXn literal 0 HcmV?d00001 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakF.obj b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakF.obj new file mode 100644 index 0000000000000000000000000000000000000000..1b577f09475af76c51e0ac4f924f242281b237ec GIT binary patch literal 21597 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 D6}W@w literal 0 HcmV?d00001 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeCPU.obj b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeCPU.obj new file mode 100644 index 0000000000000000000000000000000000000000..9213ecda55b000b7f34e213c532c8d15557b23cf GIT binary patch literal 12524 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 literal 0 HcmV?d00001 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeGPU.cu.obj b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeGPU.cu.obj new file mode 100644 index 0000000000000000000000000000000000000000..174fee624a7a1b119139ebf5b5107a351c6eee65 GIT binary patch literal 273038 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
    wfj5`o=|~5?KTgO^d{NSlmVg3EDyc(c&jXVt*As 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 literal 0 HcmV?d00001 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/Test.obj b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/Test.obj new file mode 100644 index 0000000000000000000000000000000000000000..bb056fde8042bc0597e308f6e9fdd07d78b70622 GIT binary patch literal 65168 zcmeHwdwdhc7XNM%YM?wykyn$zCPK7IxL5%(T&3@Kn_db&(OTLRs->-M%F9-4sbWbxM{B<%>S}sc^{2=h`p*ctz35`5(OCa+hwdKPgFF+Mgj|S>XvuN|5MJ z|H=k`MPR(2T}t_I(oa^e*s)!L)W4#-rcc!vUeF(O#_RM_rG!+XH>QT`)eVuPB_@7J zPPkrmEidTZ4)6`-ms&mlL|*XoeUg-t#V@sbjgxr6cP>fl`ys#7>Q#>B1s9tosnZ|) zlItmo;;E<`S6^M@Z}7S@yrW7Q>c%vbk5jTi(m1N9x}u@3v92nhTw2}OR9>T$2AV3X z>y(T74;Xc6#;DQNwWBIh`d8M}xRlOd(-W9f@2{+`kvvsWm3LHLZB^ZZI-vH4L zNS&L_!-l2EZF)h}Qm}2LV6WlU>!BC*xU@s?)Tti9HnN1rbIIp<Q0SgHH?i$s_SRTlLrfl>TDZn^L9f-$lEz0mj=7*H zvEnbA9A94PucAOV9sTLOy3D)@<2uM+4*RuDMWXdDTdimaaI^Q8jNb6frpK;%^Hjp4 zXPnpVhQu@evf0J}j1&@qX=i*9ca1zS=icToFUeRgRVPlg{?#UIoKW9TT^p$CUtL=R z&2_Ek(=}IDPx^6h`{t7$zjf!%#I0@rs^yyMan%7D+YdLI2XuNj{)xTgJG?#Qzat5_QY5QH(r>P;KQOOVi2 zv#{5jJFj^=@u|*#)oP%6oS)dN?0El)ecvp(_PURzBwjvn^rFN^691~r3B)LEtP>Mf zEdK7;p_j{dJXX?n@-1T%m;Yt6g4MXUkGgl?yD6=*$~Z7$JR9l$|~w=tEy>ymz~{czF?_opmg1c$I9+} z{7B*rhAN|*s%rw(wPl$6X$N|);>Fv$q+NHzVc(+{RPW4xHL<}^snS2by23BinRn

    lrwYguT|dm{Dfy>0ie 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 E2Q{0nN&o-= literal 0 HcmV?d00001 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.ex.renameit b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.ex.renameit new file mode 100644 index 0000000000000000000000000000000000000000..1635e7a68977f9e2b001abf63b3e612a5787159b GIT binary patch literal 314880 zcmeFaeSB2awLg54IY|bXWClneNPtmBi6&}rKuL$lU|xutz=XggQ36uUvl7MZW zKA%s2pXd4GqI1rES$plZ*Is+=wb$PJr1-8al1Y*z89!Z@q@8%vKeu@P{YN*llcv0p zB)u}?y(@Rd6uo!lBH#UM?A5D(^u5*h{J?(iJwN>6kNo!U-e+H}{Lp^?5AAsiO6@=R z(Te+SNK72*h@k$%2QT@5x#OK_(cjK5J=1=R_|7j2roD@I-J0Fgo)>ZNv=;5GCUcnuh;#qx20wTw8FD z|F-)PmZT2kgNM?qe+6#!n$`CrOYj7|5pX-d1?RqD^?j>;1ivGs1}QC2NG&$046 z=LNPtOWWW8pxOD2lL9`w^AIasC zyi%Rjsm_mC;c~6W;b4VDc-dLuEWFZKq0RZ86M^!Sgn^|=lz0;WAVWU76tInf$9;+^=cR70{LY}FI2t+}P z^L@&(uxdNK?I@Z|ff)#-QlOAkF9EmqnGREleavHXzUw@sd2Gz%ptpl9a->mU76M?d z<|#q|S*%djs+X{{q0>`Ldx_s^nrAulq-)jb>@UD}dXnj|>9F%JM6V~4;I0UL9#2(f zGE|dCDdR$)kEE15N-3n2(V@?+23bh4AmxjcP}e9*u@Fo$rKE+rWJ*b<6gQPh3w~Ocl(C`D6A8vnDT}Do_)ymfB6GP`y@>rKbb7Ku9vZAwFQ+17LZ2rQCzxlpR&Apq z6GL5bM#>TmatN65q0fPY+6}NGf=LZ^iOwmalotp+x=hp& zsnqq=4(hB!q0cR$lgUUmvy&G-oO%XAmCnu}6n35@aSO9@n~0s!d}h{n;iIV+o$DXL4oNT93X7mdhUoAN;aC*KF@(VRu2#656*(v!X}M)90HJvrP9|5VK96Z$m73x;GiI)amn65lfryO=GS6Phac0 z+H9}ulcX4F!*A{$tfB6d4YO>ivuUxG=g7p*?e)=o%J*L(Ng~9KRY|2hZxysvo@3Dv zDeV}AAbv)$FxCjo3Kp7-P*$)|HbUva!Z;)32o}bxO$msoEjDLQs3RVgr3F=*7=gxy zm?e0-RJ9^lY^DP&`Ej=SakNF6zR0m?I*K4S zJ(`8IC1AHbm~1v3WtM+K{&M7}4$EJG{N!QzKIGe?`N&HHj#Z|Rsg)%^iePmRZRfwZ zsOxp_NM=c@`-Wz8>*nRUuCrAR3$X^Jh>9gzKpH@5o@2Qdgbk`AeFf4$E7DeJizNX0 zkcEaLs~TCD?g_v4B$=+rff>M^s0)CLP^KJUOOP3l0*in(ASDP~D*y^wkn2M(>^k36+uDiBCrs!)Pux5z#73cf;EC}f;EEa1dG}LJBwhAU?IU8!Px|B1TzU1 z6C_|g1ZxC~f{W_hLAeg8GaxT^LtZvJ-a*qWBYz4+x-MRLfUh z5P}0fKyWa+tefv5!7y^s3Df|HksTd4L(7+t(S#IjB)YbUJt3&kV$i1^z!D`I2D&2? zU_^xgn+-6cK!6njjH(x4vm!)jBl%}Ns5H-!j%>k=Cq%`X8P&M8(Rii-OA{5Oh%H(( zMUoK_HB%%N5m7TmEQpAjDdIpx)I3_)urX*4ent3s@XN$+HhzWp&BD)(UmAWk{F3oY z#m`b;I%(Qt+NTcKAg6Y8b2`+6!@7A4h(lvR?4yS_(yQL2^re}WVdGBo5>Mn;Upe{5vLNQ_rWi_r=43YIN3GQhIlDya;V6!OE!1j__?5HeT7tH%I5 zjVSS==RiX$xFYhaq=meQa%WKPEaVzFF{D@1Jp5+LE26wKDhWZ4=9AhOL%Ay`cK|9D zIm5bX^;bU2Gsu|;7_hqy;$n1@9o^*E){6aAAO?VrL(|6~%4 z>i}IU+Dh7CARPC)E*l_2{pUO`gg&#>gXK;des(@* z1<`FO9o8ot+RV3sc((^52lXb4%+iH?i~zO~aI_oVB;cnA_(=i=H6pXLg#YPY!8QXi zmsA_tgnbznF+X3{_zAsC3SVC5P3Y(7zZ(SPsu`V!^En#r0$*rAqd9@|HBw> zfS`F1eD@I5CDhD?o)W%LKqsrGW0X(W20)OAOoB!(DWL`ssOCPMvI5vLJ?^75`H~}Fq9VZ zxCnrU_a*&J2~kg_(cpIKWmRr5wuIJz3gMrkknd%xX(tpZwYth(R>DVpSM+RIv};k( zD+DRT;4y;4&=Hws9{%Y)0vM<7<-HkrR~+5M=l8 z+bK^7z>NkW5do-`K+ZLkDFk2)GJVp|fnpBQ%J`}Agvi1;4eZ@CunqpBKcS38FA|@q z<1fIj!XT%v{H%=s4h0!G0=k*WBf5%x&BM?G>`pl{w0}>rpVAZ+>C63;pvp)vO7J(4 z;7g^U@jnPr^P#5@=I6_Z;RbsY(uF?pTN=q6I7BR6YeTfhOge zDj7LqLQFJLl6?o$-BMdz9#Bz;pOGc0!qC%y2jBaEx)k3oBPH2qzn1V70w0C=Fa9BX z4}S~a52E<01->HSqYz&(gwH2+V5;#V!Pf-Rw-Gz|cVM6i#*q1k=|A$H?wj3^Cm9dI z#Q5>4tU3u|eU+{n|avGHO4Rsvr})cGB$ z=!=owGJXgA9#{@Q29jY?(PGM5!sh~J*-})ko=PTr{B6wU5>^yx!DYIa&qS`;DrfX5 z=lGY%!F-#IjLheMG>iGXl;4H`YR*I>WV7!=<+4^DZIGQ>C=&Q%f&?WOO|W@{6sIjDpAPI>JFhS@ zqf%`5@Oehcx1{b`q{HoF14AWKUtKGaqw2BN&6VU!^E{;e z8ps|63|MsMy#kj&9Kxn^>OkykM4r_xA*@A$~taY$p3@jqp#{O8H^(9>7cj;pwGSZY&b-M;T6L9mq-Y zaECzgEXWDJDO!l-zf(ttEP5*yK97HZ_hSSuB@fl4L;hsg#;GO5c=T)3VJFtW=p>6{XD&TxO!qF1m2Yyf1G z`30hs-w7`bLJ{Wg1M@xnYLT#+dgFDGKrS(lZ#^ZvIM_Y5=KcEBOU}9_{pxb@TqK?z z@hlS0Lh;NKPq%o^7SCDYIYT_N#4}So)5X&vo_6s}6VFufOvcldNJ|nW_FQw9HVQ-A z;ybtvnkpuZ+=tGdY{hJSvdPt|OybYO_0uMe1La=&$1@R!M@DVe!^?O*B%sQEx2f3! zYx;4>MMZ_C^z$De8EJe5mKznUH<)kncy-0h7e~@3Kp|@Rojl)&m+@Z#5Uz-}{C==S z=hzF781i33UCn$7pwuQiRq^klie|B%Qrlykd$Z#_(3aLGLM+sbMs!mnW}_5S%z+-i zqUQ7-$dgP;Zk5bmpA2Hu`}!p>|D1v#08%=_>*29ei129TV27mEbV|xCAUQk|C0~G( zvZC2rCFwKUTk|A6C9B&_f;5glid`nwt*2xn0ka-rT6_hUh-^?}dmhyM7ZTO`I*B6J zOb%++T?LexL&P|*F^GxQA8q^_DA)|pX`*(f!}aS05_O=%`sh~BSX#z%W}sd*hlrAt zRQ}*~=#9y|EgiED&JIaD1*4~;Vk_}L?Tt~Az!I6YhD}Zk<8YeVYf?sec_}zvL9Ge& zC@}?Ut5yA?d#J4+v15Vb$_-VPrP*=rck?8F3Uy=|YC;*m6$EuXL|s+`ACQ*C*NWFN z)_iw3*~_yCjDHttZM2I6(OO#BELu|bHiwcg(^F=n1;D7M%tD~9jZ*9MU>EJw4?;~w0DVmuT?RIkPw{O>)T5`kfgSNUs_I|_ zl+d+P<3;^>fY@JU=0Cv@sAxvuztM{o2DM(+9UjHvfgl!jwaPBVyt+Hu6zUXbQ`ia? z+E`LxFJKM{7|ZHYQ5ZF6Io7U{e}a$R{EHh6_Kg%ZQ&I&_L7J}tL(^A{2j5DBQMcUY z`}1Rp;Mnl1QJU4q%J&mCHchEoDq*{a<^KH*?oN<0H;t6<%lazd-=)RYeOw2ZLp!s;^qF0!)Y{eMQ}4*h2S4x+4=@4!oo z*FTCN{cn*PLG~OX=*#SyP9-iozr&wIIBSk+`5mGLR&y-;3*S>{{?5(BgJsYb6~1RF z{xroGP&_)0FsRW4o1~;`qKRnt2}mOKHt+uiTK#w~W&6#@Xr_#2J`b)iqsPhCA;4x} z91T=7^7@aO?1*vnQd0@nY5R%tHfzaZrRZQYY=aVzUe`AahpS9^8;M74LoKaL#?eH{lU z(dSdJ=WVh)+3_A`@wnHIy_zTW%ym4hTuY!62sD|shc3pc9aFF=Zjyrr;7$SPcGUXQ z_&t|V)`j*HWyPI(w$iCgE#;%2;%T0+(L7-(<-G}zk^IimG9D!5-;x&X)TnZN1}K}U zD~hNU%{#Y)Ma^675J+!T@=^?&gq?6s!maIUtw~_5+bE|>_V65PWW_f5{XA0OldEK} zHk0x=w&R9n#8q8UzO>nS_;tb?j@wG`nk7~Z+0{LA-Nh`-of}S84J-vj6ClbMPB+9c zIY4b^U#nkE34IyQsLSH(*2E>#kr2G6d5@wi4a}sjY)guof){k5pUjUOlA{ z+AOMkSiV{9O|BWIHrZeylD9YCQdeleLlx#*XmT}*L^LBxh?cuTtEc3M3RCNrTl9x@ zJtKsM3Pf8Vh82~~M%f}%7cE6m{0 zB;u^QqQZ5-`iKe%W}jw!7R;5^UU~JpGCpUf5Sd)L!qvN`a3|T#2*k2BQ=6-8wbi4; z4*3rZE}=xn1?Sn2>K3ViNUDE&s4vzNobeRjJ{54=NXL;#J%^YU4;Aj^|0Xg7N%`iA zaICWl3*Eg?5nilX?flGiqR`8$0R}B0Fl-}eOr>#EsSqLc8k+QI>{ip*-8x2sACz`L zp?s;XUZ@JPL@t-_T>xz6YXNEKBZ7Sm8vnwviA@)dRTM^;T~ZRv??vBvy4~p2URK;+ zg)QaU&~?7re|<`-J9Opz?+%oB_^S{s_3MNUNe?IF=s$A*bZs2WH+GN}n=xI*X)#)f zOpT(=<(wxVjIj{L%&X8FsP#d6WR~S^*bl5gmschGwoqd}fJ1d>@m-R%G8>bVTT*6i zosCx=_U2&?<-;VI_WCFc&z0E?sgylI-RZ|WX{`-*TLVYX`eaBe?hMOhAHll&{#bMa zhKji=jsKA5KR)dmVX>R5Y@Aj}6?_}1MDULP`g+Q#F;`9G$8kELLQh!)9&D3n@ci}` zN!qp;eGXqi2*J^ZKO`?=WC*N}jqaPh zqviLPvPz3#XqT~?WL>cZgU`c;({{W<&s`_z8Q3YF-Qu}lJh^xt6wfB{Y{!$Y!bpRP zp2^#=Dlu#(XOoBi@nW$OO|IRw9e|R5Bn!NZfV#Z;8>q~#_K~=XPyl;rsh2;L0bad9 zX3j99iR>lT9U7ohgC@%kVGG5weZpe;@oaKpLR^|7_S z5~0SiTJRVs(1IP}`4*lHPoBZE{;;)v3h2pIcM*W3J=21hb@M2sHSWo@&n{m#+PZs> zb$2a|KDEONH-_x&aW+|Z*N1wMRFx3wl`FKs6DZ~iSs(okj4Ry{c&HGgG)SBSd$-`F z?GUWnAy~RYFmp$@NGE1?twk}^qjJ0d4wakyH>=$2zq+u{pMsb*P=;(ZNbu&aI|svZ z*POv{OqUbkIssEFK@mCzvtqka5w89EIwezWlZ}365BIwUv|U7vHFy!zzJXo7@C+q& zeTftnpy2nLMp}383+9;*Y9ijXA6eQCYFSqZK|*`Y@YZ&aGz0@QxIETZ_jt4&?FjNzQVfBn#sEoP zh5srRq=6UQL6XOx$BDH@Q?5~MeUr8RdGcNkhj;`KUB$ppg3`Of`Z$Mrw$?w0F|PKO zS|9%ly4_ljwn-|V>$gF8%JDN77Fq*8fo)N{$KDp$f~rtTGNj*Nnmb!`XYiTx$P^a8 z_Dm;&THskctvK6@@g1PP!ZJeYdI8Z30b)}Y_-hS3g~Va~q#-(#t&AMXVq!d~7j-4> zb>cCk#sIVd&!AXpyQpq!{r||YMF*5oTis%Eq(l~=XIp@nNsZ{D-366{>z{xowDrM^ zkhOjqa8sLqPMP1X!k=Y*ZC<~}M=P6PfST+H21qS<_}#anM2ihGipTTv&Kf`DqiLdo-oarU%{mDPe*mJ0NSR=Y& zrWgX!^(O?k&K_(pj3XB$rt1Kb)!u6B<9(>xTK`8p)!zH8o8J=Y-?Kh8gVxHu_xm4E zdw=LpS9^bC4Lpmy9??s+2H7A&^4}Bb4YWPHnB6i+Gd~lCGKbLJ2t&XqX7^iGvH*$Z zN-aKYHiBCG5M#Q2NemVW$8A&rohdPqa3Lhs5~;risVsq&$!eRq>qA78(Y0THPr1(! zC&Z<2MYa&Fjq#dz*X<%f+lHKtbC{6Jy4Fendif()V>An;4RuQz9Q|Z7xm9MeZzY4Vg@vhgX&uMOW6$RVGv^@>5m-IPQBs$@K zKsE{hxuiAl0Z?T%1U|&8EpQM|$b!_B4;XYB3RnYo66Q9!CuH}h8l33~!Q`>NJ+D73 z)8P3T44HZTlxX@H47$TZKoea@A0EF!cD1e1Cg6j5i5xHYCjt?+WIh%V;f#5C%SIqOi6@4AgrdUkU6$ZFZL5 ztBbMA#IrRf%N@S;_LWGFce`B&l=B%v4;<wtL^HqNQy1GFhSTZF#(CpV2~t(uSLF5*bI-T5t5ZLWu;YF z1`>XQfkE9xYM2&1c==Zbnl^ry3C2aHjo0G!ATixj6(7t$4;}jyA_blT{uP#KLdz=Y z*&@+z*LP5+fj?M$o}WDrB6m?~sY2NSC%drTbcmhk0U#s2#P>x+4>um{`W)^3E0(OG z5aBFXw$%CF-Msrxy1uu0%O9@6DJvC^i z(@NDi-bQA+`T{KqrP8XRpjZ>q@#(!_8rp<(EszfCf`KeN(KIcPi9kefgC{8bBr!*O zhPqQTEKOnOt=#-%FOpVn4xSRPXQ=;HZVt4F*9%SJ^&4W!(~k>TWchNiSZ)(F(R$WU z&|2_3K-lg5#(dT$h#F5zTR|*|Ba|`-HI}kf5<6zBUxR{hEKg|#i`AP2TO#JFSU&Dr zC|Dn1xMTqf&Jtgr>Lk7jwn4*!X}lS0Nm|94D)kN&|0o^N%C6r+X+@a}!(V={5YwoC z>|FKY^{dYY3;(QM9K9~7DzO|qm)|QwUS)G!9*C?;s&KVWY?>NUo6MEXJR9B<&=$XQ z`z!E;BCVNU6HR)>NU}uMbNmk|4)0HiX6BC|2>(5Q3vVq8i@E*2jyv4a3LXA63{PxW zrMCR@)Ca+bY~)r>Wbc*uO8eQ}7A>D^#i!K10sk`GuL;U`v!YEn&b-N{<##hU zm3!gxN@gGNXW>#nr7?k~4ep2Mk@M^rYE=%@)?l<7n)1g{|BJNzw*=btxP z)(pa_rQyBcX`smHi!C{T#ALx85TMXyNLOq4j>0|_!)VUxA zF4OFCi+_yI;K&iy&I^oWR@|!Pkb3=nH34c0La0Sf6MTx8)$?t03dsXiE{o6kFL0r^+_kK%^0mgVQ~4A4HZq z`>Pg+RE#n?D1X(2kT%QrtQE`0EVV6_|Aux`NX{(<>VRXzVW7Za57i+c;Y6)wZlGfaJmx0^f!jyAA1vWau;AA+&mm;g^Q@b^G(^gQ*z(PJJHsl$^)2Cs7lzX_^Ni#ir>`u%?$+}kApFLU0 z6wD~&&!-xUP{x<>rJn;W_YRiQDm%lYv|GaoWjvR%F82Vx3CL!kO-D~fb^_2cZ_sF( zdaI<2_3*1G6a8ea8qGfd%PRDnFZ2B;cA)RJcH;5f{h>2()ZD*UhMf`I979KRwc>7;bh}mzQ!2(9 zID(AQVA^yxR$F|YdHc1+y=?J$t zRck6fuiYiH#TJI=LbFzC23fcOJiOM*UiFVLTIG3b;Bnx|fYz$)4fV-Yb2Pb_<&O>}$recaNODaP&$)=q)nI%9TXa z^zaqIJ3B=BD+aZcCFp$sWCDws-7b@xPhe!Xn>E~iRV-l>cJb7j^Kgl}J~7t7(49fc zAyxrsGn|kb%FoBsjHhe9Sy|o)b~O}R2SN=95EHMit+7f<65A)(dL#^bMobW4!QH%_ zc_UK8*9ssT*g6cVLPq=zYS@vJ!FoaW2pd$ohU=|a{Vcy*H(LYekiXU5P8z{le+&_p zcpd`9W>#Ya`y{s{;kLS|(DEj&oJvddqei|c62U#fV-`{7wi%h~^9|Ic2418N6y_QL zg|^xUb0uYjx?CO*De8K&uH07&UvjlYKPr;+Y8#@%4r4Gw%!x*bIZGjV&u9-8jXqTp zEKG)DGx;V`ux2tJ1*j{IVAsm(56sD8bZk3CErNAJ=~@o8@9t$w_cm|sM##%%(lElc z5xALWVEgO)P03hfQ7zcKGR}l-054o^!YRF8jiXXFqtrGr_#P-8 zR*d435=a1>Ylb`a0b7~#c-UlW%Eh+FMb>POTmSHg5p`5Rh zS)Z5)LSI@<4_bWGqY+|A&5#fD8T4B4-=u0NfyMo=_K_EAO&G}`#kNh{lTnMOO$7WCun%k8eS!428{Y>HNFgF3f9witx$KDUyMV>9V;4Y9a@HFzWOvC?ua{Q2`^v^)EQOPu}asnCEtAF~G% z)B(|?r9Jum=zv;kveHl4(F`!hwP0{U8K4Q}0HzIpe3aiwLleq;F`rU?qL4*i1^lX3#TOVe$E(vn`YATQu~E5CHA0&fG2Mi3ZU2wm1K$| zRilQ+tDkpcf`)XK-VPb`meq_>59miutHr%~I1yr}?!%eJBPVH^P853YW7q|yGILE` z+>()JMulwK*AwL|n&KbJsEDVK#{n{|%Ur0rRnPci5uB9X_mNH%d;AlpJwM-p8xE zQ6hyu3y&Q;hI+7^N`Op^ATxr02fpG8cBc(j!ErP1vZHND()0V-kL$sFe6Aw=C25;)kNl!7c}{)m0cA`C zF_2c-mqBmtorl{|7g_Onw&y5;qeAvCq!_IAK^hr@l6i+&13(D@XeH(dT}utg95oPvR5n0L zYo^xLjFOa5Li8|d&Wv!eAtCJ_v7sT4j0S)t4hiUphsfp%zGaX)ze5;BM^2Bp1&RzS zt5MoWt!Ok2dgwx>+jNM=HIi<^us+X{f)B=Mw+8kqSGXq8JPUTi))K~qlAvBqNu*Xa zbMQ8KNQ;-0V6|wbwFeG#*Yvy6zM%zye*_G9sJ!v3Pvc=#>ELpei8p4!YFp0*Y zS1YD4oLKV70N*_qFgPID8kfHp^y(=Mn37S!HNF$T-J^-Kj%J^Ib1dP*%IMOT5aL-d&x;Xg$IT1Q%O5Jf*op*W~NT9ZcLy!yIacwX0>Etgs1JcKaT#39ucJzuvH(5Rg^!~ zXG0d2z>(9Uuscz$mTv}iaE#z&-=jl|vHy2f)Ahu^R`r<=qE*lSdsXWxThKsk;bRRR zX;lg}nRR0ud!jzGkhpap5r^YQQGh21pmk~UAV!x<}a&GIUI}y)qEAZK0!`5^nA9)t2#WAV1K0W1WAc?M! zvDJkG>%0rpRX_#6Z=w~fBI|T3ot{8gU%QHroea^5mVoa?;#FutGspw2Kz|4$8pnJs zyj=vyw_zOP)@Kq^a8C8d0EZ8@*h*-+uTL4SZ zaNKP0@^^%6>nRekbH1!&A8Le`-*pH;&K}hCEYg=z=b&Q#Fe(!D&^f2XBY>eC+%4`U z&i~*Pa_Km`wZ0eaU<_a|7MKl3ix7O*@V}AHC9wOi<&&!Nn%B6xx zL+46t<0_P$TE^ohp>te0;;>6{o$h%H#u2WtAvk`C8;73j2A+9(6#sZMlHoCG!W*Hfu$eZ7FCpmQR%khtVKk+XIv`XBaI@!G?AqHON!#i z-H>!cEr%2MPmoSWBuT1wHf_t3cGIH_9<$apR)!N-;{1G@>>5~oHUia7Q1tkFP*`{e zUwf8x)y-U{=C$G6O1!G4ujW7#`81Ar8Iwt|IdU#B#*af{m@DSaHihbDi8G03aAwyy zl~|Z+ePR#W6a7@qu=3FOg|kiV*4O4HmwK(QJrM6aT!wQbsezug6KN4yXmMR!J%W{+ zu^Q}KJ@PiSDcyB+H5aEtFF|McyQ>C#_7~{d8u`_?qI>Du85ZYJXL>K3>4Tngc-1jn z2ne14x5TeQ?;4Kd#h8XLk>i}4k>e3LW^mQT+>Q{Ra^axb5fd4I!~O5V{f>jabVM=6 zc{nEy9|9Fkrz46HcpN4l=CLEU5=8;uHA6TXK#VHnbxfv@SX!DojXoGqY{;Zvpp4d)mK4(8Y51B5yKB{qif3JmYT%xYPp&XVY zvL{jYZKlsakn6~XKd{Pv1}(NC=P__l8PQg-LItYu(wr^Pcpb&(=lnPtuc!E}IWS}l z_yEOk$bq?T#2=yf2|g40RA6BGAKlmr;6R~^7ENq#5IcK-_D1q^Lu_L==7(Kvsc+9xX$*#bz>2im;l zNLSo!!3wSTVB5SEn}LQ>ZZMnXULfB{(1Eb=ZTY&6S{QWekg z4{GyPIGcDK7&K&6uL2*2L}1(cOQRb^6rVJyrOu5d){2wq7BYQPwqp{5br-&obcaN{ zXC2PHIpZOm#*I6ukGdMVen2}AfG4Yl&T>a}5U%i@$8Jh<)gs@sg#S52#2PM`|1@mP zw`h(um{8@evg1tU&tYcaz<-;yo(!&SbbL~q*@9=VfsP`Dvs&G<1XpLPJ8q=5-rVYT zNUiRxN4C1JL3CEDdj{Q5g)>Oxckn;ZLAX4f3B){i88lYfwK>tHyW6)cy7vJ1`I zEdU2L#Ik|5{0w9yplP(}sB6#a!+L&4b`F)_@Ju+KiI93 z`C^r9XbWC1j_ZqiswV6&L{BM`8-cDhyxdBI`n;a0R%G zL=)%Q)_mrKaWX0 zZj0)-;%av~#vciTNzd<8_2dod0*uM{_Ng7c?+1TGiasNrZ*Bo{<7!k|{JikbqxuY1 zBt}{%zxWvq{TRNIHYN?mkssd73M{O>_H&c-c+&YbRkXSG&SaQt- zF%{t(y?g^COL!2_qv0uZY~PJ);g0zoMAE}$aueZZ&BP`$GHA7*?CT(Qp8j-j!aIS+ ztf9tW)k!z-I$YxJm=#}v#YPt0+X~9tvu9z`&|s(+&G?snfUj^Kz73ws2rsw8e(VXU z{u{Fwo0ZX+oQN`F`eZOYJR!TrtR(k@VyQoFjAUV-12;)e}pzrPn>{gU@DK{QwVP}B*y$pG;tI)kuDQrf9o^4 zBG`s|-!K0@I`Nj#KRyF-OuNLr(&)A7S-773UD~_`X{DTrP<{tCvqrHs{p#rvxTFYA zpfY&72_r><`cZ79gZo)ckSN?4Xyxw$KbGe-^C_Lcn$U?!U}xtvmH6f7rrnZTAkzq z+&c5>1Q%e}#>{sf#!CC{J+{HaO-!nrCC+bLr>=+d-@)9;Irg}pPVlGXTuHB_oU7=S zkR!Q&7Nf-Rk8w&1wi4E*zV$!A3QJsfaW*x>>4l*t0b(jH6kz|5t|MU?19wd-SlIO} zI59iNA2ZL(e{F;0VZ$P(F}}V@IawvoE93u4#x^v8mP413plIJUFqDRG^YZm5z>ekC z-kMY4pI#MH%6(!vMmJjg^S$`I;AAYJNgu&+7Ii+(|HSCMHraHp7H0=u*9Aqqy$R`8 z6LFF{4GUHWsHPdv*;E0;5+)$k&z}=e6qKm{JB>|7Y)9e4%U&&k{RLY*ZR7%nQDydz zAiFT%ENf$7NY&nYm13hqCjGy=%KAi80g}SA^@PA-zqv8KAr`~J(m2`rYBNsP?ZD!O zM(9{Qfo_(-%rCYy#_Rc(hL{4et7NM`j;kfu{8D~h+h_O3)%J~0egRB!>utKY6^52s z>$^ZkWBhEpU!INg0tMl7^TU4>83&OeF7AwgEi4GM=`pwk?A#lUEtp$->k|J(qV^gb zX@J)f)%a=Os&>{@SbB7a6Y*Vy<7fsNfi@iKg0p+f?V|G7CAXnLdzG`)7WAQiv7pfm z67HNm!apx?+@FK%H68#@BS?!aaP zY?U>)vEI;^u|e4iL+fS_{~1~Q-4(8L{%a$}O#@|KLa_InD1yRmbe&EUSL-mp(+oT0 z>)*Nsgkjqj<9_A`pbneYP)g0p4LDSQb2olVTO2`YwtO39;I6K~ zfDM2^9f0Vlj!{(XFzrJUYg?H;#h>JMugp$WE*h`Du$c!@TWM)&MqBBjUbm;Lv{cJK zuOG$!IJk17hrdm$ATS59D@#jp?hRK-&jX5lHe?6%^O{`*ffZt9Kdhqtq}Q6mOJEN9 zWi;RsJ*AzJy0Lv+zZyK8HQ7JX4OaKjqbH-)?Ll3<9MxdpgKP`TkrtHb$_Fs&!>4qn z;9M1YAd*3|{?I<4&G;t9eZ68Jl$O0c{|on6_ z>31ra&{XI9eSS-arh)K=N}`o^Upi;yPe&x3rfQ71;8906*#BhVs)s)HmX|_ zhxjfW)n-x!OP7Hv_39=Vagl{T4L2OEv-0wfXig#ffYz77a@$~b343~V8vhP*BNiNi z@I6O`&Vr0wfqUrUz7#Wza9o?R?#_#m)ebji>g%6}z2?74<+6Xg$`k#TLO;H6(Y#ZZ zLBP&2;wfX|Y~DGV!sO~@-9A!Ra4Qw0;k%vmRZZNBdaa#>aHfLp|82B{KDUhu1vlDW zdg-Ot$-xQ6132Tr+f=YIIi|tpUZ7kEZkmMXZW74GWHZv{D;I=k3ws)Mr|4_ULx*E= z88N`&|M2K*Wj&=6Y<-)`k%>+|3};hAeH?xN#EqNE=Nkj30ood%1Uq!f4Z&y5Ai?@- zyY*F*t9{+LjF20j7Eh%7D*HlyZ=#TmT-?LKEOHFi=~_!L(2pXh&4Zc=5T8)NYLd2$ zoX1O%j#LcGvcc>!6e{H>GYC~)1OUg-h5*FX69M#&0CEu5Hs@0{(3<|F+M@VLw#LnI zgf={yjF=QkvQrWeHEfP2c7@HY^&%iqQ+J2G1{d1`oxt-8Q0&}$tq8LxDJ58+M5Rmz zgLm20z6t9rwM9v&6V{>HB-C-J_N2YR7B;m?(p=a3tM_0haT%S%wc*On zUb=#vPR7n;Z1(U?Sm?9ft>TvDylVd>_2E^Le>_{~!~AT<%CF%~jAa-lHv_mC?(dpo zhzs}kIvClvw(~aQjFY9#v4Q;em1F}x<65$mH zTVHE~Zs`VzB>xMM63piwC{7v|l;lK`7^K16G<>5+#SI-;(HMnv=wgi}doj0eqWzHC zn$tl4K0-m|Ok-00UTggxM69nCV6g1%`X$1{(98xJR@@0R-`|@B3qJTi%c+xKr(V}s z5QA}+ZhiGux)2MNbu`u%A=8}angnby^IW}aPr^}IP(TLMA@mzo)bLutql7P>bgu4V zZ6K{JmzFaU{~m0zs(3Be63n#)wMD>l*Ge{h?n*6duG@VFu7I559|5H6H4MwzBGV+< z?Fy~!0q%`-t6~NQK`?jn>jOYlYc{)YS1z(#nU6sUpu`c!ut~$=X+vchn(86aRN)u1 zXOfIq1LeSrnU%(XIOIR-RWS&XeT6g#CLBg?aR)xz1NR!a=uU9D!p)ZXuyEoEMlW_f zUAeNgzKpuT9*Y+3>ZUP)uK^jp+t(XF8%7N4JNJS*Rl<~L<~vakMr(ctIxs*!Y^I#h zY5OiRLPoO!UydI2Kb95MsrK~QYyv0*vc{-6!g`D4os{a{1DOx7Yt*nzTsZEp zjT^pr-t-nF9WR3+@}r#8-Pnxsu}#~NhJ>ZCXzpU0$iI!)c*Jl<_)o7um|mq)JmWeT zpA~G=E0+MY3<11zEdl7S5}CD9Gd~YzRy6tS?~tsv*>Le(Y4h?wP!!fu z#5+L7C5aquJ761-Jxa|bBN{t!>`~(0V1#HIj5G~Jg3AWUddMJa1T#ed)rYWj7>V&3 zjW-KAfSMW{M<`Lz5IIB!ZBUXIfk>ml^8XoG*!gM0N4K!#yanVty)4Uh!Jl5H&Eg@F zO3Zi@y_`%^oItO@=I?;myv0#~3L5*Y4PVV^qPf;bYUa1^VXnObZUNS9%(Vs8u+uhT zu1(`6O0HfNnQ+HbKVrgt2PJ4%-a`AfG~$B=tC-j*=J%sgjO9zlTL_>D7(O_z-)Rhz z5<0l<<)31+Sj)$i5lh93k0|5$r~9Lpr*Io#1x`l~ab+!$aaurhX!bH!SO^BL|NpdH@@X?Yp^f|A8MGNn$df^s}BdnH%=ayTBC5M)u)6G;e*cIFpKyIP-z*%v%kIW;Xn0}d z3nAFsxFgdTqlRe8oNpv3XUL$DB7e9e`u96x-p~gIHkuBq-MVv+8j^8CFs9DW+1!4V zys5s^@Ax%FJFYw7CF%ay@O#0z^e2bpj8JoM?!aK^o54_jBqW>=WGqpSLm{#no9mpC z?CM*K1MFt#f>Oa7UVO(JG(sD8h9EwRj;z3}Z&!OG#;&w<;!BoLo|hle2m?Rv5IRMZx6kN{cnHl!_FaUGMsLX))o~qf6(1 zu?F&xLyJ=Ky}SCXo0j2C%^Gbqt7`$`g4g8nrcZA~yww}X!e~j3=u~tB(ur|a*v{5h zvD=8Fl3F{sVSP=Wd$m+crWzDulpzsc1tB$an9I=h*g7Y7UozZ%>F8wP&>M93nc?p1 zX3cQu+2GJ?rMKx&c%E?o(Vr8dzhPzmEABshoeVj)LHC~p?msKsf9AC#=4T*dB0fs& zJdThVAY1oSqkUiumc97B1k8kG50&08c_$h;_m$8fL*YfJ=7d}=$H~B3YhZ=CJJ4Wq_QF`#jv<|Z=L@yg&`|t@*{(H^$g3s1z$!ruh7WV zO1Jm2uvZS~Oy>mr5k#yaa8>|+)JXk$0A7i|ti2&PV{LHB?`&)oE|{qg$&#{P7|XD) z_(JH6*U>C8w)hG}!rNCoE`~V1)CqAmbirJi=H{IQ=XVmhxPRts=TE<+<5-_BmF{d* z+w|&Wek-~dqoh*6pcz^${hbY|c`>LnvupJS()0&X^#{^YhbO})oXp^c{Hl-qUc#1Vl;YoT$xw>5?{!) zx(1dmQ_M@3Rd!vE5r3hL+M?iZG{&MUamZlxb?08t9Zz=FyQM9x?G4IRyX4iFKuo4~ zd}O5;V(8^&b`o&dTTeK z7X8A<;B*#(z!mM2i$O-pP1lx8yPWS;EJmFdnnu4F&la{y^8`X1ry*vHLx|Zwwx7 z0lug3dlA342zRt^3QXX@u)Yv+w02lOplyIL(k|({$V`p1RAv0N5148F)|JqNO250g_M8L1u~1047aYBHs7BQ*L^4?lH8 zBuXOjSS0ZyBe8P}L7s>t?m=S3wob%g;U@d`Qzl{fPm$h%^sV&3)VUyg5`p>WVWELK zapMay+?DPlNL&PkHJk}cIbEDQo)9eVEvdp3c&el-#-o1(Wq2aNj>NZU&EArnis|A=Jb0CK;`*Jn~Qxf)5wR2!Lv1pw_>3j0DZ z8l5D(|3H<1!)mZMCi2fh!rIZYguTOkh+^u7wSyA{WHreRso_u9!C6=M<=Yl0o+Y>&cPpTYe?l!5t^$i@E7;ahId3xyNNo<1~bD3%dSio_-wE_lra$q zSUrP;crPX7Gul`ZgnURv<%Y@OeQdGKbwmUYsrb|vYgMd`BOA=_+d#`?&=Q_77&R}z z7k!J9mD5x>wjmW{GAe_;;(SY$r(sFFUf#7H9oI0KpFmYn+8;3(RUBc|yF>K7N>JmL zMj16{kiM)aqb7h+q?}{HoaQ0sOyCa~5Cz>jF z&L`sNr@3kzpMzk90TOGi--q$`f9I#Nq6(jdp=2D5a^ssO2pJ!X_$!}dzzMG9FY0>4M*Ans=apCr z{;m1^mCcwNqI1Xa`MjNpxub>hF^3e=`xw$>1k&*voTLBVd`{S*?+0NcbHioxxuD^1 z&gT&vF9Fq4_-(SEXfj5e4aMt^K^LYffj4CYnCF7Td zp98;4{AS=c4?pzHKVd$n%9y?!Pal5k@!L!((Z2a7%;(Vs-{jxWinUwa`7#ts%*L^_ zZCEAKvhJ%XVr3k*v07RPLJ?vuSg3Z$e{ChG=4{6tM|Z;Lq-rYE87&+EOgC=`TL6lj z7KzbW4wL2>d~2|7*6-maCTC#ZF91O2ui<}cApg@%Sc;-j>>$6t_J*pCa#~Ef@+}ja z+{zUG?i=Lyv4AcdZ@~eVQp7PA!Da9v(yafDYx-w3T*|+on4CQY;zSj`y}$1M=;4<; zjZfv{3j!e66(>IJe+v$-l@IxZriVVm{hvRlq}PxX zX|y%)Q$)o1U-5Sc8WBTz;e**#7z4X*GSM#hb3|Vq#!{1icP9=Qy zD5ILgx8Yl;s{a-*pK=VA3p>UaPNw@h_Z+1Ybq`LW!H-W8*2Do5E)N%^p`S;3_@bw& z@f*{y$u2JIV|-hD?Ie+p5N3Q89*5CjPNMecVYDg8Flsl(>aVbO1QN&}YN7A3`YnVE zXAT1ZZ1(cz=ctOBey_MbF?pUq=5pQ@b<8HeMW*7v?B^r?5wddyBzR<6RCc4J+=cqNWtffXzlil;Fdsu4Yt5+ zl+4bzSvTDVzH5OzL>mLtK71uKK!0GUaTF7+b~e>s97#v30#q4`H?^tj@!`3YM$e3f z#pfG}zoN6$Rk8Frw(yvk=L{eZ|K%{4piEvRkEbdQA9TZ7E*K<4CHTf7@g-8)aIYFy zS(cyxuE>hkdT_etyoUv-E13lL8CPV9>(>H|iK2Yp|9s*lh!4WOr0ybAG1krRBccZgzb^0!2l#AUgqj_zTxosH>D;?g z9RRCCxw)z!CRW=)?K8SL0=Q5YY5aE>78NU9f|J&OkLnRfVuf=!CVAc}?zUdNR zm!N}MW>ABvC|lHLedOXdP)5^`&yxNRd+!4fWtHy_KQr?T41Z@(pg|EI6kHTA5O5hl z8<0_Oofa}wDoQqCg^d!ON8|oPhepbAoVNC6zuMdG+OBo2b-UiyO>^A>WdXI!f48-A zyVUZw4~@5|T+r6!{d~_k14OlV-{1Rt-`{=zj6R&_oag-i^ZkF$*)f^da{I03Twk=e z)SN#MjSo4;X$oK}V}8=zU_QPI*+5{%wR0AR%`PWqmx3fPr)M+C=ci!LGc?zvbT&8q z?Pc&3IxQP)zFqWngbme>NyYBZ!@)_j-s+a3?0jOtiif|n{f;sjnGDhHQQrlgOFB$z z{OuVCufXJ?eEBLF8|D&LUy7k)eN}kOyt9CXQPJtFC52cM4=h981EgB#3j<5u+4Qo_gd|C4{NhtIQ3qO z-QLFL%ynwtZ~p%FeGjY4$9iC_eYdeNs>itY-9+uP;|puhW{TqfN<(O!mH{o_NyAgb z+k!^uenT-0r&v>P_#L&u(5++Zh5tnh4Bfm1dja;=znU|&V#V4jm_hmdgMbZ{em~`# z-pLNFt?+eLtXNCtCt~4Vz{M%s{<~OYY&d<#(z`4C+wv&!*DYXIq~SLdvGCokCj*gO zfx>4#Lhj%To41}XXg0>-=!3vbm@3N105#|CdnJ?$> z`5lEx>OG#{u{f^GM&q3}wnEid&9I+vV;GvEMc~z&6}U;PUNgMafo!oZ7`(S*=NFrS z?))k2tfCa{*uPMEDE}nwBwjrVv+<4ev5KU#Np#okup`NN{Z8%o|H!OJENeAF*XIx& zv@hBAP1SzL(`qi39aRU|nH?4cMBX!M=9jG1BLdXP%SPjIlz#pNhQn}%9$jqWPh`7o zu=RrNbn!))Us;wdd*0KzUain>qVwf|-F=3k(kvVq7e6Wv)8<~GTyO|re;{5PK$fCs zKEbOupJ;mP!YiDChr1@5{L^bDq-W>W27L!MG4}*Ei-&h z+%jV+&$qfv_QI3ukmooCEH=Hftk4nLs?L+2(!`n!uLx+R36Px3$ zxniqPupcD^wsBsE;>(Lfcf(e_CWS0~V?lgkV)^CYBQ7j9RD~N7o45TC0b3R|_T`*i zH9U;`*MHkzdXs5^1pK{evi33vE?*asRTHDU?Md!=Gr9=24$4$Ce3-z&7T+>FdR&*20tA)NR-iNkzM6 z+iDWc+sKxs(3xod;XmVLL`{S`dwj})N9g;>Ee>A&PpJAhjVMf1KVJJBvTEOD$ZQ*X zLa6$%a0^VS&^A9hu?4V+$2la3Sj?=G$7&ZG!x_t$qeiN{e0d;*SA-&>XB7^EVeh;` zyKxZtBCR$HFSM(5pqdR0P7fTk)tLC`#KIcwOG8C#gzh8+Wq*n6tmyQhWTY!}DCTYN zpoOqnhbkPyN8_d?Au4!B^f|`hyf4p$ab`n|ue?K8aRm5TNZe(^li$=F!4c+ z*ZR%qm1~fT|FI{O^-2%xmHJ1%(koZcE6}664xq6(dgbJM3?RXSQGjH6bO?EgxR4=` ziq3&aHU>Ijpp`_1{@gx4*6rngRyRwJZM7^J&N6LFGST^{QzA4I^p_s}0Sd zQN_QO23f49OZhqoP+2*)(4W49rN>wMRan0lkjXhX_7!Zc!wyynF7rH!7|0#B%*m9W z1sYiZvh`~nTfYhou7$1N$g%Zn4X)o`(hwK5jbO%Y z-IP0i?S29BVdC0-fO_srYnO~5T@8NuCR}a5TpRdzHo_~ZNQ?)qUX%nF1`$sz$uh7p zmYRzJU#{8zgU|ASic2=JI9TN|y%TLU<643t){-Y{+I_+WR zy0izGd%w1Yxtq0(aH$F}xiQPMwG{f&HhQgLuiNSM7WTT6UYD>}0?Jwudwq;vv)Stt z^g4^Zk_D1BmA&q!R||XHL$5~mx|d!*-$7+(kcmX_x=^Sn^L!zNMrJ7rIPeEVsb;qc zC6etZLVnLeVDgZsun_Esq>u@jY=QM#p)p0Id6eg3DKw^ukgYr^2>CV(*_cw8SHwbAXsHx}+As|d^gAoWo;KXpgS}BCtJjBj zz+UaB&iqR`1!oK6P=n(m2#0mrju7^#TW9`-oU-8~Sj%-OuwolC=z%eeOa>^!N5Mi$ zYLQQ0pi}1}(X4`6othfn@0!-BSqh5vY61dd3Th3i1y*dh1u(o{g}4mHj*m?3IMe6p z&>b5-ipejztUL6m3$pn$Du6^6hmR7?;@VU1!w1U$6hgZx|KB73-+4Oqf$hN>{eA88 zjj)D6`pFyiiGMN1om$4m9mjt{c7=Wb$N`D8as@QZ+CKucdENjd>rrF0Hz4}rl(DbJ z_lLkzzz)Cj8bAwOCYQ*86(UaNU>UG1tU~J~aarIEw*OpQb;yZxKZtO*@&%AL_I%Nq zsg1E~(};%tHB#Wq zL%2u8b?@Ki6d*ABD`Fyp_u%l>@(dby2G}+0I~usXLi-rf@$viwpxzjA)t+Ne&*H@f z+^W976zv~JbJPBbeGf0fzjR>=zT$Z^DD~OA*)&g)Zx|8 zP~gN_^D|@Za`iNwBl^FIY%P?n!}KIJ ze0-jcyDZJ*Kx|w@Hd1Fi?{CE!&xI#klhRM%kh%>3Zyuas4wX&SW!SDc3=T{BQOrAt za9eTg%H_|V$G5=T;eUrUwzqYVg2^(Ff(OxXDEke)>8EdLgt68ZmKSMLM0Fq*E~9z$ zgQrmlY)0zMX`SLokHAcEBn3On#udH*@;exCV>ycB0wx?o(S$65tPiY0vAORWs6j%> zPp$IxVw1QcRs7_s+tSatrdR$1`zn4;Uu!gP)g>IH$*kKDRE0EGZXb4B!(hm+XzdPE z7{f+q(h+lnO*9&z&tH(K7;1t+g2hV}Ge_%x&JJSSq%~q_YX}9Sw*hzL;qmr!^O$L0 z*|nTr?T&5Ce$L{nNV~;Xk#dU`i!#yCUooE}qd-_OR#o7Tp-U)4oZHVSv91a~Ob7Xi z{D_rCwt!>>IN~bsY);9*6>f#Dl#Gp;umbc&{u+1M$a=zdpJ z(JB^XqM&4a!xX_OHl8aMbuyx>(JYo+v`UqiP!a!1)qzq6x`4C8;8I^YX+9Wv^H8Vk zGMQgF=^4lhOoc%<%5!D!IO!g!bzeATdG`mVq-GA zGP@}mi^}?4966#e34j6CL$vGyw_{7I`Cn>c4R1kCK~49)g3`C{#lbjxb?O37pWYSL z2HRdqa&HPlE7NW2wT-j1#-CtUpu?);#`U-^(0OenU`7aZ6RSX+8@jOyF|g=Ry*Ho2 zU54SBYhM~b9Ag=<2IFLX=@S+lAsdGAKoX2{LpCGnk=4+(Sx_Mdbl6V-s${Yt}6TPjMQ4O|V7;3O5VQcI# z<(x=A;d#$Y&X>Wj?+gy*T?!K<-zm>~TfI)L@DtbM^iN2u`Yj9v<|#oat9`bI!140JH`F|sC3jkI{c5l$`ti+aG9Un{tNPLD z>5l%2TPn3>qa|P?Zm`?1=U_np*)uR*e$T)%rL(c9iI*8@V|U5onh%VM->GST1$_>! zseh4f0m*qb!9GjlHUU^vs{$&{0PbuxnP>|j>c_SlRA_r>4+yDvCgI}AM*VijmF+cG zyB(j?Eyp>>x7S?hc3hQ;krP<1b+Isc_dBjtunTdK4@~eh4rVUPob zK&<(o1wyxKQin2AKxkAg zYu6I$%tAPNl#6ID0Xq@pbp|e{he(|HFe*8L@H&PFpqri>jmPPxY)TKp>+cZJK1J;z zLPCgWVPRm>PJ)smT`X>Gy?He2uM=6mim!}pV%@@*IMK7f2+2iZ{G0!xbx_Cxmv6baWjV1-~W5}RIVgxKE#K0eM? zV{uvXlbDGNBoapG^1RpuO2+^&3Vu-gk(b7scCDz>kvSp(e7zKnXyZ5h62WqU%yi5z z;Tq$jQ(~R95>2+9u%YLwZc8fEHk*~4vyj4>cOAV216aADF^ugjAvtM#2Vw&tIW2`O z$$BOKPMvG2&rRvsE|ogK4YB)Wpc?O5^?#>%H-~?x<#>gbBhA4Ad?vvwN+L)OdUCK) z_vO;e?G!0+m~{(@nn~C~v+6u%6%#iT4@E0gPRFfC!>4EV`npmj$ao5S*N6K$s~^K5 zS#rv;OroyarN98o2oMj(vV|vCtlWHiVH6!awPCrB?l|{-f}4Zc;aH)Z%Q19A9jSBW z(TU_)!847grJr%b@KAY%K_8!#=is%%NvJA;!HIBp`OSPv?~y&-T8QM$!Ya? z_0l0QAf3jHnP`PtwWat*EA+)E0p$ zhBm>Em5a;*dLLa3PZm-HA1sI;v`g>}iU@}$-j7vDnhNqy89UPJ z)qmyFMv?!JJ0zj^RWy+~ysw!m9(L3?r2Y;!K(k?^)j%pYp#}+!jvTd!5LB`}DY%vmdg)KLgEC^yzMSz=F6jf z>p*n?8gKfh@de_#)6=Ty)K!GmXtI(}@H;HXfsN5Uj`Ot8L5k_+B7OCaXH1n3sj#3 z+wgg{x26xo;WYBvTF!bVLD|6j_~vlmWP*PrC`j z0EE#Oi14&t9B~mZ+`O9BeE9h)+SZ)~b ze=T~!zV^V6u{|J8Mh$;Ku=p5*_t<1hh!$YqMW~T&{Czr$BZ9FIahZw-WDM#H!+uT6 z0b2$-hMo6O`0j>VK~V+ShgceAI*myhMjV8LcZl+}G2Q_*j;9YdjY0X^_8O{Xeo3|t z`~ok6{@`?p@7Mr|`9RPFO)0a#mM+>!MqcCTqHEi0zUX#b2VE4W{RMg&4r7T#y4LOZ zVw6OZ{Z&^Q4YyR1q-O;vCaSRZ1&)^Vbfu88={~?$;#yvETyce&Uvgf>9<_+hFOf)N z2hIp%P1cEAQrHIEUdCsR!Dz$;fQ2a4C508uk~=$1d5A3;yK#7Iw=1nIw_NTyY4xh@cPzoew;I=Uu$cCD@RLs+WAh zoonQbq+Z4}KX&Mm-(&jI`N=)TJ@~un2%Sy67jNn8Z6Ds&u($nqd;PbFd;l*!#zXjP zLfRhV5xfoHLywVef&C79>%!ZG!^oduQVp17L|YXBh=!}&e{le))MeEE9gOZf?~ej% z3tvcs+J|l|5)}+9=nux9U8xI1E@tG&$Jpf3-chZ7!ahC><_(1P{i=TPVy2NkLQ5oU zN63z_?a_;|NhM@Q=Z@&b+m5TB-I?zkJJUokhM=Es*oUtB&B8kA0`h-1%9q&wUT`8Z6&oEl9Xs)s^iC zx0h_E#6zSQxpS1R5jA-0BNTek`tN;1UB&+};rfEgO6n^vZFD7&j!e5B9Y%L&;81%a z4RIS%AchD_EfhVAP!KF$y2eW6KAsSIj@B>NyRU#@8g5Bhp)EU#^Moo^7=~f)*uy>x zsK%jYomi)%M3qULTt7Bq5HJDwgD8f1XYN)qKrDP$P1pQT9yZ3`#S9+7D#XJJ0z;^V z#IZ6xOebqoO&d?!)W|JHRG;`0Pe&MziQ5#Wl~W!w0*BqLR>_s*tAgz{;+cQek#pQr zkV!|(UNM~Xb!r)Cn6EX~b5o}8Gc}~!km=u`qZ8t`SaCkv4%7B%YzzkF>ShBY?U1&I zfPw7+08Y4u7(7&0WJIQi$&;{Ndye7k+86Zpe5D z3qa?J#T~q=xzXS+)>Tw$5C5LB2iM8=LGxZ3zje!VDPueVLPvxHU}8TKn38_L?empH zerIXZF&A(LaThkbgJ{%)xn|V|;YV62gkxvdeIQ^8k8OO+UH7ew`vj!F>iYuQhGXE2(pBM9e8)+ifozYOLs`tVh<*HV;y?; zdT&afeE;^4#VfQsVYo(^+`MfWqEieE&GqB%_xjwO>$aP^?FMOJ9=@caF7BM;5xqF- z5ZU=^tdt{aIhGr4tXe$$&eV52f9eBvkp{P7G>?5N=WIl$nvBSFhgfJEY!S@TVgwl> z8VW?A&#$Ae0?0Ibv9B9vGvZh!+J$Ecm~m>g&nsYU*mk-QE0`XZC!0)#N6ioZ2DO4N zpvQ3q6b))0D{XP&zI5EXV$mda@+e*6skS01Z#3cJLv#z`R*P$Eqzv09#_?AQqO`FEI3kx;DP-^`J5-Wt#KzLiU^F4TjYNV;NU)Mdam_1i6#q{{*55-UGzMAsJ~#$h zbMek0>umzE20=r_IH(F}OYqd^ykIyr4x$=*DzuL<4aNXz8~`W*QUamAH?-AUh%pL8 z?`RwaqMl1x6OaPH;Y#4%l^iLY7Dyp{wCbP19P>ZggH_MhTJTO)?-_U;<3=N`yqb>hl z!)VJ1eqX6`-Q~IBaNXv)Qm+F0C}dqTD*I7bw?VY;xl-#&@?0r)@hIGG#lkpillB|L z3S^BlW(t1Tgy|Ew;cGtJdxcd>pecg;JjZcr;oBGjNTet$z-k)kx(=NLBV_Fc3-)#U|~KHCNu!F4kNjlF`UW z#^c~>j~`pR2XRN@_JE{E-f?TU!wX4>o%@Zb<>_MsPoi4m7#iZgAXmXCA0PSj1sEhi z>i@BBha>U}_#nCWI{dHz)LYI&~qtmY`U?1-GCN290UCCMJ-3ghF!PDk}YuI=ec8**GSsOexR)tLMmZ4?+a8de2vgi)88_@z)e zpdo=y{Sbfk>Z$SHpfe8A7LMPoL}S>$xs^Ts@~V%I5!)dj6z-w4N|i4epNj zwM1gCeUes*`7myi`P*%Ld3F!9V|fC=ZTutK1HV0Kiwpw=P|S9lBXU!;mO$ym_WQ2F za{!;9f45B8Va5W)fvCrq1PqN?)z~V(wD8>40Tf(#Zo75!B==2uo1^Gh%q(y)HrMcV z$-OXJHI5X^fjHHNiHTG0|itv_a;BB_Ydwy(?A%NvBF~obkpm z;2U(nR*CGJWIp)TE2z0Um5@D74u*ByWQdJSxPqYwPC06gTjGFF!7_Yd_-)Sx z2?zJYdieoAiTD`11mCP<-xBeSPC}=nk#NQbPAv+agl-u<8i~$2?}miK%8o|5z+&Qj zBgD3x37mP0os9ktPDaO3N{E$+*rg~UcrtpdI2y^qk4CcMK%_)=G!i=`$=MS6mD7>F zs|Ler?!1dcDekNG4RLPVdmCc@)#-B|9`hZ|IqQCJ`Z)~{Eaz-ctP9C%Bqq!7$jsgq z!<~KC2CuoY8JHR>yzYuvu|k`aNQALBRGYPxmdYrb`7fe_d+rKMgZKagft{+ZFzx-R zmbG$6P>1b~_C7Vrj@v9YUxsw`8GyH(4u5OkFMrvn|K+NI zT?FpxOwtNJjuUM?|I9Pb`0@@xmUuImDqrW5p7*C4j*>#*Oxyd@u}DCqfxc}qF7=$~ zEAek`?`xb6GAmoESOx{_xrMTig2rMh_xNs)<$Nv%tN+?k5|k zSI<1da&G&8@@+BR>^ar9m=yNur*Km0x%d6c+WUOjhEK7H*_RD2eEkeuI1rc$9dSCv zVxMFD>vgor`P21A=qxvcg8L$&bQx}m_r_ozx18}hA&0mE(fPT)WDt~>sS2%8jfQHf zdwOq9$7jDJO3u9XnRO~G)^LUSpHVFZ)tX&(ZTJtQN511HeZCyushMxUItSlB zLQNyo%ibJ_cFuh;0)1zz2}hV;-V7OZqaK%CKz4gu`l-Uxo6!?q*DmVNHM{zxrJez~ z{cP)>|Mq~5juyH+{RHn?ObHa-KH0qO0o3bn3|0N#8Y041&({r+FX{*er8qbr$MyLC zk`8aw=e)COfOd@)PTNnjhE>nJ^H=E1;H+tL_QB`=uTtfwc|E&OTYsVsIC+3GNE*J#3Y1Kygw-kLq$n%&--r@S@$y)}{iIHEjw3Ikz>>UmMU_JP&#AT4F& zRrjc~u!+&hQ@Vx%R^hHRmkzHik zcxY)LEtu)XUltq-{La&=;K)LruYW0wRra}#BKH?zyx+PPI*muXINSOyi1iw+t89(~ z8!hhEVdG}Qb;s~`(KlYl0cy4R!FLfOP~8|_z0`xFIogjviWhhV#pAl91hiiH!SQhO zD}(-J*{9pCR=_-cYZ&j=KLw00SHkagJ{5>4vYCH)I~v44eZmeC9&sK(Ip1~PB)Km8 z9bI7J4)`5?6=?ZX$+Y(Otq?{#$9s`i<; z;VLEE7UXko!yRCbeVwiQ44%$gJ$<2G=QhfLj!2=~XFGit4$%~^w;9$)`#$rx9s+|m z;ymoHIb=Tgop8EE09|$-c0$ZL=#=l1!^BVK49+|{^8_^dNXoU%X1bKbRj1hwqhFZzA8Do4`JK9lfaTKs17LhU#(H%=D6B$7|@$ zt!kW2?Nu!{wM&g)jXE_Dz@)D)Jf$X=3@9?C~$c=Aqp_{CDm#9}EMZ?5V?~_?J%K zcdz+#b%mdRO&KIM0f&9#?lc_UJuLyY(Z^r#!7sNn3xrWXXu*i4o^s zntWRyZ9A&(Kt0>fZuz#a^8PVBxlnHoL z>RP1OdW!xyc_?06OMFL%FPIMw8r~d&Efw8axS#gj;9^?mg)>bTJNu@e`8Wcq^z)tX zO+TYWbl!|RYcW>p8JoJ_Wvur&&+FLO`g-ea&hwkV@17?WnT}5Vb?YQG*VFpu2t=55 zZ=!{7Lk8@rds}*$>V)|3s-c)m@0J9j>qi{#25`OR+XP$QJVe7^UwC@`Oh`PA*W0k% z9KBHMVIa#n{SC30djv+@uLTNW0QzP`)gv@1eKdo*QGc)FM7_7_R3HiCiY=9ob$IG} zrLt6Xv)f1sJ#}4}(yw`1_etjVBCJZE<2Ca^XO~ay_EnvLjY#0e9>-%c;vAfp)J(*DGLcst5)+0@C z+NDlBH~hgaEyeRgJUw{c!88AYT}sBY8_zC0rFew=#WjZaAbv|+vC@xS9j${TV)H>$ zc!G_<%wA{;(Qm+b-Hy|*bvw@BIg95Up7VJA4bPi+{($EKp11J4{nu{CJ2wFZQCqyu zN9w^QhjcH}XF{R5NU!+R%V3`{Pt+T6mR36STTqAA<9wi9q=Jo-m3r^w?Thr?iwwH% zMbMiqC*zEPBol3};6rF`{~7gChrQlwID|Jh4)vE|S79FRhR=>I2E9MW7K2JEcyily zgmplbH=-7Tlb!eRa0oto7zFZw};)4$CW`5#9vbzZSR?V5Ak z{OW~z2)!7f9Dav^$^)2ZA9~dfyshuk>c>e=PhozvMjaxki8$J7uY(1*BiQ43>Q>)L z&wHVre>Jq@lAZ4(jNKaj6fVtj9(kDqNz?im-=M#>>zQW&*#RfJy^bU1gB7sX2O0u< z>vfW9*XR7u_i4^296Wl}L$6SW&pb8rO@D5<`QUZt1t>Fn_Ms5KLbK-=czK1x$?N8a zc9D<6$?Kaeprcd`H}GJS;lMe}1p<{27cUuhXhBffPoNTpWDHb(IPfdPLbmGUCBt?O zD#L8+WL36hxeg?&d;Kbu+8qoyI`{gjIt<6XRr>+4s{G5Mu+!7zbw1_=)O0*%J_t+Y zCjcCME3iSP*HgDg^2_14=NH;u0*-Xt1VG0g$-9^V#}E%zpr`2x0LT5F*8KpEKF5*8 zi$@%LN1V{Zbx2zuZtLOjab!#3Fe%ny5S9s%4&*y$>IQwM4IKcE5dbEaVLjdX;dDbs z#8Dv!cU-A&AK7|>{y2FMUI8O}2u5}ot_(Q_8I0UV=0#W=RhQ2+T_MQ$QN$<6zVrR* zX9glpltFBUaRQ8V>|-!;a2$*rBp5k35k`K>VC1K0=n#XEL!&VAa|R?r20)hU#tV zAwcbM5F7I3fjdxp%)Y{2b=ur9 z>a=;=Mg({rdvTXyaBe#G`kWnvxc32ZFZDYwc%6rUzWaPmkS194VV=4pK-njJE3f$z z!wcWWtbB`RaxKH}U*IAv=GA?(o!`D9x+pO8a@uyHgu5cbzi zN6baS*+<*D8P2B3I7A402$PWo;T4myn@`5U;AGrSlM#D{MknLP5m(1gMu@@qWaJ3@ zf5K#3{0)$h`NoOJ5ca1L>YlGo0QEzTu*oKe_MVO#eotK=5H{4CR-La3G}Qsv zvekR_u6a)w(4Sk=2`K3Wk?DA(yTqUyMN!*J^xY+LRyU5%8@Ln7YR^dA7DHp)T|QOo z{K(Sw(RC~Y4%0BKz+9l_`f$D|jF8UM^?{x;yaQ8I2i6P$ip~+|5t!(sD(VFc#=PNo z+dddUnk{Lx8;hrE(uit;B`Y!-rJ5*BA4c+me~He28!o;9VR`^oOAr5sO02?C=x4eQ zK>cS>uCKL^O2>qVlp=7Px8@t{{Ajx2Y{Z+sPJeC~s^yQItW2BiI<%IbbymxjVD4=>TSOgWDq(f!*M2Tz|JC0M6k%SV-U5Vfao_UTZWKP@vRZ zXj8+w4O!nQtm)YBDQI4DEr&Teta=1CTo}%y*H5_LkHUq8+bi_lI+(^_JRgSPLr%xgF^fxQ zA3A2j6|TMV&@q`@)6g;Oh4l1VhK>PbaNHb5Yctrw4a?Rvd zRJh^`Pq`urPpFfl=&;v7WE2vB!HJS&jL}J=JPnd75|Lr0poTm zrWmN`hK^cX#zO#=u#a5%TgrdvsL7>|I5u=trdY^p=qOI6WSI{g)rpks9?RjQtd9DK z9^Z*qSQSsXrWd|}ik(EoPEo~H^NQJd#T2k@D9Lcw%rkf1jw@79GZ$T@B1w8zc;Ok< z97UaY%+qT>7GlnQ0aPJesLfTHz2>5z` zOLJl7N!J|osDclPaP4p-agtX&BJuj468(RQ62D4wU}ld;v%3;AJ3ug!ILgjqV=&NK zWGxvmCgkFBkW0SPGaaVkvoqg}I1O44m(>pJ#}_Rgn79H2ltw0+)o2ZftIIiOw;iYU z+&0`P8g#;hSKgu%xbjLg>4YAyG}tt!*fx;%Q^9%V>zsLvrjmls7!EsY4lEwTaD>A} ziCA}U&V0i&V0TSx8xYMHpk`o7v1X7<%^+9rJL>DraYjZsB8NNC|7@vBk{k9Jqvkr+ zF$N2UX*hFfXl0K>yBWoMv%KqM?>ZA1RX?~GUFfs;R^cvd#(rVzF7eq#ll-w$BhKK~ z9^YpKrH@CP!Wd&KSluPb_ckmo495vhpY+uX8ioMF|KKl5LcTD%D8pT#0L6bLlHw9F zhuxev-&S+UH3d55F%{)Ii%ivl^AnD#h;z-z8-729U{Bj!a-n0&F5LLfR3!eH)76NXh(AKNckp;+jR(JJb20#f$PRzRz*7 zX6MJ2h&MXlx3oF5LEuVei(*@6a{b$HB{%GZSDA1?Z6i5Gj`-{xs%X>%}6WHwPMI&vb)M` z#sf#;vjb|FUw{sO?tgapMJmOYV3qJH#fP>L4IBxeV|jpfCki=+A^ZP-|HXRX^L?`P z^M8@0AK`fb&pmkVz;iR6B0O{OSn-(feDJ(14dK!MQkJ6e%)m1TPZ6G_c)o|{AMiYe z=UF_5@tnf*As!RTNyIY;Pd=UsJgf2Ci{~Hk?7*`d&kJ}CYuZY;mI{=OTR*WIn&xM+RLI@f(Smep=p zd*kx5VkJjebl<(Mx(%)y7uDXoreRI3tM10E>`dhrg#38c$A)g??$ul?49BSJK z!fFc&9P!F<-VxOg>2rheaIh$g7p&jH3`v5ng#DsPWc7n-GX?*aw%T0euwcs zzJ84qk2*|*2lK-ndta&_#oM01>xZt)3C5$#!pE2Use|`FFIYc4=2ftM z6fao6O?Vk!zbS~f3g78BKELh><&KX>af9Vbb2qHr;9Bph{T|+eZc7vM-@9?{_wTvy zd$sq>ZMe6=CCwf6OLJXy_q*oabz64+59-$6#h2z?%NI4QTU)ojqH+0MEN%nV`rnKb z%p`AICi6D9zW@$QrB1Xcfjv_`0uO z%KYmh1pDCYzKv!5w|enQMf^Px0kr>eg<}AIQOqc zQp4CfV1Wh+%7t-1gG2)Hv7ccSUh*&l_JEE586t6 z#IQ#&V3I|amIx26>KLRafZ^^j$jMi@J6pKf!p#%zG~uQSH&eJV!mSl+^5~4cG)pQQrtbEa9R3Gr9Ypkl>U5%BK%#BUVLAYB3I3b zM*I$U`OhVVFkWTKn13ex&T;-6_{)XAZH{u<{g5Qrq?wpoJu95KRVk6oO-6o;D87Z~ zNB-sTXOHvW4gc(Mep)Tb!e5yZ#qx1lLz!D18^&CRC4#xZ@k>s*f%%gyl5nRm_eqO| z`< ze~+Kazt7|C-!)|#|1R@>cr@P3<4xkz8cj?aZ6SUFSUR z&Mx9^Y9e=SH)H%l!{s_FPv2tY>Feep{Gm{}WhoDDTN(_X#maj!k@sI)5v4cDE%7|w zu6Q2Lwv6{@=CVmFe$6u8ujR{lzZNg!{n}Q>`=f>OSrR7eG7?z)h8f&7>bYAzpSzAE z?$%j(KR4NV|7P1|mM+H5`!me0XZ{6t-al1oJY7^4cPk^g+Z@hawSc>EiM$;NiJ~2| zrqOu!J7*c#xYy=RVg9;YzFr#U#xsA-+;H~Yx+6qc_S9r$&He->_P`BFO-2m+UYlWN z-)(XP^H@B@_NjPf^^vJcen*s2T`-Bo&r6xi z-13wtmafJc#{AjV8<@XINn-vMg{|AJ;(}-vUiRZjO707hO4dtB%KiH+O7R>ci&s5o zDo>YhW&WC2mQVleSe9>BRg#H?FGynFyPETvzstTmUP*f)Tq%0qtlaZbBF?xEW%1ff z2`s-DJ!@ybQP0}lRV8saR4uXkb=6E__3Ce)#M;#*9aa>}cAb*)e7v&mg+!$~jpfsC zjE`mcX2wUdd~4EJIbD@A*tqmp&0yoyRr)}L;@WqEl9`M75vr4oX*92lfKQko@K@%N ztCy=yeE!B*D4t%)FtK9A?j;_D(R67v=DYxO)|<9zb#6>GDyxCU3 zEBI{$&k({4-Hb^*BG_t*UYUMetXS^Alcjj`QCQf4T7MkU!xi z2g++hc?P_54ni-<5uBbP?%-vCmpg^~JK_FDxc@5LUkUd);XWhWr-l0y;XWzcUBZ1- zxPIY!h1)LNAYBloLxS`}ke&$ABSt}Q1nHDIk-k>AMm=BWzZd@3h1)7|dgHjB!%yA! z7`%m4*Nt(&$zl@5Wl3akoGwZ3>5Al@rv%3>f#?m4TT~<)C%0}VAGa_*j?7QvW*q0I zanlL^>mfO8emUj2%#G7$F*i)Qk-3x~jcd%f^fazfDZV!{JIp0jIlKiuk&3&*$~==JWdabGiS~T<%x1IDBfS_4<%WZYxq)evdAuaSxNd zMLfR0h{vz3=ih7Qa<@s)lT{hqUzsM-mjN!KUM(e@u4*f#_M7DP0-m2*!1Jpv;{91( z1h|d(bw#{i9Ywrfokap~Xamt)-iA#xP`6i?=I-;ve zX61C1hp=(!j|ySq)MWzQc+azOO59wIC#zCueoAsV(f`1kQFF=F%W+n|eiO1N9`I}y zpQlcl(*;qom4#Q6D{zS2Kmv`NmjIN9pG9^^!+?5aKzA&p>)(BA?&3Ntmzjr%vMV z#3AURJetplUlq&e|Lk1AANZ4VvEC3ruZYL5CAtmaws;P2l1+U6XVQ8>yqIXTAM0m! z2Khm^+{)>e>I|eq__QR@cL<-I%hNx1p3^Ov^8j~{e%d^~?&^rofWJYZaGji~@bt9` zrPs^Ff*<*!iqkEXX&l~xK4#^-o-gQ@)iR?$e*ZqFTXwr8%YmJeW#^A2i|aATvihfz zWzQ3m<;XV4^2h^{<^HE6%bJHJ%iaeiOUDk$^4O0gi~AADvUay**#{cu(6f@|3D7*- zwo8_K9+xctIv3v|r}*M-PVYPgnh1xaTAI^%xS>zS$~P|5Gx|-33!L0N8U4U>a8vqq zkviGEBpH6V4=M2L;gTP2Pb&Nd_*43WcwM&>{vM^jPT-xod3?O<=EC6Z zAGP!Nb@lvv&0Ov_&*5%W26rpdxVvj9pBHVVSP!UI`vg4JBJfzXUEm!%UuO+=-meb9 z*E;RIUtg~aW`xV zcbj6lyC8|XU%@;19Pi}ibG%a>&+$%XdrZT|=HPID0NfL{*c9ryEz z|3i3Z-Lr&uIK2K(@yJF^MiAby^mHa3hdX0<2kpoD4&t3KCS3se z;e2nTEZ{x41n-Rk-os_^K19HKxD4KhGI+1s2^VnQaDDoNU~i-^Zt`UHh8C|eNKzK@ z;X!;50Je;4FegM92nzBS1I{NK&5PUKUMi~0unQr#K`-@nX{M#u?B=&)nBv%YnQ?k;^YxnQP24u<+uJ^ota}pf8KLwvHPyPKsRFHLj}Cy zGVq2OXC+JCtsMW?&fx2`mgG&C7eW58Axp>nbplT}2|QhE<#@D>Aq$$pH;!!m55^Fhr;T>cj1$Gieh z`vsnUG>E5#{C1Q-6nxkye<=7%e?G_4LH@8#@PW-j{$45gMW^5sA1&kSs=9>Z$x;4L z@S8zAU6aB4(~-g9M?(hh*YXTb|5Ru2epL$qhtSS8#>a-r$%6lDv8J%_Ab)5Sd}EBn z!*%m{x~2k7x8w=FJ6p)f;tE1o`UM5N-)xDzKNI4)J3E@Y#Ub3Sn!{b)Tu#4)&E@?P zCGhk#fu}PCe^?{4|D+Qj;7W`qV;13sASbh47Ev$ZB<+C_{=$ys*L*=PhCI5*q<({8f zlx+ zAKFMhi}(pMh|UCG7|r?8yaGPYV}zVHH5&5~+{NQfzg5VFg?Eo+6244xja5Z?u zr@$|62Ykk-CDYs^nt|Kf3e($X&&=bsF}ZJhrG_$$Zxr^4?T z=bsLLcF-^Ib%VfX4M91i(8qw!Wc}JC;6M1sEP_i70-rIyb4jSI3VhZo@L5wXpPvl^ zpS1`$+q#g?Gj%@Z6UM7a;Io!Ufgcv~`PEv$$GK@1r^D1DKHpk|Uee3;>HYqNygrW# zK6IDBZ*93e-6-E#l+4N-M{%kne00{Ai5C!{Y?M+OFqtNT=uY zbhcjb?fMXwK2Oj4&6ddfdv-i`Go!h?K*$fEuVnF!6Y*IQgOmLUMFuwRRSP-aS+g*N z`CIZi-`SiW&it_t5Izf0tk2&7{FA6e**QL|v2%PDCghz}Lf+Yoy-|d})RIJ&zLo21 z`xz8v2Ic;3af%B(o#~aSN{zztS=9u5)+B|od~_1WXUzhiRSSGptVFZ?H$Oi~ z0qv*UdMIAm{sQ#00-u!&eAdkMu>I8&@!1rHzo0+n@=nNyS$=oy27cTZtK9Qcl(O@X zS;_lP@R?2Evjqa56$^Y;9>ixqj#YAj&(;AyK7za##D6_LD~=CkX!JY#^|o8z}slKTLT7EclQESKWx<@Q{_8?47MdT4!Kk!gA;=y%H;^|Wh$UhFB3HT60bQ|Jj3j8*0 zG5LX?77Ki~SmaB14DnJe9G)aca(tE$PT`0j&f~?+=lCp!^t*^Zt&FdW1}1+6J`?jl zE{MNqy&ztU(BFp5B|q?);5%ywzoDOub2uLvH6P#M5AvN2ME4?nn7~`rbBON+-%0Ww z%17e#aDznYfzRytj`Tskvs&nptAw8C^%8;4>~lHa3H(6#>~)Fb2Oa$1g1>y6KOO$! zasC_OUog&}1HWyY|J(3aj`J77Px@5CN0k3f@Mpt6QNPNp#b8Z3Ue78=nPAZgssL%8 z5jx2)mb*ER+ac{nuw>|vcw27KXAv&Z3Eg8*_h-CC$W+}5(|reZfK0}^1hUjbNrtYn zA7u=JW$p{|(vQB4e0yffK_1jAa9g`@{lX3EaDzN_od^%=WLt&*IpGF*?;7C`>TuhH z|7qa{dG#jY59)UJU6EqChNPI)m!+7spGq-L3`#NkKaygK!CO*&bt^L19MDN=!kxq1 zFzH*&HOidFh>~@@zshE@@Kim~60n&Ny05tToCY!q*`n^Y8(F-V5RO~p!Z~jlrN^42 zIs84&lg5~^mf@c!Y`xSM?|ck^!MZV)FHC!tPNh)JfsYpZIu;Qo1u-$#`*!Vtpx0nZ1zX-j;!RdS(Y%s(8htUX{*YPs!FeWWHY?d)HW#M<4}G(p$c ztY`J=SM{8il=sIfkq?@c)h`*DKD2Tc$GuInIIeYaU1NWo%y40Um>kL4rCQkd^tV{p zcy%Rn-6V96dS%_-sfr6SZsl2%5+``A$_(i=!%+-3=;ZP_95*!)kA-=w!^Rt$k1q=z ztz71^kzI43Bg6MA~~mJc{3OxRS#(K<%DsCgVWR|ah^g^X1vWJt~#C=cnYgw7(Y z0Js(YC_Uyg@=MlZp3{1WHZwOSmt62}@jP8-G<}DS67h&gH_H1(^L(Zy@pY0MiFJy2 znUPqx$j>pCa0+abNEb=vkj@h6>*msT;=Q@-Cp8k~Av|F&&nGhx?Sy|p7T^TZ8`p5& z(k6Iz2l3dHui!yyrcgYcoM%Njlz$d|*U88GInQa!=WsAv*mgJ+p8pqhoTqG9Dls0e zcs|Fy`@#D?aS=S=KFQMcJov-C;3L8Btpjg(&o=Oo;0Jeur*wliynhGehu|@f>;!)a zesMGSNEi6aHQ+xFJOLgOJm*gEq1(WBdS~!*3>A8Y4{Q47qP~h;`+h2OtNV#IP~@r) z;+R{h(fBK}^FyL_6uIsq!A(UjewXG4WTOJ_G^A0uBsb*p@fzdlINWO%vPm_U!Js=-3_=I>UCMLk#kEK^@{Q!F$!s<8Zf0=nh--++P*R zU89w|ZKWK3XP0q&$ap#EB!j$VtKcpDg4a#3^7YpsY>O*}EnT*i_iM7?$?}Mo1CL)U zWTC2%jmDT1)=%w|xEqzo-Bg;#)PEMrB z++seSPNKDuUyY!l+h%b8G?~U1sRU0KCwRK#0*+Tw3qrDhAG$JyF3}-$iPa?>k2I8U zJYw2rRMH*^QxxEv6zB!37jnF!F64M6PS~(y3L6%O;OQC!PuG;j@keDE#~-WqO;cib zKxX;#DN4mlu}YJjw8$SNCztWvcmQFwogRnNXp#3NbArKV+-mYik8ws4{W<5@X`{rAA@( z=`ZFs!d*=QzqAVc;uLs9eqL7Ag7{Rls?lxB?L`pYQ~Du6zz>qGEacx@e9+jL|&a>?Eucq)$k~d`A3fb`EdqEaV6N z2-@%yZ-{m!kWLZtlH+;2%qipt50=95N5T{yuWkm%dzk`nmG9wrA}Ual(OWJ|s-i`H#}faBf*njhF>AmDhLfa9u==`{#A-de=rOS_%( z&@IH1qu*3v>(Le~;9?2-4dJatK{yWjg5n7qkoFRexBWsU_o#rIyM#>c$x)dc#|!?T ztp>@eki#NF0O+>J`)ZfXI? z52}FUHUY;g1sso?0LN=4!0{m5t`%_G5ro@9wwNmLTM&LH3-~>|gv0MlvDc`2f=n*) z;V{Lt&7`C}7NIEn5=Z%hYQYy|3LR6N&@nX^$TXk&)dJ2Jv`mo6r9Bp}ta{w2#Db2` z!2XHm85~|W3BCaMkj+OmF^uKYl6V92w{o}$_{iZl))NbNzl41gfY+t3Sd?vtq7~a* zBTH8^cPjg?W^ultd4erS@dUUHT`Y@#&r>(R1|&`?+HF=g9|rsvd_l9|3yLSe?Mlw4 zK(@#5N*8R=dHJJ!L84;cErXVa-6i0Em4N>f;dM=rPY`gsQo!xV?IDVb(vb|A;MXk7 z_fVr;8G`u;e@q6sunAO%&XZ(kF2&Q!ExCL?6^C%R7!$(b42}oNqVdrQd{CRgWnZw} zWcgJKoljH_>m_Us@(ov?*++Cx~pst^jt-i^`xDZL(Woaa{+&g&BuO>h@*JW|wS zA^EX)VjOds|F_f1Kf>9xoBT%5{I7&NgVD^o5JHUz8?8v zZw!z36*$hK8czjtiNbgYX3E-&7>vxO)nR<0%~fmy5lCK{(!!5|Tx5xt!aIUn);|0QuYH z@)V5Ak`TCT9b78gGg~R!Ger^pu5!`|J(MCh@x6SzoA+>A{9>ky>|lP90oIK3lT5F6 zoS$?~)#Loj;jbF!zZ?Ea;V(!28{ijv33mtih-QI*n)5k6#@-~>epS$`Edsu`3i#eE z@QMn7pf$b!^<4 z=JW5(bGX~8=WbObca2u={^Vwk54~krw`h-l0zPXO_)IMo_@|Vw>&j9Ci=SP}`!%nW z&(CB#&$o@?F%xW*xg4;47I%wH+^w0!-Izq~rV5+>mI?UBDe%wi3HYaa0{)2;e8X4p zj~M4vG0tHNr&0g(CoeRxe##X1M-}*|HI2)qD$~N*_fh=w_%ua%C00@Aa(<#_u9>Hg z<@`fsEax9ug)L%>utjX+d<5iHGK-&f_y(oo@mR(6N|KV%0UL9He`+S+pB90CY(e}Z zjEdu}4OyC=WHV#!{?_Lo96v5U%7kljT zpNdnq9RdE~c%i?U1kK6ogoRW}+c?EbE7moQ3*BQh=w9WzdY7`Ei6KLMU z-!h-$fie8U>EW;7pEQv!oWs-V#e$Ci3jPuC?1X1v8~7Fc!{PKOA5jp*KQmZ;!h}vI zM%Xwsl?ePJ=(w-qAJmuN^+f!W2)Kdr6A}gfDHZW(eIWds@ehy3@C)LN@)1eAT?>Nv zN9^GZ@)1o_0Cx!g2>Fz42G%+Js+GbC|A_K~_(#CGQ9dG{!xzU45pNX#e2d|qI_PLT zfZt!_z?P8q=2>R3aMF+9=_7jr+7oBMLuZCe)Yn)LM*13h9&k(24m^@ua{J&S%-x3f zFbYTTc071@<75K*?)Jg&#Ww>UJs$j*!tsZ`Q#v;VNFjLWY?;xYD9(rgnFp?>Gh2ec z`>}D49%J0tcRDj={Lczc$Txid-`RWH7`=|`Oz`by^P_1}Zqg)8QlxxIlNwTPw)%eG z=8y_0iMHsW63G&iFeaC7lCQ*$+5BjBJK|(DLnc;i2X+t*;vg}cff8hb#PAv(!)thq zY@k^%<76;i1dIIGAF~T%z+a1C5eycC`Qyc&bDmT8R@J#L+h4Q52Ba-s-nUMDSDiZN zsi*2L)7>ik-&(pn#ih@8n)rYC9+Y(>!oN;;pYVTc-`B=G z7yjuymnyk7{M7Ionkq?-ygoa5==tHLmDi)+>FCIzj}33$SXxfzuY79Z_Kl^5;q9MJ z1JArh!+J8E+`nq)jvhM2Lx#@EmHF3(+05%VSN2l}Ah(QjJO9$sjn|jv7n2WD0dAjv zapCgPjoUX~TTXs{;r7b>wd9rMl`9K3$OD}{|MSnCKmD`MojLpbsq>eYmahz7yFGKY zOD&hwWIirjzkF@w%8;d=zB2sO!sX$`CAuqYVR7h3C~SnQH*POqq=Xl)4}-KzD=PDN z;rhkpJ2ykwYI6VOqvtPEh0nWkXc>%X-6OiRKuXD`+E`J(R%m5uXl3WD?5Bp8mv8hW zo6cI=5L1) z(3L;dz=%6JrCxsRDD}i^H_qRtd*NtI@V#*7uhPA6=jnzdx)+XaK;nDh&R=%-!ky=P z;g)bO-1$$+y>R+iCZ%{D_ZGbx_j^i%lt$#D|>h>8hCwqA%6D~F}|T>CrX$*-CFmEYXIDPE4$ z_+ORg;zA{!W*j8!vT=o~+YIyeUs?MvmR3nuPI`Lj21S#!5#!B&EJohUkoxORVlP8W z4pC!H&WHj{W>)enxpJEiXIEZ(ZMZc3qei^3vW%E)IQ%O2A>E}( zd8SozaOvu?{23~$`;E$RaOqe!S9m|(q#N;)gEx=qZ`W>Io+Q;*G_#Yln>QXf^X#)} zPl4I$TD^n1%h8X8l`C1U zh~z3OSF>Cd$u(APj^&z2j)sCMU`myx8n@-vBBnicNu@GrT${9~I;qqrjVqM))F_oI zrE#6oo=T-st2C}w)>Exas+Gmn%6h7mp^xHfWj)o(q*_^At*ob7nN%x_t3~t8bOFsu z>Vhn;7HJ?OnoG6vxLSEnwQ{Le9#<>xsa7u4%HwL0nz99uYUOdYNVi#0sa76WtLUj# zA=N74Y85@zDx_LPT&<#~S_LLmT&<#~T7^`rh^tlfRI89`6>+u7o@$j+t+K6_>V(p- z7ArPV30GP4^78z`m6#x34ZaeN|HBAtKE(f8|OW zf_XTdG$=2ndhJFVNDV>JC~_rjbnOZ>d>c-U^6?*5(O=+FU;flGM{WyE&3ACQtx~5I zOAHN)a^MWP`4`J(ZkBX#^7PZkuHCqC^O@uf{heI8apg{OjhvmNXs9UU^ei)aaAkON z`RdVxnqq-$d2vv0AEhc#{!}@r`?*qdGq;g8dPV(1m8GW7Q>PYP5G9rKZ)9Q5T!Mz2_-0NZYj!6@5a^jqM{!CILS00nw638#*HP{lr!{`s<*J#(m^FB=jVm=DB{7Ty44YG-pQ}h(>5>a735W>naN8! zPAcx_B|TP4ch*u6zj%_D^rOv-=asZoT{R{zX=`aFKQC#$TE;-#svfNu!X%2xtD(k6 zUg>OB#F;hHP^pY>FSkb8mZOpl5ziJF42?9d&5qh%v&$Nc1g}vVX{%Ivz|T&aw~?-E zT5YYBgVE7Q``YZN3pBejH$#M#2AMYT7-^)18|l49I@K#83>Roin_WA|(|XQ~k~wLw zG#(~%(#DN+(p1(qg6gSz5V>XWE>!auHW}PTHY} z$M2l9T_IiNHP4*1U1746+U0SU>8vzK9_N|PN}J?yrZhyk$MQJWbXIC=6n8o+Rn2U^ zQrFDpE0xV`zEa!F<}20B;_9ZeQs30%%LcX^abByXYGj-A*~%t)lRgH$O&+=?QlTlv zY+R{$jjgJ(dDt3D!_KU+^ozg7X1vYdhUr`?qtu(nDVz5PmoRjwFC*QYs-=&{z)BFr zmc~wJF>B2{O4a&DCMmptwlJ77esb&;aNFubnkNW{?o+W@QPn{>`UZ{!jo7$0a2rzg zD5%a7y)8{{nufgz5>tIEN-oGu25btcbk-JDS~i9MLDR|<293s!2<0Nt+rkvKFxt`> z#YP{ea7Y**;1--UrDe~A0w6Xg!_=<~oD@b-xG;gaGS`^Guow@L;T>6;0 zUHW!%vAfllelAKL1I}+{cw#C0#I}{$AEUH5(6loAvQ*>4Kl59e9W;uoVo=C_xWgfI za0L^CrkQOw^qM9MRHSQ%%E+_V$N3|DD3MMcrnz4pc!aHd)Q@&@u)or#A1WErY*HM* zo#7>>JOOPx!%LJv)6OMkiN-I@j4#ngDu#tJs2666iUId&+ktjw7$^(m(O{4d&7YJu zWQ;hvMU#hm?2@U^M(92q!aFozq1{cR~W_W%n&u!bx?D=(b;;3TFh2F2ag%MLfVY)*bwG<7F$hg*ei(e$DCJ9(>?*z2RZ zLLLi2D^B|;aW}#fbmGCnKLmbzb*vGsFLrK-0`MxT6K3l3{NIM9} zY)~;QNnO3{aH}ypRDM=;eJ5{+8~nJ`$;&Y4rS8k^bJiL@qJ(%qNTC2Vx2TF;(wN|YrjSdg7%Lk(} zs1UlZq*ep|8swvzi~_}G^YTg6VQdh;32Ztlwi%%CBvW1lGO>1pj8tb)Al4B-Cpbo_ zCeVZvv*^GphsLXK$$AZjv7eK+j|7VD@^YF(DKvhhX$xoozwPF*;PAwTl@VlwdBuV7 ziM)(b-5t;bU}?3zRALDTa)JrbB3eO4=m8D`EbeCnk2Re`{Lt7htTw*UHKoc50i|nGx zsD@0PKGs}t?1Fq${c)h_B)HI;Ko;rV$|#3SUH6(QCUeElNPR^P!`kcBD1+Oo@w3P@ ztc`NW)VF8L8vPLDgihi>=|C?djBAaaMQeO*R71wwZ?sDC+A0fAkPW(z!?0=P=OeAE z(KDcA?R>3`X2{g<=CufE1sP$EaG(^z%g2E(4+5)R~`?`u!XTMd;^*HrE6)MDB93ngJj|TJoIanG$4# zp}=97VqQiW0D%ngGmN7)qdBV>6d+*t3{xe@3G-C~Z7)eNGTq_Cy4mGqH3RlHQ#NJL z6G2YsLJpKJ^r{3HGSlr*DC905s~HrFU<+Nem7k5`5F7@B!plbig-kbB(X=ijQh@Pd zo+)a<+k4nH4syb>$bm2iy__;Y51K%R9oIIe6is*i# zv7TKDL@xz$K*IhiLGxH@6y$^cR-NhRL;b>rHM$&|xn4dBD7N(q+ zmMQ+AI@*FDs=Hi;12c#ej>$+t6YT=8Mv9MU(_kYxs1YWa))@G8*F~7He$%lTN-czh z{2Bo$aZQ75=pZ9R)EWanZ!^m5G*TdiJqXmwA9V_Qa3F@HKk5LRWJn+bfFD#rIynPo z5>!EY3r1sx{eYkbFjQb=W!P)(YG6kh_QPRW`q?Nng7r4Tc0`a5ElOmHXf?1&-sNKl znL;OUVuOs(lJ8?`rOQe~w~UN0NBC!qBOgwkbX zhnQk10Cj?lkb`R&ndU)8m~F1UftNx9(@Yv(S!gU{t1HMz6T-5Ijolz4jVSG#m4RDJ zO3Y?N+L1B>JmY7iaEuzC7Wto*8ms$7r2Ig!<#s42TmyU}$Vi6*6dU&KgBrmef>oPU zvKF^}q#mn3hZM_!og8F@!2l09vp^22>P#QdE4qyAU{J&byQ)D(Vbj>b0L6w4vaAXR zLtzJlB0|7wf{b)9U{+S5^Mj_tC{Qc73I{`B2ZKUG*sAa|0tf`ZG^<4A2l=S_k+GD4 z8PK+zdd7Yi0pzju0X~HN%B&WTD#%9asT-6TAfhfCs}_J2!3PX#BLl%X2X(9!P))6D z9Uxw$dd&=C&x3TL8l2wxrU;WuC>{o=rmJi?2ueE$fIpEl7#kn}n$$$A<1qk7+S#O_ zA+&9#j(5;C(uZ)+{Wb$&q<&5vj{seGR9Q5kGAOMw078XrrXUv?uZmov zwCk#3yC2vSK#d?1bibPEjp=>~)|!UERi*%f-(UcqsDa~t0a?L$?;u0?l@TT2sv35G z1qX|`48nzW+^&qVE}~A%WtOl5J5wZxF=hNyv@88B#k@USzBzN9cxu9%x+$|47*Hvr z^u)oNX>w_4KA8m7i?^YvdL7m4rFl9pbmO&ziC;10>*{d+N^))f(vZ)zbyP9(puF96 zw?iu`$@^V5-C8xm98`x$lzy59R)ccWpBc)w!}gemO(tZodZocB8{ zWT-FiXEmy%g}tABx`_8P)@j)J-p}fyVLkSK*KJLxX76|GqG7&zKkFV^M-levROBo0 zVD-e^9<>ZZ?{{@>0B(7|!^njh6NSO6 z6cj_h1oQR87R2c27h<9vgrG=z^J0b^ghaRYrzF%iNC95yO^WyovznHe10&L6A{o*G z;Mt!R^S@XsMAS?5!iR4xEK`RcIP?5346k1qap2%h(t$l^Euxbcqs-wm^RBBJZoroQ!JX@HYtc`s2iv7n8_+ZS!1;M}xPzvP z=TZZ30fpw#;SQXMVi4rf;f`RKz+3uJ4tFS2>KTg4n2FrzM~6EU0TlenYcASf5DQ$; z$>4YFo5Xm89w@-xc_1%{_dkTg9kkL`Rgpb(rR9CXXr(3K;?e02arGaa?&ywd>bZ?a zr#omX^U>)JLbw9@2W@G}u9e4obf+^IZngwsY-2w<-9daLX*kcuOdp-@;HwW(phlNN=rrDHE;N->g@8mH){bqi2xS_}oxy{rl)U&F=DaARn$(!RcCGhJ~ z9BuOoo9hk2dgT(Fk|11KR>kavskz=LjEj`QJEZ2!mR{}*esP9rOPZ%_keg7_S}|$q zAY7U@2XhUF{eoaQ1Crwy{Aw4b=6X}=s0&kb_^7RzDJ(iz#c`fcu{-7LUm<;V;Ha8RII4P;p zt6!L!YL<2E1(G_8!|kuE-N zrSp3iCzW&k<_QMA7p_;o zG1ps-q2Jtarr~PF5AF<7`S8$T&xNTu4%@oqp&q;Bt+oZxt(6bEQ4ci+)kC+S_@Vlw zTfn9C9z!h0WyvzPdzx9assx&5#)UpEJ}#K|%Yif7xr*UPcMy(cK(7aa(&^g44mX?@ z&Q*TZ3sZC4zSGJFODx?@YPu1JomC#AqAiZSFf|8x&WVSq+Y3{3oOzOU-!?PuD3(Cm z%;*Bd&vECE-;B7+CkGfz6CxhXI3u6yl|QReYH|?YocyYknjDw%wDPicbnS*aA%gOX zWu)@a#f_O&O1O&0iHE7%3sZ9?p7Ba#n^|w$;Xu>OY|hi?4G=%ag*$CE?1ia0h;gzu z8A?qK7Mui1iM_QJR+Eb!-ls!%e4FVBf?Uoz#$1@1jt7pCSo?1Vr`$*U8rR2Luj;Iw^Y zFHFsGd@AROZ7xjBaS4tDj=C__C4if7+MMi#sW~pr$@ywB(FER2VL5KWkuXE4$#E5q zi;uf-x{S=ud5*ho#Dp>zrskl*NuZR%Yq}iQ*SPrMbvJbRp&55z*5ynbHW#MmpwS^P z9^iV}R;~%)rkb`&mQs`BVw=3ZZ!>5+Rep53$y}J4!(F=EBq*cPS$!D+x3g(m>g3^yn^Sdtqu0 zXhqgmIZp3aNHr&~yCW`4b%>DCqW*iyz&jY%fgB zfdhywA}e!HAsQgGcUhPKQiMir6z~#FUF<0Fg1tYFM*>jOmzuF{*Qjb%%4U8 z339fQaid2^AIw!lbC|g>wE!THYy8NeFK`&AM^vO;n zK%#6DwVFWz4X;XtA`WcImcUiwUxcn%&xNT4NO|G`8cIz8bV34Q4th1B3q8JtOqM~uj>4T)h8euR5HIk}RdMp^!NZLLJs8ZnTt3i#z8b>4{ zEagE)Fl~`gkdgK^9+*Z!M%w1`D8fsd>B8f9;VTqKVGjbe^2fKrCLCxkOf3MLXaWTk zm|q28A|w{K6~G594F#5_pa!xJrB+q}Rqqr5-Q{LRGawT~sVM-BsA*F`M+Er@BG*VmxMV>_L4Bk9neBzCg<>Qi$VQS&7M1v3FsR2EAh*g2aOh(Y-aK8#%;M`?ohl1M?gfZx6q)mgh%zniRRn;Tc z1uk&zGP3%{eQ?SsHbrP$)5LgKNU14s?{b%s9SjQm;8I$@k8pFddZq<#dJg&siVdrA z0g7Lnk-adrP(%oKKm-}-V8E=zP0v9_qS4d}E{b5?rdylMbk)nA&4sB2Kp1@3tc z@=^68h8_v?Sm`Qc$AaGSXS4$FAs9}Dy%yT-v}kohwNTiRpu|C=6%b5NLTp;1k( zY;-BKy)d;<;K|%=Tqwc>kCeos8Kj#~GWtn2Xfu0ZYN7ZOE+h2^0q*Nl6V2QS4U<@H z(jvMN+FqDi0EdDd9^@j^9cDfW#dU!)fTvpE+rjMsuotElK&Z@xsRiaH91{|XbCL1o zY8rNbX}ce$m$@*t0BY4XNvsD@N3CfHT=iU-S^#*W)VQ#Iiz#TOZDKBhMkTeXA#9b6 zxG=R~W&6+zQ$@mUw|nNUZt{{k5FOxdj|4B^rl=!`v=^r4-Q|(G5A6@oQcKvs=g-$y zuDMl%#ztJm2ps~B{QD$3zjLD6lzMR`UvB%leAApxD&G{1uS*zM zpLEk_5p-4)E&HVc5FXLs(S5ZrOyS-54WkCD3`y^IXT7u?bsP5)=}H!hJ0B_*7X|kJ ztNUt$K}p4rYXqweV0a))D?%&_^8gpb2eQ=Yv8?n%0qi@FrS&qFg>db{Hi(5WwF@BM zfpAanslVCXE~ZSef>CXgvQ|*cyCCJB{=>!31z_<&xTgcL1zvPtm2ZpD(}VEA3gF;= z%DmFXWriPBz-kJFdwLMQP|h+PJR00eKg&d*N*^2KRCoYUHyZ+-u6YjSU9_B6M{( zn{jlbqt8<$2${VdvMh_arspnx_+297Y)h{}L`Rmee@((AW7C>&Rme@AUR3$sJ0;0w z^8CpXp0UKGP~h5rGzK6^sJAL5BNmz?UAJwsl$MTNixR{)iOgxo3%X2p5*m>u1-mTi zQf?OViwl%1_A^R=PbDzy(s0W~>61`@mW)ShmQ0|HvvjC5|L(UesB?7ryELJ>*&fk| zY&#-T>TAuEb|h@3)bfN3OTPq}N|Tph=F$D+QDNQh(&lyqF+S`vC8IlvT!Li>TRF&7 z#(fENnWUn4u+U0?!9AIF^PwPTCW>6@XZzw&gy~;`m`6fUH2>2H&O0u3L&zI1@WjOV z@PEZTGe1k41HCJy^c}L@e-z|c1CGfq9t}bG!v5Mx#T8|WsaBn$qy(t^{|4{ z$Y5|{hB;osUM<}Qhz!0UKGX@sLQ^77T3-`<|bG(EN>^=@>)iZdVt(d6ipfph55HmDVIs(D9EMYva#vG^Hv!zW&F~>{L zG+Qz0_Mk9W79nPs<0UZuQa@=UvAqrO5~g*@3|&K;D*@X}pme{7pOrAPU3_yYzXS=> zWp9x95|+0UlQq2^%!Z;_0&Z97)tTcZOm!!}Ds#L9&fdz)YVV5E!yGSR-K%`WoUoTM zmcZ9#l-gE?@h^dSZ{ZrGDxL$)?fwlr3|- z1UT-}59tY{$;6NF%2R@6*~&|OyYiUJ7)!w7DjzBtDqZWQsA*pU29ycd)6P0kXm*-* z)(h)3zB$!H=&np@6$1g!uNR; z?COdG_bw=@^q6JFD%%_G+?9ix~@>JSpu$M8GAV@m-kUEsw%NR>g zE?fD)l1Y^`;deV|kt)9`bDZ!CR8MLdG4(8Syabn0RHbcZkSQh5G&8nKHGUa!DJ^rn z1Q}A+Cc_-3uaRL+(bSUm@YY&&qBMHMrL@d(a-bwB)-i@TUTR_-D89RwPpLaz8b4A? zh50&n3GhZ10mB?G0q{uRDCW3JAesu6IbJH%!+Omy$4mHdqV(YPdUe94wTmCEAeK2^ z!gw!vVjJdoi4AHA9K{@W2}GmRGRI3T8I#0C&v=xn3iwjVVTL(gf!wI^N!0fPQr*Dz zOuPz&Nim@ebG%YuiR~*Xh1YZym@XQhB*ToLZG()U-gu2S%<)R$BngZMxLV>Bh%F=< z!yK=G8)^J1 zWI3jmIbH$V5zE>z$19xdltAe~Z`eU^Xmk<`DrA}Cm10Z0wu-D@A)r#S?v7xNYxD}x zRJ+Jo=6D6?zT~yYFvlyn_7VtkO@}utN~7^fGN_Pcj#t0|#1;{>DyWbyZCnWHKCj`F z5T@~~kn`Ac8Dj;6K#WVn9IxQ_OCW}?pA!a=CP1QLm8@n^EP<_LseVu;%vTOG%<&5Q zU=`w5P+x5)S>|}9I0Cz802%z86hx3f+e_4>2bVN~3Wh+Jlhq80IIt;$o(OW1#Kg{# zF7(=Gx39 zZ0c74Da1Wg+HyC*D?ksL0Ezmep@%tM0gIq$Ea(x0Dqs~#q$V*A_w7J6k)+ln9Oh%l zdzBs-#Xt!1p-yAMHoDx>5upcNy&^hdYS?=fun_e}f{bLkIvs65kdQfEMRY_xvg^@$ zE2SeE=6L1s5p5b#4PMJ>RofZ|6E;gRVS|j+!-J0LxTzI@lD282uY-)VY2`uEK_6M> zc%?uJdk}^>UMXzCfrdF=0XESDNR)H(s{l-d#5ln36o4DBgDcPsgBnn7qzGVTRiF>H z^TsmAD`gwnY@{!oX#<{NkdIjDt{R}3b@|vq2I#~x$17kY#PybPgKFqygo?WAhLh7W z$LT}hGB*r!yu!9~+b+_xUN#O*RSPS;3nH`3@d{W9r5E=PPzcvB;2Q=Rxe`i!K_hG% z&N9a<#b$I1+8fINVcfC_#$k{VCIQ^M3e58^Bdc$K(kOcEkGS^Z4k)qMa6v|-z{wwn z`$gYk4<=1+hXT&gl3`(*PD-r%MHP5OK}I?zp(es}w3&LW{u~09L+P1}po(47*uenB zryfLjj5gEH9r_t5@M9>$70%GQrUCCLsFCP!SL5&)ZDyI{6%ZkEZQ>dM`q3>r665e7 zBWN_Wg26m$2ZLpfSMJLcqL=)^01yb?A4!>fRQ*T;R^TCZ`Pi`lkO@d|TO9Z`T&elDT{TusC7x6JVh*pp$7S3s@WCW-X`>gZ}3 zcE27y_q70kCrT~u7hSz{ZJCof^~weW8E`E8u*R6B6|(y z_FQEkdB6XICoeli;z%~^n6`{NSrUZfr+g=F8Qof|Sdq|KTG7mI%p>MaQCJd>>}Kc) zqTpBls84y&@0n3GjV#uum3|uKx zEV+fOzzX252+G_gmebAorTN7!d0Iz)xKDB4SXq8{<+ay_OTvWm{QF85J6(*hH%x~i zUcYwZ()=~#M0$tB;+r6QB#0m=*0>&g=uL$weGq~^>K7ss9)tiQ^a^$NP6DLq6=Iqj zQWC?xzh-pVg$l_G>lb1g8K)E~Y{HpkTi2VF(72o(2(NT_d-kDJ9znGuDh~~)AA!nK z1A!k!<*DHvj-v9^fZUbJL&7}rz4fXF>aLlPv_CT}m8XUoO&i{5_}IIzftx!qL*=P~ zy{lN-QyfvBDi-dMSSnAg8aGPq*uoY@J2kMFIzG+Z(w6hbFsThQd8CxqW zGB&1aAl)^vbLd%b51|IwUH4v_SHnb*Ksel;K~n>~cJToP`^5qv?Xow-v>L>;T5;>Z z%9}HL<~2}fm0q37Qv-;0@~cvLYOMLKd@#3qs5~_;Csg@WsXR3hX&I$vx3~uUEP@KAZtLxt~ddd)I;T|0UXQRw#^K4 zSOQHm0|<8U&C&E4z;3N#*h2EV0obmJDRkZrA4Q}Cd0B6q{Hj!*8t`u`FN(iQ->M~` z!YaQim8S;k>%_yAwbQt75t&was9u zJT*{RCm(H)TPjZtu(p+#C9O*zXocU60Ap1?B1zawAZtLhPCQKAQh91cV0qQH&8#Ra z2byLEqN?%h5P_u+j>km98VojBn+%nQE?s9kO#-FFo_7Es!$k)Hu5B($<*C7h6CGoy zJT*X52?Su~t?9r`U3}oCZAO;LQv(;3MZi#bYG9)hIEu>S5(t4&`U<&eIsl`h*9?`X z1~`g9>^*gLf@4){{16#UEtRJRGFtP*HdLM(^f?I}Mdfh`L>C!aDo+h?v}TnCAFR6= ztp?Xl!VHzC2Hxr7M;94dDo+g_jfaSR8k!@|MJT)*A zSzB$(!K@Q{LOi@Ro_hoyNNTA(4V?R0=qT}oZQ$BVpcKNJMoQ(;_#_$FhL+0H00$6T zMC?qjhZ@BI*iIQm<!+(SdNi51oy=GT0%#F6`ES0BmXY{bH`5DQeQXNfVCL`>4 zqaPcn$u1)d25+ycLDkhF_gl{4Ba$A8nT)g`7~~kRT}Fn=(>VMDO4F~ApiynpNMCzh zq*ZNe98B1rOCTEsQrLqqRGvm*6Am;~o(8aqCP1QGWxuU}iI5lv_-zF+1Pf*Zi$ACV z&1f>jVP!SZ9fOw2(*QDo45v(__(H#!5@q|ZF4BL_?kg1+rCx+0gzl9Zkiw?*?w1UOJfdA z5(vy~rdDthT{LK^JdK`)G#A=6O8vt$F&7QeMvLSF-;0EUv7nxDP$B73L*;3J55c-^ z%uRz@&$hauTCh}}23V1y@-zx1sa0d|8kB0n2`AaGGIrU~72dF48bz2~tHi?q)zr$y zmkruMp!gH6+3^PffF`Ysy=xF_pDLc{vO#+ZWCI+kr!q1t;OCG~G)dKOf~VRYnVfR}qPXpkJ zQsd(I{i2|iYZKG`GW)fvA#62@%G0n$c_@`fB$!|+h4K7TQ3Ru?JTgS!u(wp6+9x&0 zKD6K5bf5@uSMScb`(h)C(Z457XEt@?Tza`*h^X%>Br2s}h<1ZjNJixdp~`Uw;LG)v zn+zvSNtV_Al$tqLa52*Qh353?O0lr?@+!WYk*?)v7fB-jNgEE|SXidEJ#gmvUl?A$ za`ECXrR67wOBesi@bcyPPhQM!FTeiMFTZk;W4=jLs$@j?=8c^6@cQ!79TsW4Bwl%L z;pWxh5~q?JBB@DmK@y>8;G3XC4q4=!M?|}Lg^F|MXK!4;JiNGY9dwZCA2+3zP;!`3 z_*E|T((rW@=HSvXGQysbktRRhTpC_U4&FSbzv1SpWM=-_!t2+QBAL0=xR07r`RHc+ z*^g5fO7w?_Nb9_i) zM+~1Ip?71lkLX-k0%Vd=UOtJ3SU#edN~IWjEkL?OKRc^E#4Y4O3u2&4J&v&fM9zw)V?F5gj-1Bczm&`)4Z#ry zA;hmQb(s+rLr#&t!d^z~&UaIV(8&Rni+;U&Er&=H%bT?mV0oYMC zsY-8kmuETAo@(hA4rL1Y1?Z_3Jk{ZHQ{4=sj|yxL`65iOY}v1ss(=! zex5JdQ!RLG*mCzpd#VMWtGf(MtHrKUTvok9M%H*QRd#-is8kCMk1yI&E%+|5pM24t zYU$-Gac{9B5f=bH1Pm!JRdxo9s0_mJlu)IY)^k*4zv@{aF{ns=`udHOY`19eiq9*m zWsSplM>;U|z2u>dx=VpwuFyQ9#BHwBM!HnBx#G4z&n$=44hNyT8(5Rq!zyBfdxe2* z4oueM%JzAjE3W(Va)l4Co6~WN0~79U{nf%?hZ*3X&6P%3oNEf3#m`p8o!hGBMZv3; z(%+;9y`Qn847}E6%L{b45Pyg+PUn>UY^fs%FQCy)e@kC^=_`4z9ks5uvQPlMwb{~; zjJ4pQXWpy7@m#O@aFPKTA!4 zI{H6LEnz%+KPzMe*fscFIv==i@H-atxS|SD0{0H&WxfJ;4}O=r0?rS9m&#)F2ER*f zF`0tjrMj5g!S7OEs2cxw>>5Jx1;0yuvD+2=F7=g7CpBu*ta>=}oEO}mh_6hI#E?RjuP8JuNpndd3?1Bot*SO!;zu5ZO;9tFjb zo=BClEK1T3cH4R)6%rmS@jS&|pSzJsCTEZsdP{c%4a+^%`!%7kK|l?n%V1ThkjH6x z=X6Vzw21o@lc?t)HME!$vxQYvC1Su8gXpTVL@MN*Sl)Sx%{ASWpiPx%{Yqi8tC%6V zr`TRoDWH#jS(I_Cn{ZN7k9v^mWt!ZThI5y~t|xnm_5`_9o{O96E||uL_6YJt6T0kU z*fZ7bE!{JN*7s*G`ZK(a!t&8mI0;1ZLx;u-x$e`3n_#3lW$g)yi=pn)5kSBzjC##ABob1>hy4`%mFkSr4r}#a5YC3uv#G40bL^h&!b#jUkrM_oAg)xD{;g z>Mj`vn%;EBJU;b~~K1u0gRc5~PEfjdVbX@~Y)ZD^s{j#<1yA?6PU|Ky!M@=v@); z``Tpeg_(4a3f)VEvfYafHacI{)4lK(U3|O8rh8^8W_cMp096be9j^kqu7rDLJ?#4w z#Of63bf0ku>_!p_de^e@Q*AIU4%QB@II1JU&v?-yY9_DV#esCN#(2rVai@=NhsD9H z^9PAu!lB8?826HKq2VA=+}u~q*n>-1gWBGO4=sUa@?uvl)%f-b4s5QaDrPw<>P@Q{ z$ogIdblDGY)F30mG^g^pT{_Z@UOKw2g&S*RY}j<5MO-?uU9T!PqJ5|4kN|!2Tc!)r z!K&>QM<-Lb1xIV2Ee;lcO(rglc1(CXN6-DBsou`Ps<5Yf)$*c1)4kY(bMbLIhd=z_ z-lr-Cw=M{WQdcq98(upohYELSwA#lWHc0i(4VO;ry6egxQOfNhgjDa^KsvB*yyEB* zhO0KZ;@G7E9)7CH#HCSH7V*yQnS~U*b4|v4(nZH0aD=ur_AEmRhPoUvwY>|*xdhtY zMbB&e?h`_)6V(uj$maHpLkj#MWpa->=V%aF{vfk+}kh z(UO62@AUv&FOmv+z~1DV>2zi*3Ek|MKruBAl=0#90Ca>#&y0-^J0}D;QhO>SQ%{h2 zOH$MzcMJ{=gB9v$FFmKxGlLIcJ&zB+7&S<&cMJeBA(2Ipzm5(Hl(Ko*E4r%jGf>z4 z=7;MQ-QR2vkr2yB3c&kI@iLI98_Z!nhe!n0)c7PC4M25jq8aFY{7n}Ex*t*sz%$ky zF3Fsy#`D9NA{ZQ+0EtE=aOk1vhE@+Z@B{owsHX|!@DHcqcY&@Z9A*xkWB>y*#Lo;O z3@?1|`4EV$DY_0NXS=ZmiI!-vGK|v7u{@sALK< za-a+lZxCVy%@CagqwL{HhF~YHU9l}jUrxdZpp`~y5Y%2r2Zi;VT#@;q@#z-GD1Wu^ z!G3NBfP!>E{V8>Vt=VBPXA3fgKRFOR>@`59d_259W`;-CT)C4lS8=@tUhHXor*n&q7&S1Dxa@>d>r7}|>RVSMTQuU4$;H4xT zb%HWwQ1Gq*<`52D5GkFY_HK8cfG%{D0IU-v#u@njA~bm05b-GJ#qvmP$uJCB0HeU% z+n3=0f*vQx%$*PI%{iiSNjh!;q>GAmEr5C87eIjmESY{6IJ8w7A_F?>w?J^BBI~## zZWQWTAO-6RU|7{efRmO6s2vnQbdYOeZ1n{N(590IU5pvEK(8_44*3KvfC;Ud)*Ax= zG~pIy*ntf4#}I*Go^>=A*8q|%9n;`)wGCj8=wz^*;Zp_m*C8ltB}v6Y5Jr^K1|4_C z<&U8q-g-j%(hNa`@L>_))J8uB?DoID(-|G$ZT-uu{2x8dd8K6qnzV=pSHKVn8I_i+NfA2y-quI6q1`51) zzx@>mhS!nV>z371z;XPnBP6*3=l~40uss<>ccekr!PfC6fI`3UiAcXo&UPADk&74bMHO zzGxo!{UjCjK3sL@kr^HsTroK5t@g7gk%)Z2gRduZ(S86VNGQ%+b+1Iu3`-0uH9Vb9 z8rlpzz|Fuif|_I8VPP=e^g@fcc8Nz_9~MU1+vE)>+MJBZfYrKoHP=I$#OUbIdi^_TH3N{8}q#?u-WdgTJ%xTlK1O`utB)qqgd3Sh#6^y92IP3*THMtxJJA zK;8H3UG9=s~*Dmph>vMTXTbqjO;aH<(lwqgsYHy+zOHSLxVBdX^M`lk?Wx%f0FZKHz zxVLLqb3-+nL>tB+-+hqRs6$m5t5IV1H??KD>XC2}VJ=a;7alkio-YPKoBsxyJ6ygw zbDeH}m2ZsP6a}mzGSle%M;4s_VDc5KHaBD{?KZ)KG3T<;YL`(YgJZp1JEHgMg+#>xzy`Q^c@6t#%cOLjQcf+^LbdKs! z1qR^d0&Iq);<~G1kxAK;I}e-qK*@(Jl{ZtRtf@h>d)WikAt{qRRxv47Pxdspda^f^ z-b@!{Ro#Y%p?Hmf%`>$hWB{A&b2e^l_Lkb4s}+XSC|X=g(at0l{hk91AHna<*~ab3 z-i*8)X1JzC(V}bs_mEWdd#?UN|H#YP$Lh)63_Z*`O^jkix%4?h^z{+Z(B25qIT(*m zOsYqW-aujLQS?WQUWLMVU@N1S(8F0erq*4@f9A%Go5`gcSMDU&7OoEy`R>h6#OTdI z)`g~nM)CZz9J@4nLxpAZ<_T<-|&WVz)WrSGF1IG0>-XlqwLBY zWon1GVQJ+6lUy=p-&5Nzybh>`(VGLfZ_|laN#OA$<kD<~AWxo{{40XkPP?1Bc( z32m!9X>`Bey&5dT-%Z{gMsE&gvu4-hdWjZ}nay^#0Nl}JbnWnpqoXvuWvGYIn*&+b zbg;&F$pF71p|D#Fqc;cIl!0e(yZSP_?TLMk*0v^vbzJbSc z9bYPjoqE4_fo`jqeuU7gfG&cU2cRs00tk57=fHtod;=%zN(~^mU0N)oHwSK<^Jcwa^yUD3B@m03R|3F9ESahCY^wKArWMySt zHHy*e62R`O*8s}F3Z2i?GJ10$tvRnIG8;V-O|GyIhZ#n14z$O`w`KtfAKe0O8NE4p z)8Zl+MsE)6NCKsFUJvBJNnL!S1k@}F9Xq0D8zq2!2{9TCqc;a8i@K=Zs2^e6u zcMrek#`PRPM9wR*cwl}XV6#fX45K#(Wb5J^$W~1VETZi+w2aK|hS8gY@hsM` zSVLYPX!mGzV`=woZxBJ<_5dtVR_cb)n=2wKf$;{#a|+pc=vG)#!!s{Q7oOd#-;kfXC%*92iD#0XR+q zrEFg9+xrqoLmgZq$*B6h6b3pe6OTapjH0Mowqb?v_s_*w9oaU4aBhG-`q$meE@PAk2B?lr(}SP#2WK45ODWsFTJ= z{KBxR;RBIWFa<25w*cHApo(Gi778pf;sO=njRI&0jZczM3;d9b-U2N}HmvLCpan2M zI8f5}vlrAtqZe30JRQ`xz1_p;Eu6zfeJu-(pSvQNJe@`{dNqE5(c|UcOXQ+MBxVRY zmxAUJMo~}#fXqVc7iY?AfCBhkfdmResRp=7QvL|&Ve}RXlJcxIjNU>aQVvA%f)YRh zAORAMh5&|E0rlhq;qo5bd>tXa3Zn$8f@e3d$Kv!H6GXhhucdkEDMu1-N=2-UsbKo6t0P{#$HQ&0dMe=!6$2p&GV~Uiu0!4`A}~O$*5Q)2(X~GX=mR0dS<~?l@Ex08~UTyl=a} zGI|Sm&k7OyEuf@oSnDKa3IIb1=tN;j)j9%Y;B*Br#L5D8D_NIu3+Vk1P%%B6YSB%u zfh?z58dmGo26hAVW~$9}Z z1i6(My?MasW%3nQ*mYY~nT=fab=-rcy+PmW=~Rk2Mz_%y0{>rLo_9CWd)i#d2@oN- zB1YDF9h$0wN9C2rBPA-ojGQ$7zQJgI87sn{h+kG<=VAoEtk9cUM%@@$=shWb1W7s? zfCV-UG!v5c`^)mn3Tz{cyfL!C4nQjwPxV3#YKR$rS)uL*q*mjX749n|>MNS6!Lri3 zQ~;-XgQCz$kJ)GVWrZpvNUg>%EA+yWQT(z3{nv`cOMaoNj&`%Gd_h^EKp+*)D$6e` zVAZrXd~916K;c!)@XHD{ejsJ|Wrdn#kTU$TQVnYEO0y4I0{@nN#uC->%SyoC8lNPi z{w#G2BYToI;j%=knDWb<>ajONmgp)c9-d7lt5;lkuM0|WDqHzrB_=c6%8Tk(`OsAC zxzVwN!|%kS)D6a4Dt5zz)V8wXCLCy58Q8AIFM$pD)dW6VDg=yECSJIzQu^8rW}T$( zTDgbMrK-ZohYmXhnq~v^+sX$DiSPz1k6!l~t6gyn532;gtjUKOhTtYi-P_bJ73^cf zsHd5whYJq0%`9e%#xH>*d(Bvak6HrBqWKUHW*|%|rsUbSUa1{zKy_6<1SV)Im{tRR zykIjj}nYkl@HB-x`x;$ubrY6FITQh9ewV%vO=vqvu!IY;>&@imBD2#5x)df z>$fr>*b-u-(M>B$0ZWJ}@2M-O9c;i|TKRAr!rn|+0%&dJ18b;8NAXGVl*G(W(EjZx{D-a zJ!+fT@{CH5NL%>;WJ>J~fhDjAmB)J4C2uIEC9p>)9;I$I5}*bZ$9mVMZh1hZ0*kCb zZ7VAv$bqJnLEqH)W%zMPy6M;GCSZHAE*ZX038JP1%39)WaKO4MHF|WXt-Y$R1cXEIx{_|g5fg}Gg-1mRMqwV~vc0LCP66eY(c5M51csW&AUoF#89$$<9u z+e%{p7&0PY~{jv5Eyb_>P$;v&Z%#^^AB+O7|O7J0F{D?nksWBy>A+bRWHKqic zQ3A#2@!Ae9ii;jG8Z8H=1PD~J;xZhV5=amN<4)2YIhO!{pjO%0+v`doJ|!!7nJfOV z1L`AThU-!Su5s}ra--$6lz?$c_HzxVg+8mu#;64Lb6Q;V=w4QP9bE~cqO7d4Oa&E! zO~e9R7$w~5-5f6hJxbprN#$YvF#ztMkxUhh$SLMre7g| z0}?ihjpE{m$UqP4qy&;svSn#lCnazL2^_^baS23Mx7z#SN}vZNo5(U#f+}GsaF}72 zlz=<(aTnQjT0;LDN(V#&Z zJp?Iwcp)WFijsG4MQi#k2k1eC00=hH0+BK*hPs1 zNR(|!Z%e-fDnVi#;6be=@CfA$kXU4dD#Tb~!2@J1#ZX++#Eyb4Kl&i)AR~1YykS-W_7s1?(2VGFz0yH) z!?kpRH*qRdNfRJZ&dKZj3J?m3ae&_`07hU#lN1Mgu8^pI?jl`-vr9n1-D zb^=BLzFNU530jUEK-xZnx~i~Fb@|w_0N})d+<*#DklCZAZ!U8mWAP=SXsDr%uBM?fu-AIg_ll7irxG+6 zka4&GBo*g^GRWUm_<^UM-Tq3!CG6}5*`WJfo0#qwMc}F$cE7!-i@wUvl_8fet_{#6 zSYjjNTC%4-2QN-*?DD9!~c0RCtd9wVmP5M$e>QvQn3 z0GN}oY)L52MHGOmY1sXCN4^5UWOn4~^Q_X6u1QSyOGCSwhO^&Zrd26AWf*RiB2%tS z;_gQ!T~)*GuYj-94M3Oq8`bWc3G_|M)%Ms$q}^lp3=Y(m|CUyjZ=-j&W64ld5h*C` zr5C|oI&U;{cX+$e72{>nh4hJZ8dF^{Wi-fcH{DM!>G?(z(634e;F4q%TMARL6rdAo zhMV(C^NU^jG&S`mAPQJ~$XDYpUb)zQ`JTVKs=MaI=xrXhYfdDG)ppIR!kb8y2N)Tq9Er=j$Y)z1fFA(Y;ABXEqHh0G}hMB^990kyjU0(5(6~Qj(5ViwbC`W$D@Yk?Nd=ftr3|&C0`MnYX|~oX^-;N>je^DOa`7=4gHodjUG_3m zgK(&Rr4qp#Z%2>0qNq~i#W9b9k>5itsnn!$@`3fM>KJHzJJR5!R4Qf|unL@25K|4{TA7f6m}Q`NK3vFps3kQpIhXvZ)RG!7oyxCDEvW(AIdN&6c8u7M zWY&t~@yxcZ3@lHyj%j58a~i)k%TW!0u7((C^thD)xT)Cc)RG!FTq_@VS3Otc)Ln47J*rIysd*PM8mx}}!XpfkymZ<`qqngp6=28yQfYlEWIK*ef^ z(R_%T8N?(N)AgvUms!|rfM2bAU`zK! z@|1e1CAB+MO0JV>WnfntpCq{z+g7sFk{XbftV@PkQUjrqK=@FdCxbOuqmy8mk2xk> z1Jn`?W2hxHFe(X@+2y$*HE=2yKe}DdQcG$;QL+LUYDo=Vk_3*RmS}vE3~FSlB{k3} zQEG--QiIooK$+lPm9Vh7_|b>NEw!Wu9wpNn%E{ZytAR;L;3#T|OCW@#dTw>80ZG-Y z&}2Xd840{4VTM{#17&jYLsTlY)RI~eVYZ$OwWJ1`B&L;=!fU!3h?2%9$)J-gwWJ1S zBR(;I!i670qTei zVyGoGC`}S5Q_^cY0AMb9#AveAk{XOAQCx;vQUh~AVBAT%spk@i7)_R1QiIV{vx1ko z5;UFI>k?+DB{djLE`G#l@{l6jOutksZo@{kp_bH&$#9@_px1P;@-;dM24lfeOKPAz zva%{B*M4iW2A(2eBd8@Vdh}UsOD(CvjgqCvP)lmK{1PaI@am&GOfG)(y=+S@sR0m( zB_ev!?;(H#5;lrj;^K$MKo7Nqz#aB*#j-Tik{Y;y1VRz|Rf75F5{Q^nmReE+WT@HK zmYL#L3DALr8EQ!lc)`Vwh*OqYQUf{=*UV5$YK0frZyP}^(fA}8j52^ zHGl{Slpge&4nC=i9ucSPM+9r26k?u>)(l!s`;WZnQPdI_KV(k1Yc)Lgv<6Ti&Yz){ z)BrOi5MH`hqZ-(SCP1Rxbl&E54OD`}IKbOUs)1T4(SpP{+=B;{T2iSl#hD3bUiDB* zYTyi(T2cexP-+Q@nT%uy##N*VoP?eWIBM8qmReFP+^D{n0YtQ_ZH)u7r-xcnJLm#B zr32M#-C%J|BjJA21=JFf<{EcRXEu9l2dzLEdl~6?)jkG}6=Wo1(5*rtRmxqQ;SV!l zEaER4jxh>8Sa4EnHw> zb-c#Wq+nQdJ>LK;(RLxRC>yoe^g+@b-qptpu}1L~Y<~vz(Td1}#7ssKpwyBkqDfh5 zNuxc0^Atfwz@1TJ5;GaW>V{X>guoYlNHiV^jh+rQ)RG4CUl)E7GZ|qF!`mY1py}+_ z>l!zkMYZ}h!a9tRLQ*Cl(Q8UAY2bvlU8Mf0H-#DiH-=i$C}^V%V?Z0NsH<;?0HuwT` zmyI0;fPf6Oqyg3gwbYoSYEUEUW07ny1}wFt0TcxF<<|u$5VQ&jMMD5})S5!4c2zOl zq6T=7QK5~(McO1V7X20z=u>M7AfU^|Y61n949Tof6bXK31Fyl)MihZo6*hRAjir_} ziZS8bO3-BJewiF36*n2Q04&c2_(_+I?S6nwSowlnWEf##ZQxG%b%6?iKia@A?{YEJ zk_Ipo)?Ystos+VWLPBv}q>fm_8%(z@6Wjg3rwp~E0W_t?cmpKTuZu@5A=zLgTWU$8 z=oAq2pe|Slv`xUa^0I-kqko6k)Trwl8rCEazrI04espSyjQEIJQU=r#-e{(#=_c@x{kz)|~rc45fN*}BPTa4&2RZs_Jl=$6ANU^Mf!#5U|>GuO?p8tj6^(zly zst_Gz@=a=j>R?{)Ma|M_S1k56nsSvCy@ zhh4r$RnDdPe>^rAP`LbI87KGgNbdiPvxojc*|c^3FC@29LXehA?^o$Jj)4&Xw;w^l!g>zsz#nVEVgD zucCLqd@T3bT__(lW$xtufP`4imG3{FLi=-io9I6$_Y3rIUv8Ynl`9|1aTx#k75ax? zzTG7EiwFwR`*$Sgr{~MDxXX7ibNlM;<)!8MOY|@Iyt63p*FWW$J`2|umIpKK`(S2y z_{Q?g=*c6p3)f$}@lY9;Na@YM?Dq2OJpFpCsqQu9MGn|SPZf7T304d@?e1D*@rpEU!gv+>&+ z(#rmyHG|3a722+y2lD%x!TL6w|FLF3>eKtPrq!4Dan0bpUMS0Nj^3w&7TG$rX|TtW z*gCaARuA{n)~SiXNA!hf^%l~8tT66=cg@7U|BB~feWv;>^|@D{2laVGpIh`fug{@A z$Mm^YpPTi$OP_c2xuDNQeNO6gyFTaic~YOJrw1p0oyvY(-*4%2MW1gS7@xhne(J%S z`hHKJUwD4-&fWEciO)T|j>GStdgcTC{JT^8@BYo*^(Q7y?H?p}zq_8E694a>|Dz8k z&;QZ>MBne9`YQ_m$bpHo`yV6uckiD5qd~$e-pdEN_Yczhi{5)q@2B4Uo*9#NIx@S>$y5EuCl2r5A@2wG zJR#3=$2xhY8@9{y$hI+g9)1$_EqAX)_|7#5-|;xYGt~c!yZ0h|0`2hjj`u=tE5Z+c z6z!Gn+#|pL!3!wgAHIO{?K^>bmQUjOVut78V|X6igXi4OQva>rI5FIb_;+?9{xGB8 zKeK+~&PVb4y^p%zpOpN5e-GO4-Z759VdBmX$bE4K7ooMI96KLP`#Dv7#asur+d174N zkDoyMoIQkin`iNSc@v(uHsZN*6wmE@P>)@Ev>s3H<^F!~;*)En-{*_n@;)puUlwL| z$oqwvjS{}|H`gU+zWl!A%GY-#+rIh!CFT<~*z z;@k#|-z`%tH$HJ>3ghU)ex29*X@1e~r`Ab+5ALm#{O_OT_@u8kkMBf!$1o4~v7V%P zaOz1K@08D$0~m*!$N7DHV&O@ocY11z#NRi|?_(qnxff?KFE($Y@k;XZ<4AXH#qVPi z3l+^D`h9Z+`F#T#uk^ll0KGDnA^swdYmy&sLi~j%Xr9sgz9(otko>L&dgj(fejl5d zoX~h1A-``U>sc!A9?YBV3BOZ2k_kZT#JhJAmD@@BgMQ!s1oYV6-;`mYMvmdhpin z=l`hBf345|L7#u7&wr)QKh)W355Kh!TD+2s1^?*?~2 zkPPl-iR-srY;RD%ZQdmPba(9Y=(kDqqr7v!t@Yly-^TR)d+Tx;UoTF~$aDMntUMST`-t)8ikK=jOpv@^t-m?CXQ6J>M8i zo%kk=;cpG5UivnTVFRPpZyQxkthD9>FGezq@W?aVOTv zmBBhmXJMA+CC#__L$GIVRnRYYj>3*wEHEz@X3&l+sweIo!#uuKk-nk$i%&tXEF6X1 zb>|r7{aX#n^SK7)`9gvBw+p#1XC*B6iJy3rurTU^eMte@Dv@c`0ahv&hqcpjO;b8~{{z5;sn)&{iu$qlgY(hX?m zl?^nmDc`vbXt!Oe$HxX}_sQMb{;FS(KY{nP)2PRmY1CtHwRe{7mVMP4e%>#S!EQNs z4D)U=gWWRBV7DClwTWcnw>Binmv$sm-`bU&+X1`f$d30){JlqEx0FX^J-8Q3i=GxGcW>94*gIsaRmlj0kXCwsoVHyJ(wyXD+|*e$2_ZN0FTeX#A&wwY2DmB!_V=F?K?4l zcg?aKt+TTjr!P*xF4#P=Q+`helHY^V1IhpX?9gW|8< zgYiALmBuT*AKVIka!T!?lE*X2pWTM>f4Ct1L+|?vnr|du9)tWm+impw+z#lQeH$?T zkMMjUxh;=T{b~Ljp5}M5TV93Ta&DU9(eHboAp4GfKU^UGpZzWDmLnPI9g4p)2G+AW`$5c}gV{}b$%ckT|R zzWMpV)aSo6m|Fgw!PF=Ih5n3rmns-nEEQ&IN$!0!PM`O&GYHc4yLaC?qKR)&rmqw6hF8Od*{n!6MdHK zp<9RW`3t2oA9&!_^|8;>`*)KELtS@<8TxyeO-Q{LGPKj8>h)Wy*N3|9yj9V7TfctdmaaQ@ zblq80|8%J9&Ps#va!>pHR)v0E)OF|19@XO~(2pw()}32VQGZcCFCIfb-aCPH=X0v( zzo2^l?F#zxi`CdH>&H78^z!H2x^QBjr1QlWSietBeBlJ--#!8P;cE!LFoWl*G zjpxgU@ciOOF)r>sPxFDw^;Wcw-O+XI+zDNGPGFuboItytQor`a6KK~hN0IIwE+_ft z+czGRbdRjV^WauISElgXoZxw{LjHF~tULS6x|2w{V*@-VcjLLZ4bS6G;Az&K2J24Q zVBI;l1MAL_9qXi>ZxuWE`Cy^&*PYD?uNPC_-c9~3)}4i;?-lvIQ@ZX)3?E zUpXf0&HX#N?%dOLC;94btUI($?D_K~Ss1bI+=|wn6@S;jr$W5>uj{(Q_8{f6w}8G{ zc!Kl=y)P#Gd$;Zk-MZ62pZp+o>kjnYamB^Hcl7T6#^qS^(iZr=n^ON=cTGjXT z6n(!=^*udB->(;aKlXWglAd4l|9s##$h{f-;Aeh)_OI5Tys~C6m}NiQtq0Bz{kQ7h zIe*ppv9aIk{Gb1?{ngk{{cA0+^GnCBh<*?K(9d*!sQ9JdT~Bxm)=}rzZk`?cTk2=h zCgbw_-hapmuSh0N|J_b`uAN;Yzn^^fL;sO~KmPq$dCtB2H1$(5@$&a)Z%} z`G(W~>tL#U73=@}e$3N(#+ztdIR9{Ac1+%fx=t_ZIz7J=>*zhkf7w4&yk=zw?6RTr z52s=G-O};AqJG|Dwif9g%H?^B@hr;sj@p|mjAPOJ@JY;<#Rh)Rtz*F7oImzCU8ldG z>-5`homTv|^$*n#YyCs@FTc=Wop%1=Q2oGLiod_C{^E=3C%*jx=GD1pu}-%Bq57L{ zoxU)Q_B=HW{jo5Oc0D}}`{&#=+I8h9=^?7;J@I4LPwZ3w@6OHx^1Jg7*Q(#RWq{wu zD#Tkn3cICLzdKc&Z2Qr5694#7wA7T{CTGwtn{}Pu ztLyX;^$#zoe;BXR8}5!Ldwyemvgvokew*u+rG9txk*^<^miUlCjNX^GJFdD;e`m-Uf2!)aB(;M!#mSYNILhX$0eQe zDfoxWU)!4e;x`{pUZpj^_{Qes^d9(!7xp|Za*K)jhlk)FzI+J&;ar4kpG>e$pa0|c zlYh94u=n>SpC-R^PS@#|b)8PtKRl@Z;qfUc&x4awQoj4ApM-z-;*;&{+jq zk7!*uyp8AQnu+6Vx6%8-hM(k@>>)pq#$7tga^n;CW-*RVZy-O6em}Tjr~H0uljQec z*CzOf#~Z}kqU+%t7cUb9}N#jzR7Rm$6Q}{j0N2QTtQ7e(!|(hwmh^ zPXG0TLGnRbXaD$j$rmIa@V9@9d_nR7uaH0Yr(Yspko?5o`JKVkUGg2TlCSvn-y?tU z9r6XqKm7D>k}r6d{7Ui*KS4g?m&srJEcu5w$)Ef;>IeQ{_-Vnz-y#3;^IscGtxQu| zG=A3n{S%V^JmI;|QU3F@7{~LoJigY`dLi%khk} z<1Fi?1zpd?@BGO6i4|SX-qiJMvB3CT(Dm$&>e)A+!8lu~XgpDWE$Vu9XOpfE&tQDL zc@+J;_$2JGm17uhcXVI!bJ$OR@P%hkp10Kx{i3dK_X@;o{mx_iB)``0)b;0W^%ED= z?_5;>^3Icxe_8#|7uEm#;zzOWo_ju#^jg1D{n&daP@Z@_yES4xTl)<1d5ir{+UMG* ze&w4x4@&yZ@4Tn}=#~L~->&}ZTjS6}W8<)=({c6N$Ja^xavbe8w+HQgcn6+G9>epv z;s>-}De`OM_3W7F$p^cRt&x5|`waZf3(u^R_d5;z&Rfk!dEfSHyq>K~c7EmkwEpZ# zHlM(HcHsopvq{A}&nn(|>)8p8_h9AOJre&-?AJb6#5#9>({H>#dE?XDlV$ShHvGlo z$%O>#+1Y44TO3SEI%5N@XSZ}cJE!Z}$>cFf|G)pr)`V=oHoqvbc$FFWnu6=oP^7(JSHz`+H&*pSJJFe^5NnOuQ zyY=i#+meFTvzxR&{x+q3d`JI!c5=sh$^VG1XLH_qc9+(|^w|EtVSRgK2d^`02=~VN zc93x&(xWGL>v~qO-1x*>1?d}_kKKAkb{qX(Zo_(Zc$=JyE&h6 zTAw~3`IgV7E8-tK%J1HKrutzE+ifIwMAx^yALe&jpFXVX*@rb< zUdKr8;1u-9zD-!qc5UR}Nq!^bwpUosw($Ng$?tsu^I}2pS6a_>{BL*b7ta@x+oJoo zlQaBI>zVqU7kGW6c3S%s{K(A}h10w9I~UmQrTCM&Zk>CI{a*4r8SmkA2C#=01{|N( zvlA3f@txm!PWO?|>ORl+p4Ij2#0>mSS|4~l``!TS!x+8)BYHpWy?=<_PkQf9)BADn zJ*W3M@4cq?m%aCYOz*s(%IguQ|1qm!%xiu12@Px1$^ z+=Y+(?%*Bzd9dc)0qXLx@mXF=#&qw|?ftF&m}07T6WRNAdw_zmeuS{pJ%b6_t9(Ft z{5`qM-*>+B+q)^kL0;_0zSs(i7~X-3s1`L2gli#AkKvDeQmE`1FcnTaqO3#7P&3!u(odB z2;Xw^IL%2O!?34G+kCCn*xx*V#o+BGko-Ld3 zzIPMe_YDy5u-MYHN3l+I#0D-e&G~hZpIfK(8P{@*rznT+9d8~-`PYsgk>8I@Af0jw zYuuddv5>7kh4l7KA-%)DNIH(nIr;N=9()PU^i@2|pT*Oyi6@_+a?xDee&#(A@9h`x zJg(UAo6q2V--q$sm8=uFvwJX?PwHOn%gRx^r5O6$9<> zWE<*nYTKCf^RCCR_8xw0jl2(Ez}ma;!a8|h+={jL;MR@uzUOzhB+q|-V>11X-O0{x zO(ut*!POx9WUZodGDGkMNLwVekwiikp(zVuq?eE)r zukUE=B)9y2?TkH|nX}h^@4fb3|NY-lN*zKbgh(Ud=$21e?->SXQtP|mOQ&o(tUa{ zI&0iGqHFJWZB8;eW9)wV^5o1XE=k70U3%s&aGRdqc%9VqnU`KC^?rIfI%_<;TFUjz z;%eBEYo5F+x$F(kPhRuMwQ?SM*G*V^r*Fbqdl+Yp&um&FYvD5+)?6m_TD(H~=b5D| zq`#iN9%qwu_P9Ek``9-o1H!nIkFHNPsjYR_Ev?^_HA>coRcj8u7;Dos+gLPiSFPdj z)oXrMZM1`HfQ`KS#dJnS;rDWUmSZj-ttlInQ?-fbF~!^1!PgCdd1b~w$#(k&#M^c`hZ9GMZA6M!*?yNJ zofln)d2;<_G*3xx+hsIwDZTw8yrvL0iO(WAA3n3B_`MMhXWJXt&z8$5Kl=RY5u~&2 zTB;}g{nAm=6BK{#By7taYP0WW8=KQr8|1!gSZ>vtd^6?4>5p>wsx|-o8Q7dVy3m7d z<#y~(kp7o?uqpd5Yl#iFyTID}B-wuN`vbB8Ki<0H=^rNh@MC00lD#)aw&BzxWJ8iY z_#v_>FOY3`?oEUrl8yP9w~&2F_Tsmb9XTNTa+2)NPrQ$8NU}NKLU!mQWOqJ(1M<1# z=xSLH@B91+l{Z;4{vWo;&wHQYy+N|(_@8c)pLczM`#)K8_&@SqCt1_`1J;|#n%#fM z*VvLo#^+6oGsU-{-gZPz8LLz{wlTQRDUk+L_aL8hV69rHuUd9dKMSkZjVP;<9R(} zdxxGGKB{Mk&bD)Bi0@Y0tJi_voz}C%rPcWR_+|LHb~AoHv=92beE{pB*m86x>1@mM zYFj>`w(ixNG5`9?Ext>+bnVS(*Xz|L%h{GA8-KTAp^J)*Ub#Mzc6#&$__^^~{M^Fh znA`6Pw0o}ecE=TH=kvPWz4QvS+xRVLZ<_fMZ^P|aH#Xgl{yfZkZA$Mx-9tZg1O9%| z8tz|;r8eCrwdtnBWu#WWiU>#Zi$l7G<+czW$tv5H) zx!}FGVO?3g4eQD#<*;m14$FSE>H2EZO>f2eao1L?AFulOi;}C}M407=uSt%6@~UKd z7uK1xyDpdV>{qO^uUO^c=Ii+LXO=dvmhgBT8Krdg@wpNw-ut7*O4_pye2tAw&#UU{-b2-Ny02!mq~tC0y8A+Q2u`UuyR?BE0<-Xa!yw9 zKI)BRyC_EX$VWFNSA6J_WFzmpNYCA;w!dI!q!+JNjO}~}`e37STz9iPjgwV-Xq-{J z@mnyz?{#O1YiPXF=c~51MDFT6{5R>}HKY&d=UwYHJ+`$-?(iDKKYl6FU3)o=Z<5=v z2Xa?3c29B}cf$7Bv{U7}yiSnK)rFqfcopP(mt*}{dpYE8zK-Sz#lQMG8qXyEqFvB8 zy({={T0b0zpKU{`*VTNcNOISI1LU?{!+(0 zQT&(c`m^H(UbhIF+{~Zz`l9o8Gsh<`ZUH^_r7c)T+8OfQe*^Sh?>4pNzIeIl`-6v= zvze@!?%XWpb#}<^8&!V~(c@<`?<_4wmEd3?|v1J&aXsUBZcOs}tc z{QRxZFOTkm4ZXxRIknrOay=fpO7-F%YB&1){H;!p)Bb|xlmqhU9<19>C?@v~)tm2D zOz!;^CWm$53CGo7teVn!|I4_%8`ivI5A@i(_h@|zgipU1KbKbH=kd$%bM0pQeE%V| z@1wWV_@eY4*LA|J3yxdz&>pm_jAN2JtK+iGSULT@dk@<6`rDB1U0Tk3Ye*u1OycR#V?8N%8sCs;d>hZf&k8g_f_;oUHjGz$@)juCpW(9>yqT-*H(7Hy=oV1(=(<`dd75i=NcYQ&n)hQ zU9c2ka$Dc^4auwCvo^Vk_VKsS{fVM zHA@-B)9!W9i&w6Lp4q7D#VTD7#%}@kMchqE?_NFg*|?7Lq5V1E*`f4SDHrPSu5}`J zd>wyIcQ>l^IsR(W<4EuLr6|wF%USP|9=}Y>aU1_lcPDNmy-c5PQq2D9)ucb@Z)Xo| zd;#`XeZl@n?_N&%QvJ4E2mN{Vbk7IsrD^!nnRFA7&owJ^$ z_)d@aeiC}T&-OT#>!sHszSHB=oB4Cr<62I>^F?wybiewgRQ0%Ga{GCmpz=@Os(Spw zHPGWXV*Nj;cLALq@87s?l=bpKaK-=P;EfMc`n%Q~ypj6lj&<}?=D}Ya_{h!4fsb61 z=-*Etjo{+^(} z@6x{yQu+(@x85cEkh3Gs>iToGi}jf9CQ1Dlb-%i#`u)7>_p`eGJi&L)X#aa&*Plmq z{aMoW{j9D(58X!NjoS0^Jy?Iv>m9l!U4I@`PTb@Bu>L%B8`humtFewO?ZJNd@qJi- z-l2NZ*#Ym@GoBCX84q#fB|m3JJhAVaj)__=m7etz%>*2BjS(7dJkJQ1yDkLr51xKG!geVEsG?Yl(e+xyV2`96%#>vtjD zhh!aFPaI|7fREmUpS#!N=Y220&nvIR&n?R7Uy9bB!@B<59IZe1M(fWewHwU(qy4-^ z`+37{FXHxj=K9+%k#^dq>(8RDKj*gsm%3}~r4nAPKks=_lKjF|$>Ip?iTg&LkN8)? zez@x@*bnEGi@2m*#D`!<5N@?b(71B`m?0#&knc# zC@%G=!=>E%!}s<+b2+U)*V7pe;ZnC?{kiuRtUpV-{@fSY4UXe*T<`9=_2*XN`e$^< zuKR&a$s?bk^#|+1GiR}GJbgHU-LN}>-XJ~9^&-wX)*reXC4b-NuRkBXf^3gVlE>%{ z-r-BdE_r5SWG@`Q^m)=goAgYGa9#QPGCGg@_K$8#uHH)HiuB#4OKH5*&%Rx9dLMtUoHZi`N6`vQnWQt?9^E$D z+15$f6%Zx9?JNI6K6|Bb{h8BLuFCX0ecitL6u9f|oWnatUz^mDdt|KaoEx0GB{_I(_f8!&`)4Nm<1;&_ z`h&q#@0R4~Y=4HHP)d&VU)S5|22rx@&UC|8F3)uTb@IAWGSz#aH-)B3l5M9)H*DS4 zJGpo|`kbzgY`wcTH%kt+#wQ=>FU-#OdkbHi1|B&@!+Jw|!wt)J?(m^AdXb^CI5~c* z*BLoIw|E0}0CMYaZYPe;&z`3D;F8;^01GD$_b29O7iLcll2`N>7RRTOV}r%X{w!Ib zqbFXm_r$>?dk-DoxBtY%{9v+oYGLF|omwiX$$ae3OiV3K_E>8BWbc9gL~m?{e)TzGr23SlI`96NA}?Wa%XtQ`%vT(3a5wjThByYJ72K;>?)58P}un9yDH? z`Z;b9>X{P@RHE_Iy|Ia@@r5t~y7DU;7;z`lQ`|S4+DSceYWBoJZ(@s<*&UU1$KUn>GzltXI zrVhPL!*eGb?X%O4EwFkBuzVrZal+ay94_#G=(@zCW|jp9tHmp|_p3IMo}_Ol|I%WxIWB z^5oPQEqQGjEv(3;&ytbxss8DiBuhq4c9UkmUa#(U?@L~qd|UgLQTeY;EHur@t$QprG)KP9K2_>}*3zf(MVEybl>=x;7mQq#a7VF{pYT*R`dW9F^TtDa_O#hCW>jEyi0QfLq}_G%D3QFYB;iBXi$~sL#Tm5s z)TOws73Zs-9C=iY<-~KTc>DI`$QfuXC+{B0n=hUm`LvDY;ChzJ>&dZW>R1rorQwCz z7j38u97MyBv@hCF9XNTeU-4Dnr~HnWDFB{wjO)aHnvJN`3SODL0qu{Uik@G*-Z1fiydPc_nwoFRpsEu2KvoyYa9Pf83b0q@(XqU{WAtKg zWM}NtIePw=D&;@_8@!R7?SQXtZX@TFnfiyCk(wT!vbpPA5G94juWG0F`bxD#!fQvq zhiB315P>6eKO<^SdK9{$Gw>V{UM#{(Omv<8=JO34I3MAFcZ((=oDQq4Io`-OK%UE=34M-4xQYr9?4#@f$(NOCAJ| zGSdf+<5xuRHV~c{!UI8G&9Gs>yFfNO>QBv%UlxzEleRJsNZ=tLygP(vfc)%agy8ib zn;lID&5oZakF%3bFfU2q`5t|WMLN~b4ld-SiEVb(AaC^`uvZv#f&SAS17W0{Nf=DJY(o5 zBBK+}DEc2u?eLDH_p!b`f`=r7&!tW97EJItspl>8cw->=Txx0*x4u4u2NnW(sVkm$ z2tKFrvSl8RO9Y=wH{fZD;B%=iUe*Xcm-^ye5C3z#{DKh|d@l9Hzz;r``r_%9;B%?3 z%;?}_=$dHUwXxZbdoW&O>23P%#nxCFc4m#GPy98u!^aG6o!HTqQR*rBHlFFl&>?lh z`O~98`^k;C48VtU#1IEldi*?A8pu0L9h&1dXtyOr@k%~gv-Ef!~URY6*n!c zjW%B74Iez-k(quXqhGSYC^q_-5eyD@VzS}zOM~Z4RLneeMQ>Y3bV%XZnyR0=rZ9NM zMBgf-*X81>!H#J=G4l`=Ue0Yp4DZ7SWkt(E3h$G7JLfju%58IJnzx+WFm@ym&*asQ z|1z4xZKUy`;(~n9hA^#2I<6L;v}Fj1lEGxC8p%9p-G+gr(&n}JQPZe+#@5N>tt*#2 zy*y>*Md4KP(h0S^om_bBR^{W@;xXX-R)!_k=22=}8IRpcplM~;Wo?ZQ`^;};JZ{@o zG4s%ps>Zg8K{40$f{_rUhhB5?GS6yxyv^gwe)u>UX)@E!um~%8sc)UWog8>| zN998$Lz+#J_uCm(qD(;B&ae_C(6qDKi5ed-;|BGF{!uaW22>mG*S29%s(!2LMJF?( z(Nd6O_CP+|{*Vf=;^-Dl9&g0e$(x7I+j!;H6(@Xdk4k6K$HTWOAF5AUZ%i}8@@weZnelx@TYpa-fo~jKav8`fI+;uzH;Rch<$>S-yTHX#fygggV z2bLzSzqZ}bFDMTdf+~4BM~QnKmY^mdE)2Aenr4O>C+og#X4rocXqp*TU|Zwkoh84S z@#Jh<#gKLojx|HY!k6=CHL&%<>`?je7>ZT7^dZ9_BP=q!Het)|y)sHKAS>vCaphGROzr$2tc1Ymkp>G71#P*~_P`55?-kw0KOk$|$n~R3C!n zMF8+|kdf*v3Iw|D=cKQNY69U)u(aBlsi3wc>oshQ{hYLYBv7`wUQYU6sm6~qZ3YeC zx1Cy=4ClZs2{J+lav*FXFQc}e4rl_{X;qDvV+jayLWi?T6Fn1TgdXHDz~X*Juvpu8 zaj+d}>)dWW)E?n26cP(^!qnnGDMgSGdQjs>^G=`8^J>)5PUSToI}tAl?_L0Ezmh4J~HzaOmI_*fy(oF%pA}w38?d>$F!T!j8EV8oz^AU8_zipxJ^d=~Uub zDVw1nC!I<>pSi?dMtC#0gGXOG(Y~lKD{4m{ZDq{`!!F22)gK3%PQojwnm{Mgy@gQ@ z9euTxUkaDG;%B7hJBMNI^=j0C*{bn7;j^vn(kO=x3fAWH3iLyeld3}cAsy&tgmJCW z=}qupER;qy(39ITuy}11BO}NL-N#`#wDR+jR@LbAo_3gz(G2tuHu9lpNGr%lS2PNg zLU{Q&kOuKPP+!%U>l($-!3(Wz-iruW736~{$zecof{diL8oz@nSZ8DuLq|=221=mL z{G8P8=RhbzKPR-gCeT6TIw$PI{AmP`Kz7M8Q-X{z6nF=%Pw&=NCcShm^A+*wOJU8k5)^k0_G&H)yPRAGSC@cN5KLeT{w^R_N74d+CUCS z*dGiuk7bnz@;4s(AN3G_nUebkbc$22E8PRm4)`6}HDn#wY0n&OM z3xcTbaupU;SZ9R6;MYh&6YT=8Mv9MU)8HUEs1YWa))@G8oe?H%kdgMcta*NoaQy6= z2FK7rMu@0227X>;q^5&E>J&)fvn15YA9V_Qa3F@HKk5LRbdW#?0De#f>EsSDlb{OH zTi7&q@C84=qYG=Cv9)B(L+agG`|l z7_okR6eGd&?j0P5cp0IhT3wvh*4fx0rVt1=5NT0%l$ZC)RaF zb|k2N0zBh4ox(A?`LxLYC=?aUuTBx5ZlvdozEQYl6q`Y>MhdKqVlBhDeNZFphqT5; z0CSQav>8EdV4ob+2!lbJ#sWF0s@wX2UQsul9Sn--;8ZopC~O)#7+eWu%oO2Z$n4sq zh!C)vAR`@Bn3aVLmZ0gN!*v&2$o`?KQDz4N8;PX)fxY<|0R)0w+9^ck2l=S_k?0*T z1L}P2SOCal>jQjfht#T5ibWM!+9V6I1~(YKNkQ-YUhBr;5Ql6pDK%Ps0?yD0Dw>dO$>680jq{6z$rf$siSU2 zG3~03$!+%odjhBtWP&;NWC*`9q6A!3 z!|pF&U=f!=xX|tu`VK2wqE77SSi%nUxJbC8DR1KefQSE;V!HS1!SI}u*$a_eM(M6C zbM5-|`q42yhNkM#>+`ox&CbrxI4K7XXvC zqZ?%ucwrFG{77GPL}BnMg>7831oQP|`N!yJ7Gk0ugrGTGlesD0J+!O>!MnWL$|U&)P*yaTw{pZS*54co#gJ=GAj z4Uo~|DIVEiR0>?>Xm1nCiSn+LrOJ`MiRENrsn$4^Te;D}&}iILD>pi@*sk2@Sh>*= z>=7Dn1vDv7Z>^AR!dG^!qdnHnyJdD!&M9w;pToV5JcZ572&!(}fGz!Rcdx^(Me#20 zE9}8)>C^`FHh$pztX%G({kdmSuUzifV6Q*W)@S8%M=(raEj^dZ9SW6t8$}piVv_iu z%06@Da>vT$4mLAmkOdGOo5YNY(MnqsS?8iFEuRxcD=qDuS8jKRslRf&V|L!L5ZLnt zjkuND9n=OZw>t>o3g{nnq$#JQ9*4o*&RDtKp%ewZJrdD4M=jlc<#q?tp4!SASh?MS zv;LLa9rMJel{JVD*<@qv*{kl_#>(vuK7No>5og`gB}k*3S8jJWsC?yiM_WzJmD?S( z>VQkrUb)@T7VuQgP<-;KlJew8oKL$v6})o0W94>-)2hnTTDje^a=U}}@+-GHR&IAx zK+?+Xj+NUTAoH%=?r@W0<#vZ`G@Zq|a=U|}c6ae5ruDaRkwt%WR&IAxvuNdZ2mD%h zR&IAN>pH&PgKg-_?T-J&w>$J4n$Ut}uXhl?>6XM??@-EEbfEw@!f+$gV@Ryr@4&r} zmHQp>A!V*48-}d@|G(d%gO)Kex2?%@_ygh5sz8Zc+-M}Yb42p>+{q|*C5_u2d_z|# zXCq4)M(SqE*7aSYBWgLiy93=_M%3EkiDqUAk=VCqe;qM z)M%2j7d4ushFsLp+jpKiscBeBsiBw`r_?YPGosXxs(KET8oErw9i$MnahTcx&Pwhc z>u&TYiuCRUf36NwNtg#h5RQAC$b_W*5xN5dG!Vz}qdLLC&7B}z)J;d-FByla9ld_) zY7?)?9lgA&cU6O1iq>Ij3TH~-*I9?DDcmWkly#Vz!b6gZS%;}9cGfBu`jD~?Q&V@H zTkc?}p9`;PQd}sn#H6KzaA{fR*Ec<73+tXpqJy$!N5uxMrBT>`fa2VOFu_ z9HyqYDXG%SJ4{XCbjZms>o7IN_0LK^@FYN5PL5&yR)w!2m0y-plj2URj8fam@CGD- zrj>E^#l?qLx}Y4QGg7@FgMsgbtLv9k9T7tbxK)j&nXQ-Tm`Xk{?%SidNbRMc!b7o> zj~s(php8#5@?zk4t-pFIGFd#jXT?^is4Fk5H1UBs_uazXKM87 zT;!oN#cg3HzpTU56aa4}A1tvA4pUQHc2;?eimF?z!_*Ycah!OVx^hfnXuTIDp9S`VQQ-0b~w;9GaT+{e3JAxWW+UtCmP2p2V0%gzU<&(nKajNk{ ze>!dJFg1l^V_5`@!_*YKHwhf(Fx4ecgbK|%Oike_Ib{N`ahRIovJ(O&C9h7{rMmd= zgHugW>o7IN^(mRy#$jp-CpZ!~%weia0G@EFoUFst6pnLJ_K+)@z$0szahRHdKIh`YSxudhq1&bKMw9Y3 z#5hb%;Vw!7Wy9#T9ZWqJJ#VvAr4br^k@%O8i%PV?CzA;BI7VM)&Ax{DTG%a^%~~lM-)%% zFf|1ZAbSy6Qi2L$f6n`qVGdJW{Ae53wTc0zzicjz!_*XZzXT3*n3`$=k^Q4jnE5Lo zK!TKa$?)jm%~#dX9A+G*W`GS+jUVl>ti#kyaRfHcjKkDSK?Di3y(Aa(GyxI~7IW(` zHB-caw=%|IY6gHo_Bqmp-f+MvhDImBFdw5CGQ}czhc0T<8x0u{2HCw2bC{~}Nz~7X zJVwVVD1*=Dl>Pk}hp8Dr3JEk0Q^}zRBM_PZi3T}Yhp8D53yQ{qp8KuL9RY|2ATbX2 z_N{LJB*qO1hxr(vb(y+sHV#uW=ZpwljtY7CsMYN16`C0h4pTG7jKR8A`+3WpuO%^C zNGz%l0bv69;Ri{FHNs%s;a9}7CLPvhsS2rDUiYz1Zw4vZ-q^SvnqqZn@1U7lMD%DP+)!) zfQgV8w}oE?fFW2KGAvC&4FsOmT~-EF@3si!%grC z(A=>@Od%Ke;PM*|D1>VmSiV6&UHq{M`GqYzF{-yXQWL7#b#fzLRAU) z6NL>2&UHps-@u(65mw^Cpa_j?8X1#8jkIZ@$>G4c&d3f11%C7yRj)>P`N}m7JUs^) zL9t;q&fvhg%E&rQ%@h&B1E2n2P;MVK93*B2gD5uL1!s7LxX#E926gsq9HwRffw1ez z;O9B0kg6Ya5ggvs`Pi|bUjB?$06v6`Q)b;l>*E%#x}jRg>_|`#hDIwWlmz1-Gj5?# zO|5K18_eu5P~geBHr_eI)wX%^@XiKTd6n}yI`w1 znlaXOQHQA_;f~wAjfWock~$Fe>%2#T7ceL42qKlk)U$pECx@`<8|ec?4sZ?=QSxS~ZreFb6dl-HECP+4w?`OsjcL*OhI98QoAn`S zG&a1-3+A=@XZJvuv_UM4sht7&4ul(ePwmb6>3C&| zWsGW@l(m9l^#v(6^dAn*Gr;13a6<>;2t0#3_dvLz2VsL{z`=cCUTNbp!;Z?ZYYK!L zdJwiyRzElIT7yAzlQMb`FnN%2Ll43x$>5wl5N_x}*cDm*41HCMh8~1%kij2&ka9y0 zVt=0j2M>fBdJw9Oe4z8f9|$+}Ahy~Wd`}0$4Lyh*a|W24g`F~GSUhfL1<awPu=jMs23l z@;5V#!M7B2Y`!2y-YFva}e`LD2nDkolsd)dLZP@3Rq%s*FbfwIAdIK#wb%a8d~ux zNrz>f;-af0FK*E)NekO^V2||-(*_j_&&(}zJjWhb4|ObaJO{6{5)<|8Hx0JMDrRV;bOl0M z7E;SG$H{xPtPx5eH^-PcXquInSxc~!Rx!gI&w=sh`lOA-_RjQkY^|j;4LdOhY%hVL zd_DXu$2Qx=H*YS|Z69d_&E6pKIdaQ-m0q4Xo@1--(6llTa~Izr<~cBMy4i%e9FJ5z16MIs z0(HF%M>7X>UdhXPUCZM@!S5G<<4!)LCy*wSW7a?(Qv=0TrBbJFnBzHMaZNu|GHrdY z#~;mr0c8R5pc0Sg-N1qxZ`VOq-xrw6pAyf^c*I#8kgk^|H+I92`vo zjXAv#3vFPI=b&h+xT?oGdBYsf!P>0k!)~<9@f^fVR~&)*>g3Vuelx3IdY;?1nZdmj zy<>(msF#HHN;3kV@|zKC%3Q^8C>(@Kd*u*Q#a$QF4mTWgJNadq<2gu{mAp)+I(@5_ zz`IoWh}UG9;{-cPy;1ydVQ5?Ccn%_E&P)f}%wS?lplN0hD_wl^WMB?*Wv*i8so@+a zG%7~p(cfvbmHy#(3;2~zKBNw{Ept2v<+73wb~0_t9M3_ERQY9@<8%;#W|sLLG4(8S zJO`IjRHbcZkSQh5G&7D%HGUp(DJ^q62N_bo$&%<)_;FkS=iFC_PxcUQQ08!OV+R5X&6Tv3bvVVjJdo4&+DzhcU-p0?{b7%<)`H z#w2mkGahBC0KSxSm|>0=U`J|v5)Edj9$$2q`(=#4yb9Lg6F{j0d>x#5DmD4XR|B9f?+<`viieYu_azx&FBUyCF|}G=6HeV1)!;FbX(?l0pniQBEuXnVA@Nd6vC^I zGRHMONd^_N%<%#^fb2yCqw@zhSdS7mj5)6Hi;(l!_CS4w(F>UVvbi+O@d9?g1Y-F5 zRl*?B1V}Wj682$!9|0uDnE)cy52}Ru%3+2%Uf>+8K>Px_u<9hs94{0{VDk*9s-KgB z2oh*}Nfea!%9{lItd0%x5H`%#UglzF0w&| zu&Qy`Fy?rH_yy>Su0jpW@j_7xxfvmAN{|zF>>P;gpjV>;2!$q4fUj8PWSQdyun3CA zb@zBk1;7I3Qlzq}XrXLFSO?x912p2cHsBcs`3ROk z<>Q_!Kr^fJv4hOL#DMkE&q(H`GRF&04ZVy|QCD5*usRz%!~lU<=6C@>y=oWfSuY!h zrs@tW^aate%<%$P3Z)lo0e~W|VZb*GGLj+a>I)iSRU^wBFF-pa81{)p4ZBXmCvl3GwB%wCvZ^cFi8q|V3=1;915u`o5#p@6jv zyrLi@=nrhvi|`(8M=e%=4gt%t%<)1I8rL*-FhKFS8q1iiYlIDgpOFGThB92>4y{_I z1$ak6jkFI@F$a7I7Hd(;4lT$=v`kR&9`t ztjSfg!~+4Xz()#@n*3Zu88}N7G^RQiI{<)C8RmF_xv7RI zz$rf$(FCrhVfS0+cmeFmFvkm^R#lUj?w3j6Y8rOG0to(`0q{hr#r>kIm#!^4WKQ{& zLAOB%gs{~x=6J!%mPi!CoiU08?Ws_hnm+_B&HsuHiJ0SPR?!s|b3FIS$Z`%%^;_N< zBP)s~XN*kuq6Na^N34*SvF;fn4eUl;k-Y|UXRb1kyw9=i#f0+K(u!twV;(W*L}5uh+Q>pj5Cy;RM}3UmiJ1}+pTmfS*CU;%Jf1ZD0L%PDw?yWkb% z)$j*N0n=k+-`XDX6!+}nV9(;IQ@wd%Liu)yE_S*Yq1ov&#ObNoljBpBC&*&Qj@T1q zg9H%-VW|SDYfObHeGq~^Y8E0A9)tiQGz!&zC#7wh3(Yh)q$J~Vh>*Dp(oi|3k#S0) z!X}&<1EXfaLgR9FAiUD$?a_-;c_HG@K(v!z zmdaCN&9CHxxz#}BDZyh?`EZqNcvrOqA}yoTwld&n2{f$?^x4G+)a|!25aCkAjMqE` zB}<6WU%t+uX%53DM5=E4Kr2%4{%x&Au*bYmeX=X6y zN{w%>rk4PAOBKTrl3y=iyDGLkm8S$w>g1QD@|1voD|u1;bvs+N1XNh%m!sr%$N;|>Ml#=DS^srvWfy%&1R`Q zCBWKBK0MB{RGt!Gtjb3u3F`#11W(S1hpAgCPpJqjuiCbm6@}$M)676rHGUZ)unkTi zOEB1EZ8B6Ia=Om@GzpXvduuI#3>O^)xT<+Am8S&GDmuncc}jq$5(vPoHik;zrY=5k z(<&oNJSDJE2^{7G(j^cAqx1^7C_rE1fHPMzKiE)tN`RvX6y4>w9fq>T z50TNfrSgpU^S;u=}Iu@T>QwBp{4SafG%Z?H&mVy_$3140j|eDE`bof zY+EW%3HVafyX+jj^S=@hri2+PPYFoU#mC7(HNq{Grvy}4@;1a!c}gIU5*XtOKu}$D zV2^b^=D@ymQ=EsLY~lSH!ITxX2C&NOBt0~B3E;e{&dE@DN-Y^5%*k8{GQzfj!wi+D z1VrNEhh`1E@@=LpkQLck8!Ar;8(9JkREk1q^k{Ewoj{hrOk{1fEvE|^B{_u3_uds>Ge=oF#z7D45RXB zd=d>a!cl{#RJ$lx$(u_<<>|uim%w3E9!-Em!<-D2rwfpvs^PRU{B!JNYJRNX{4{cF4C%2H4Y|hgA>TE0x4`k z7%ER!VG|BCRGuzi6HS0bxys(TU>BGOiE)76Rsch=;*ykUD#0jqm(`7Y3|c3UT|g$V z;e%|@x6nu=W%3bV)>R{NF=(AYcHKb`)=RHTfRQMGNMcb&sHm%MI618o$gVr(f`at3 zfq{bECy7PbgjAkx`64S_OBuIIiysVhKSK;sXSf9W1+){bE&cPsNr)Bi%@(A;bq`4r~>`cw+eHUl|WHibnk? z^-p%DBxSlt)ekgv0)le+#{hl7YrNu zmqde(u~eQepesY==>oNK+jMXPz3GWMx|)XFZ>c<809TY67su}x1+840nC_R!qE!uH zt6@~0E^CyFQh7w;Ia7ILh`?a4s63@lYLIhizq#o`5p3ti<8}99BZhqQ!8zU8)YEn8 z-98mvMxDu)OajynKbuCd&-anh7zS#3_KyTcV+v}Ci;4s~5AJ1lix z#l9KIEl0IT68TTsuy?LMptim7(7sppW+sO*F*xRVqEaP8m>L@|<)nKvgZU*E>E7R) zAA5OkVq*OMv15Dtb7y+UA1JV~=~9afmH3@j?A;WLjX6G~ zup@@gkoq+BZcO$OohwU#Oft&L=eZD@NfcAA9>lyBAl(x75Vw#AEyxC4>Vbwbj#=3& z8Si2Obx&7tq(MwMXz3;enZHDbfmaX)QnV!10Eo&0cm$2=Cb#b_jZ$vAXKh)X^TgKP z3MO_63Sl|-5g_G{M|oL%XCfSQ_34-Ax2a>25^U;Dm8E{s*r^&aGodw7DiMyqyQ&;(S~XPHUPZf zi#AjXhCcuTU$miGFx>$Z`=Slif-MId-4kUKJ+2n4bC~76XhXF?K7eKIi#AjXHZp8u zU$miG`of`1Az!qiTCh}Mp?achaK_bwtqB{`7j38(tVLLfzGy?WVDG`s^Fp}Qx_rc+!k^&ul`yf4~NE!aIUczn@@YQc8F{>c|@s21!G*d4xTL$zQ- zU?b&;vM~@>3wA(wONg$wRVqZ)1vdD=7fF5k`i+!qy=d@?&nv2R3W?1-(!o}rOBQIO z+9|Nh6`DtsxXqQ?NSCTAS9tsLw&mEh!$7Eg1G`)$`PvPz%avM`bhd2GO|HB@k8_2) zKQC9<@On5Mw>U82+Uu|8iW6ple>PVdWpS=;>{Ka}BM2{`(M|tKUwP>(d9KniuC}sJ0KK)@QU}D@s`p4QTRj{G zR#z<}nm*Qn+KaD#j00j&1x&VlKy0!#BT5Z(no4wrBKs}Bwlbn>ue~l?=@=YI+ia=f zOtxl7;T#y}&b9AeS6gX>(8CH{`HUwq&Wr7-1{Q6qeeI3c6JCdJ!ca*+5>^ z;cPv*)kcg*28>w1K7QKLRzRKnk7alP_3}TK2FK&C{>L&Tf%^F$OHF|~`X5Uzv3d4B zR>%mjYw)>rK5*aQbJ?fBUr_Kl&0czEDxP}k`2e_kATMMM(aZfe_|;^NG6UT`BPl9c#!V4mx(bPOJV zbe>LdH^y<&s;5UmxHPSTsFs~g??7``X(kbi)H2W8I1ePcC}J609lE{}6Lk?3M|z^I zlx0zpey}^%6RD8!V2S5#ob@@6Oforx#L!!MB4|kW)u_co5# z+K>YJ7{o@q>LHxe)T17>acD+cq6W@g8~b_*l(FNPxNSWJ)A-OHLB420B|MwMu(lp= z>6sa{zCU}>pP@Sn%SUg+NFb6QIy7F$^_(_51S8EUYfn&I40V@|00Lfdv?X9+I9rxH zAewZP>5Fur0K8=M{3$#tYoN8aaa5+&0@~{(gB?5x`IdD9-JRZx#yFpo&o|17B zncj5;RTV1D;Ww8ae5n|8uOAN7fe3_>KX@x%1)Ls>Dqv{vZ9PYG=WOvJt@AWIKWm`J zxApwYr4w(AwA*2n)!hp_!><>NH?0>aQC_uNX#;$nX0#n87;PvHG^dx0`ig+vSCz&( z%%qD{=w4DcwtI2FM)%7ax);`>i*L`^^vq1fEH6U`po)Q`<5fV{mGI1rE{m9UP%j%u zr+&sE*f+Lee%9?^Sov*jFn7`?qsl9e>WJ_eFIq%xaiG?b4%Qei8SVS%jd@GrZ!29} zlZi{?Lc>8~$^C7d!L?a~+TMi?ErDk8;#AGWw=OtfRJT>ka#YlsRxyzEy>`%LKlG?U zn+VgK%I9|J$eDnND_Z54xA7lcEp ztC)GGK{-_Lp;65`>o>HmJ~v!CvHhSce?%#_mk`>Zgtw6n_BUR)=n@838+CE)QUMF! zB@>rMRawM4w|5rW;GJtS=1CVFgTN80(%8EUZ7|g3imB~gFwP~=_AYu}LhAKFarF?Sr=02(AvmUeGrryF#QUcVg_=;Y9rr*veJ`1?xG*-3X(_Ofbw zbjdbA3JH`jFMINICzDj;8>~WCYWUr#I>26uXscgXUii(Gh_+Ue1I?8P@SRt*e6v#MHZK|j_8vxCFQD^~G50XqAj#0x z7Yd9gAyL<)=$e4BBXsGE_}=z@MjM2mHm}5L`c+i<4tVquh7&2T2|9X39`Q*uf@`tk zBB{UvvG+EJ-z*hjTA8`G(NWGA2g;7zD*#5HMkm22f9T=JnQ~!B+w^j$WY0?7T<7R$ zBTJwe10WY^0wfw0Kv&7w2dz=*sWZse#HOL)g%R6y3uPdkr2yB z^5F9&>B6A?(zk66Yq&%ru%^cE7afTu$NIfI$0T1 zK}QB9zzC7RcoeAmLwsV*M=gN8X>?;rp-;a7bj9M0uDPPpQ7n=JWq^2t5G!a0(Md4M z9+qSXcGB7v@5SilBpHG%E;R^hPjinDWe#h&xk6>yGOzK;3uKhP?(pG_uNnXf(gpRW z)F~4-7|h**j>4ZDh#vMDpo4|3qYQ_dx8Qc8QZthBM?k}!nvQ~`ymBKGzYVk=9B59T zIsiB|fsQ>5(Gp0*x;ALIYtzBTos#3CdX&nJ0;xLLERd=`Qh=9|bkqr|!%*} z0o3-#0H9dc#5n2;3V;cuTY{RA;tGIt!@YpJh6`Yj-<6wIOyP9TH)p(U^} z@6vF)2-xyCO`+dud0ZT5VpOB73ApTHg6wi_L+V6Zl@#f;Th z#1G=~MnHewC_gFA{y!*cgqU+->6 zFOpQGe_*ORi_GxApjBa{SAB19A`$t3JFg*g(S86VNGQ%+b+48!UVfoc!`u0!q0Pt# z+zc!us5!!@qO!bp3Yya7dr$WwQ2n00 z%Ux0p5inA&Hg}9TqwUnawCr0t#=SJaIfZ~a^^^B5b0iw7I(r|gqfVzqB?L8>5vKqT z0B%pyp+#J~#7k|H$wQ7WO~v(ctkE*cu+w(6o~Wfo6!|dNcN646OM%T?mP55m{h9;! zb`5JhRHI3>VGQ!!1bGd+RMlZMO3eOlX_>BiC0s<9OCC(WU@N6*v+WaqYl-^)& zWQIJyrXBM9Iww_D5t(Up|DzM!|6uYJt2U=0>8%54Im0;YFm&%Iht;(PTak|L#}w%w zQW27h)~b#|^YVE^?zEKJ+zru^o}QPb7>RWZP7kw4AE@`5x1-p*G?LAo2foeS@GUz! zM|G$I1MqTzO+-?0-Bq#3q-@BYhfRE;hbh?!u57MT=`G+L@%H&r^WmL-@TZ z?{OQlHzO~_He6GqXi+wRdq^t!JXQOldE}*RV>M)Nh8|{}CPuNMT$+p_dVNHux;H{} z4#wjXlWK+08<tA8?Dip>8TN%BC9*)vAwb~v3q1oBFsyAO$UwQ?N(AiGU_YZGI~=79f@^A(mY%I_yDyu6O!g|w?bM*Z>py$!x+6OTBs6} z=@n#$azG4vGYE#}!lB7>jNTMS8_);j1lCkdbiGRHYtcbwV%VfuC}9-^!D7;;IKBu$ zZp-LR^^8G}^w5reYFLpfF;iIV2+2LVm}s(Y1<_@HlR_#y^sVfSkJSeBUjWf)X6xvw=VHDI(CsP3NF23OnrGS~L>}9C> zZ3K*6#fI%GQ;~TF*DZBus zDrRQ0kd^3`2*l_wE~wWIji&$&j;&K5MJ^q?eSp%48PYHn{Lu!?zDg(FNN7D{iZqP1 zARWwX)XVH7fv%+C&GiOGZwdroi(|Ikpi7WY(7l$?o9b}l4Xo{6(1lYDG~Me83I%Q) z4y7PKrz(b1(4ZNhZIvf&PWxd`qyWa<T*fD&owiiqy&NaOgE7cjeM zP}{q}#wF17F5vN0;~RK9)$yfbIH?cHkEIr3 z3WO%*EuHONkg6rnbT43O7vDG{N`X$NDrUSFrT~^zO!pgJx4;}GsbH8}MsEt|LRC7( zfP|`ysm*q}0BhEChA?{5$bGi;HlE^!vP&i|jm}Ac($yfbjNTOJvnDfy(Mv)><5)&- zsyt`jf!p2%$lS>2O@T9O{1iB|*E_nuhpQpf%Wl1rN-V}Ug1mbkr%U=%HU3_yZyXhdK!6SMK z^f+a8WEj0E&dnrn7^BxEfYVoR1Va7mvQYJaW%Q;%T2nqNmD%Wz0ELA(%rJUWpgk_W z-4vUTwdWUE^UETYvQu#DbR zXA^ID4Wl;&VOe&=vLpBUK)Xkyo1J#k@dgppZ4Y3h%1YfZdQ(MYB`_WX$}dWR9g}FZ z>A)(ft7qK+hS8hh3|tmnnd#mT$bbw>m|^s0fPXbUiADv$-k+p`mB=!BGms&~Fg1+c zOu=DZRiuVq4`32#bP|m6Z#v#;VDx6dSY*>A)^JdNtTnu$8p7z+=$Wznnod;P*8O`1 zJWe(ThS8e=j*~zsn^$|~c4&N(jH=&AVW68b@d%`66vc?)J**tY`}J2ikHdyBdNqD# zY%Ir*lW|cEK+EXO00O4GZWu-{dAC97s#U^o0u)aZAknA^8kux-6U8(E075bcsRW!x z&;)9NQkY@%l7l)fU55CXVO7HhA}LO^Y68pX%>XwDsA3qs86ZOh#-jilLgSNU)B;UJ z61tCK8vt5}H@b$=n*ocY4el^TZ-#~@!60b~T| zic4aq0Ms$eJ&+$+?Kn;uCXITifzeCOM*j9;vUdd6dUub z_ME3ZfLXdcqc_8NS0E{qzibqoHn5axMVO@BsySd8y%`j?LQz2hbO_1_ zA+e|cXc%oCOQ@D^L9OcCu^ILF)ETyH*8h8^P^~s^1EE^hK{X0UnoH+(f1@?f!064? zaDn9%RA0xRR^N_4ojDpFuY+!8hicd|dg&Elo`GcJa}9z5$oSKvYZ8mvUws@YIFi*q z4s`&!nH?bJ4UFCl*0VyyegTwJjomtlnF3&=1au;^q-q_3GH|LfVBY}9uy0imGbn(J zK;3XOaH>T&xdyVFYH3)lSDn8dgV-~{oC&fA>Hzd9Gnlb%huyB&P^ufZ!*o|fwnF|K zP{R@URZsvKf(rj+hEk1&am^DCz^08@1EZJTC71CB%RR_l#viOreZAb%zc2!wMP}&K znAgiPdNVbdWOn(P15k#=O+wMoQzKc;G0UZvkp~00>UhiO&0sMbMsEhB8T3^WiZe&* z@-K;UsHb?Eu&@S3Zw4sZFnTi})vjGFky^&2YnNE-H34A_jNS}zw;IYpvtz`$hK-e; z&*;sJyUiy1vw_i@DY)Ix?ig{{a#$v{Xai>e87j5T;!xdLMsKE6YO~)^?-|%oStd1f zoi@CsQe#Yns?IWcGoajdu0h8D;31)S#7T<)_{uDiS|)I4@-#4dGr-qC?)*Ln@a@{w z5~-y{bVL~Jy9x4WVDx6dzSS-ba@P?Dv(_-F(Il>6Vjb56c@1OqX1q>aoY5;HUpq#x zj8|-6KyD>QZyGRqnS8~4?UYnen1fukb=;3g+ca7_g<_78H~LKA|B1o4^GNS$b7#=t zgn<5v^O5nbpVy^@MdfKf)K)A;<(H9>#_t;p=a;b}{Hpk68BQ*S@XIpw)S{jOV9a_o z&(u!}AVHFj=3a(F1I>h_{r@)nbOcfHOLj7a;WtloG8OATm(0`R!yyR!P9BVhr$`_Os z3Pe|^U{+avS%zKBP>+!rHW(^q_+^=HejsJ|WtnctAZ7Svxo)VrE6q7*4*XmC8B0`s z3YG)@*7zhD^=GbQ7}=Aw2^U*M#gt#>CbRVnnWL+mcz8FJcD=HX^|~O(n6Ko6m6%4C zl^4~o^5Iso!J}gigWriqsY4TaRFPb<8y=*#l@&MPK-0LWu|A=mj9P@d3B>SeOHM$(?+73}HPJ<^WnN`KT8SUnvKyqVmyE ztMxRPgTm^>!_@Jt51qVJF28bMCbHz)W(FcEfu@;(Vru-{`0CBUE6pKB^C8k1IY3Ai zi%(E3&nO4kvXT!#rndDEm;;MYd8}vE=(N0{9N421k5UJK;tvu)4JwZHu1ekVfO2)4 z+sFgT6%gb=)5^dEH9kplE7lpAlkObYo~%oT?~{Y5DS@KGy#o&Hu3YrUr>%9>m&4}I zS;H7&PmcE>B@ifwx1hsbckv@qrzP;@K;&cvFa(|)=C=e6A@I2P5zo^4-pj$06s2aE zIyo$D1j9|Nd}I0 z-j)LclQ2WQ$w9Sr@gr)crP$;!iDma>C^k88EeVuTcukkXnsxCbwxp%fM~4{FVUN4UXcuqA~UbMW6t+HWiJ-zeib z$VhAhy|?IPQ~==6XVh~8pEwH{6(Q*`Bjb6c04Ac=N01S0Y#;+9#tC^FQ?080C_-Lf z0~?|MfTH{FAR|RiVsWX z0bS?RGdXIEM_BLY-NVugjd_=SqP z>Pm;z*_gv-dfr!HA>+cV0OwA3n-*JWJiLd^uR9kCXKr-EJ~i9 z>opkx%Yn63V0Tn!WQT&|?nsG)8cE}0?JBS<30h7To2tU$SkU_@{uBamV~7WZf;O&U z>`;)3>PCm8gP~vv1_jUgZ}3cE9zpRREtdC-U?>tF)wR64U+C(5|N8 z?6=Oe3Pq<3!>v$c%C$+{{ivj?YS{e+@OAP4G{J9FyEhZ)P0HoY*hQo|WA}FIs4f31 zlS% zC?oUJ>-_@2SOF1C?%Jj&UWAlYg#ar?eF`*8BxM{_I;0+}U#m{4^pk7WpduI@S{$$f zX@;Dhnmsu_g%Lt5l%mM=1$#Y*Z5AOOk34g)%!_6rI;&G58SBkLWzmHYHtfwpbnKd` z6edMux#>l;QI(P*FeEKH?NBLEU_+$J9VmQ=H53X6c|#JDU-tPrN$70$;*XIs<0qn) z&^USykC7JY)l`-!Z6idjqwk0E|~aK18w_s3irEJtrQeZh+iEx5PY1Z7Zt* z!hxoh)iBWbcBH{bDOAibU==tkAf_6?&C%i15+@&`MWjb$*X#EpOtDHn&;bq9l0r>G zlxJCLNeNs~lMl&jgwaW9ylq$s*iWXPY334KC<%<4xde=-@kuh8?w!o{*u zF~v8lX13Il5}=*RhYMK)wWI_l=aOHRT2cb0Q~717B_)76CoYXs7v3C_mx|-@%(krz zEKjtKX=MO&8oxBlQ3-&qgcxb`xRn98so3(=k`gyxEBU~>YH*QL0%@z{MeElMVW=e~ z063M$+^obcwWI{T=ETF)Ew!WsPf3=1+suH_B+xW7P&AES8WgPrDpo>_=0n`f0LN5J z*Q2^#W??S@epT|pUb=x=Qi7VK^2<_7N?=}2JWAbYq!Lh=ibuypmReFO@2HVlQo38E zWICBv26myn|Clt8E?5H?h8$zToE=p-2CV~&qYfLfwq47H>L zMkRqVyF4?b1Wx7RN8a@;wWI_TRqzU6s3j%vCQubou!tP0Ci*!VyGo0C`}S5 zQ_^cY0AMb9#AveAk`jz2QCx;vQUY^9VBAT1sOJ)h7)_R1Qi9P`vVxbn5;UFc*CotQ zOG=!*x%d&I$wP|ZnSQ=h+=e&RhFVf8Cc}Z!fnL*Lm#@)DFc=G#T2cb#k(E_3x$3RW z5_pP)4WX8}=+U#5E*EomJqnZ7Ow0p4Yi~MZXkhBgnpH<{c{OK%qdGPDFHH+Y-`I*@dq8C z0|_(Kk`nNOiyslEEVZNrbRedgp_Y^iFRM z(@41AbOE)5q`Ag*)0uPs(m^Xw#$HA`UbT;bV+9$>7<8*p2)a4P8U8Q>#v=BDp_Y_D zR3y+)OG@A$g#3!q0$0i(X1dcem!nGnBf5t+@QDJkZdVmr$_><#61WBW)*oa_lmebe zQt=>Dd*4+tdq>OhQ{DPRPrs>xfc<6_Qw#5h|+xMl>n=I$qb&q_DB5d%g>- zMB9bLqHJ`}rawrU!|VE(A=XuV1;?L3eY7I-gTzcm5}?$QZbXx^)RM0D0Pa%+83A`j zjY-U8gk3kRx^4)3HTb~j>g`Z8O^}gJ3ORBmF_RI-Fs!XESW;EfSzi}jH=9MZ`Wayz z#z-M4laJ^%rIvJIgw^>Nno$?PjiHuw6|~WYF`$j^aP$XBb2;jK>`+jUN0)nV%qhB~ z4Fmd>r)^MCtuEI0DjQ2J=_>Yvs~ADE$v{xO0Iz3$E?8Ev-|d1^RcB)uMqPa>0mign z8A%6fiG-rgff^}cV|B3~sdF*hf-aySs4u@RK!KoDNGQ(5uPJnDorxU)z=Mnm?J8WP zO@hs$-(s@9YE1zI)VbL1S8&OY%({vqX`8@m@Usy`pjE{lyvoK>OS+0N;oeHnWM~wb z93&Mt8MFX)o?YN4bvCy90XAXf3vxmCLkGZ|^6LT>0DH6xyS&cDc0XV!;{&3rb5agc zy4VH!xkw$chLdP8FDZ!+=d9G)U|r`v2yIBo>64q;o$C+i^BWKCdu4BC@*+$XqN8j$Pfd`FeQUdW ze{X*5<-LiC@%zV)?d{K<>CGQ~?Xj_qmu|eYg|9Dv>A(NCzxl8K<*&c^SAY5EfA)nx zeddq<=<|R0@BZ!Y|K9KZ>)-iTzxA8H@h?C3FMjQ_zxpfx?CD?rrKf)J7k>UTKlkaM z{nXEV@~1!XQ$P6=PyYCi{piPkU z;fEf&@b`cFb$|DFTkrp?zq#>?fAXKt{PpjDvGv8@{)?yH{@Fh}cgZjP>8pP5XP?>i z`=9uu5B>U&{^7a5{^%b(we;bCbKuXv`*)xIf%pE-u8DX2_Gj;W;x}7IAOH1FO+5O! zr{46?uYKX^^Pg?K;B~+9duJbbde<}azr4Ob`%Alia{3opORxH=kG=ktKY8HtS3KDoJ@(_5{pwvm);)Rj<4=9$ z@Q++^=+Fs{QbeFFZ;(IcJjwikO`PuoM$(9BF9+9DeL38`q z-Ls1`lY5yvHa;_0Xtiiijn;#T2a|l3*Po4b76zv!VE6!}jE@cGd*jpldJBX3*`@05 zz4YOrM?*6?+?$@5TiU&_G&7Ox8yw)#ns6rf2cx-_Oi%?Nn;aPIYwew#-%m5;==|(N zZ-L4?-CAf(v}RlLtseb1(wb~d(SNPh`~K;^))(n}Yip*pD4*TTS+us!EKW_)uj}~# zRjt+)txMLm)~&u`&HnxS*Ya0B+BzzNN%!chp0~b5SmY~@K935Qtmlu{zvj$q=&RK_ zH+PP{){{i*d9C&9>BoBdkN<4VFU}15(>*kM(rWQ!yywuJ$By9>nVZKB9=XH&v$gNe z!@Cb2sXw`!E;kTyZ1+quJ~ubjpBNwXXJ?W#;|t03_+&2`r_Tq2-t^oc8O)OUBULvz z(@XXyF7K)S$@%g5rKCSIF*`rsn;1+jja+(ksyDvSONc5s7@we+6vIpXn-`LW#ksjz zQmF&#y5!XCd@?;d-=j3>*J&xih*K zGQP0Tn?57A4Vfp^dBv-gcF_V*vr|Iux zX?AhmFY8EhaFCGb^z6cb-bcH?N2zml=6ef^Q?evXj8D2NW3U|Pyb8czNx9hCMb8o<5T- z%#BY_eMx+3ma+{R%x|xD)LvEHd+5Bo??HPH&QPIeXC`_h&%Ql4A1>P{=dqVk;_}rQ zOlo+i%i)VN<0q+@G;(T*gx1SBl$O%zlozSvgMBZe^jjUT7#eS3FnN)9Imeq@q|x?3 ze}1qyK2>)R_oQUN<)S$;r0vIMXLt?WP6LO2Bm=HuRj&39j#t-vcCI(W^I&0?+F`Qy zK!3uM)i&y#o1=L{BZgb1o|)3lZE2rH%28#A1Ucvr&X9VeO~>rS{jvP;cF{4q6%2LYpf^9=rwK)aj{BR3`{K;v0yp4;<6pxmPi;Rc zZP+;Odo$E)voq7ZnE};-Wu*-pWyj}FFS6i*>(f!0Pp3$U4S2%N&Gu&ol_ugOSiSXR z1h-m;`xEoC3$v#N$t(I&%e^moN!1>Qp!7%uw?;-rTDQ*4&z>eaFwJe!#)~ue&&)nJ za~t)aHs5|;7O%hbQd&${Y$vJe)_&5Llf9!{hTc5={qolS*4?cm^zWfo+Un2>vyXl| zL?Nx#zFsJM`_#hi^Sxe+eje)$UNJtkD8Jv~{l0sO1_#Go+iE@ab^Hr`y@-BagI`#y4M4KLh$^)=sk?TfDaN6Ga!eAA6vZu;h}H^2BLw`?2PKH4UfhHj5{ckKMfx4!hY zZ`rkb&)$9eZ@=Tf!I!=K(BUI@9=+@CWA_|?#VcQR@3+4C#C`wQ*lWg5PE7Vroj%il z?fp~JGqZDN=NATx4?K8o>2=@s?dQMapS0FpxbTJx4_%;z_mVeUc*DaNu3Ed6R^CS+ zeUutxbo9c}3y;w%y7im8M_+k?)>T@3j~;!Ds-JAV@Yn^4)uQG1%$YOm*FW#v+}wk6 zbHTbx4XyivSJDzP`{1#GD5n;Eete-ff9J{9(tJKxFJ`+bJqkNCzA)HLYrzB5?Q@HB z@)`a{GOfe3(-=S9+cSG^ckB4E{dcFGk$s0S1Sp07v%iv7{NGmEbK$=@r0MSiv;(`? ze~+~6f1&(Ct@^)Jt!MY2pTfhf`oGn!XZN3rpZuTQ`BHn^t7raKkAPJVRsXGemS3(1 z`RBu{>i<@+dUpT0__p@X^^@gh0OxcS{pWJzzv|zwXg$jct5_`m>}p#}UD2w4$8W31 zifXkkJoNDEA9?f*Z+z_WH@*2SPrUVQZ-2)-zw=%1e$RW~_x|tt?hkzMd%pKW-}m9~ z|Huz~^anroLqGf@AOF!G`|&4#;wOLV6F>dQpZV0!e){J=^Yg#(i%SzD?ul^RNH!-~GLR^ZWny5B}XB{`=4W(SP{kXa3|*|DP}X z*?;`=zxd0)`hUOppZ@wk|Cj&z|NYJ1{dne}?$?)f}myOTJuapm`-=cIm%@(D_>CpEK#arSuI6lYaIIUyjgT?tt z3gGnS`|{g8XBOud^s`s?Ch0a0{+3Pk0i;!!E7uvBI$I@N$HLSK9DJaXspdv4@kX}xc?9=_0O zy@|eC7g`tIOh4%}-eO+Zz@O8{yn&?OFOZ4M;TPUae?LxPm(cfW`lkO|m(o8R&hdDo z$?0A~--HwK=dI_{_a^$jmcG}}_eT1DA$_l=@5|}?8|ixkeQ&03-m_m#-@L!Sj=o<& z--ICXKA&`Vt92QDyDv_g!&vr8`es?qm$t*C<&hTW%jt5yTn^6n>*<@zz-e(AI9)Es zH_$higUc<7nSSyY|ITupHmAd1@n>9?Eu-CY3H`j3{s8ty*7xj0GC9_t*|T`+6pvPi z$r1Y9@VS$(u2V+x_2&ZZiTDAAan5*Iv`knXO~Pp3zE8|{+VRiz<_G=WLTm5Q;{r|+ zAk$+@nGEKHaLsu(o4IaLIDw%fom0vEd$unmT;0|k!{c*s96mfgu+QYq2YT~U<8zd4 zbBU__hRH`cm^SIpyu7z`gpnfCS5*L&JMJ$(jLMMc$wAUwN~w1xgQkrNMtK}rWJR^W zhcWjE3bK$K-G6to=g^&dU!HhhNA`@}bKvg%yZ0UQKHRb>{ljhCkj@^)Qx-gN{+vz zc0N3;ouxb-o*$=saUnDf{=w<&J+hAy+kMCWxa%6~hbCW0T*_Wj@@rVXv13Q}YT6vC z>1~-z2;(EnW@j8*gcf)*ip*gK-PGito!?7G83RP2P!bXu#(ftF9-A1S8$a2f>JOGQ z)h&}FTP9^*Lym{a-3+8fUle+Oe`=PF)&{S;6lo4-2jf$B$VFm0n(#_VvL|U|EhJl^ zTkep*Zgyo^I5R%qo1`d5d-F1?UDP<<%foo{z0(VE6e%StAmx8>e17g24Rn`eRSQpM znsV+BCeB<`X8fE^;3!9JES{vTqu(MwT2~O7cHvI`oz4X5tH1l(uKKY%{^)g&d<%ZA z+5OX>-qpHCzk4qk9UVnP{`aZxSaoza{k-tyFW<5I;6l}px6=_30l`NJ%O{wS1YW-X z?j!pTr5%pc{-(Q*>^{Oj4<5N){yunQpZq)~pC4eMukb7TseP>{zki>sk#`^4cWnKx zxBSK@|M)-c`MJOT)!X0p>6?1IexBNSf)S(>$9fZsC-%-S%?)Nx92j3XbK=;6-AS6z zNg6LZC-%}IJZ+o?y%VGDZHXMRC7lycK>Fhzf(lQ(VlO_EKklZZYC4|@J{*~wJn2{G z^Dk?)KK^j4^%p;EfAr+8)=$tkf4T6dqI{lrDh&TkFZ{g}_G3}_TfFeE@9%28kiPlL zrSHe*&wuN+yZ&c;R{~a5wYIl-DoZo+VmVzy)YMAZd!N1cIeR|HQNfFff`U^*augB~ z1r*8jFr?&&ibLi|isY`AOpY$65$+xHj-Vsh$U}eD3r|=k-RC#ZiYl{n2)c$5u~& zv^@RpzfM=Ps;56X&YYRm(;uyW=%a$p*jP^ zx%u;G`*k-~Pp?{@l)JmRdV1A$*5-*2rV%J2xe zyT(A}Abl2)2E+mbfbM_-yaqG^>Hrr}_s@XGY6Ddt@Bv-}6rd~c7LWpD1G&I9-~ez5 zsDpNz0j~jOAQ*@M;(!!jGO!d_2W$gMfWyEgpb%|)t~F3?0W%N*i~{VyEMPH^4;%t6 z0n|DJ)db)GE6@)Z0>lI3fa$;jU=2_J900xrZUDsX!WcIJ^MT30I3OMv0CWWepe^79 z+y(E?0;NDcunL$CqySL>ab4YSEDnPcwrwzT5z7+U&%5jojui6dHfRuu?Hz|lehln= zqdG?>c7%NoFZ6FZ@&`r2ZXHcg>5dT0OiA&6E`u80`JAPDxytszJH5vvah+XeZ}Rq6 zJ@>O1PjqijgbP&|nJ@rp(bal|<UMzC>ops$I`MQmWN{Y1w;0YdU3!|tOhLBhrPEEW$kVZBseo;}_ zn&70zP^;=yly6`=#Dw{e!i5Hytg%5L*9$1>M~@&|qNz2dLIz%4}%BYI$PvmmYx$RvT_x48;{P*UoE+gEQ`#3p4a_TIL;WE5zwltS#}e zVTHPLIT=bVchu3#<_u0Vgn>OaGEz!*j6X5T$!LnY`H=S{y*u{**d20)(AWg9!)Z-r zNyt?*Py85?GsQ?kj-C~J1d@3m+j(15VBFwQHezkYj;S^>6I-84*49?xpL53>7)4P9 zRZHW%$zA%oM-Z&gE(0_AwFUWHH&-6zfnQuyNPJA3<8_3pAgwxw=!~GsNV|@?9vg3? zL?;(;NRbpzHkN)=u7hJ_D-X?4iwvpLL+iRzc_jEzMWmL;6R5vDp+p#!VhD?gviai` zBy@!1#fiKFQ#=28Jy`dlwR!Uei2nrE9A+;D_s?K*$9pI>t9Gx%*xqqtV7{d)NSPqV zg&HSgsB}k-&PyAl{G2Lb2rc;KSF3YuQbIK0uJf=8QhLrK$iG`;ypwa7xhAzMM;|)E z%@XQ-)m#|8>YNv%szWhPC}*i2gFI)*+>*8`uM7B7{;?wxT)KlwbFOwMQgY42kNTiW zf1T$FWE4}YP-0*e}ap`C!glCyGyTx4sN(bFW=P zYxlVHJ2@Y?-Y>ilS{-!}T8nhv?L6I{hm^dpRn77+#pxsI1U0UP1HY96WIc7iXxYB8 zagp^}rjY{0&Bt0D<=@|fNXC}}NSnLWf110@g4v_X% z0wmv7fb{PGK+?+qlHLeRA^agAe5oq#wGbxGO`V~K;eQwZr1U4%^Q8RlcsF&I|G%V! zhxX}K2hT!5$=tLBC3BWF&FbJ|6e!_22TInGC+MF*%coi$Yj+JOv1es~HUSL+CH5{a zP_ibjOtCubT&19|ARG*3_nhcr(8V!06Gz#3haT2%?%b0vT{x?FveExHH}Qwh z_{1gPQgM$>ePzTSerW$m2mi~Lg7;SJM?DO6mO~Lmq>W7@dXKo=Gfui}g_EW(IZIR> zWf^aG&^+Yd`O)!@yq(F9{Z0M5Gs@zkTYh7HKUe-wk^f*$c}^4RLQX|a|0mZ!ewm|0 zPi$0r9)I(=H#O+^+sE0*^B*~O`s^LpU&cFoX3e%b&NlsBIsdnweE|j5YJK^=Vbae> z``W1RVH=iNR;pyKl?s?|rNVbusbI8E@)Jtt+U>yJYL1la2%f|*uu`!=G!O+u00V$< zAQ%V$EC2_z1-t<-z!Rtk)By|t1>F0ynoYJ2m5%I3 zsyaJ1mL&hR>rT5>a7bv?2C49ws=eY>v?PUE{d@n8hF3;g(lN`G`^f} z89BFXj(`29rnOtY*m9?6sr71VgBhu5f3*}TU*#Tr5co>+t|OKapPilhy?uX|ZL{#myNx*Qm-w^eIu@Loe)`U$UoVee)zg33#nIla>y3)btWhi=OjJFRWL@nE*zs^w?j7HpH5 z$SEm$WbeK^$8QW7*I@e~z3d0yX-^laGW&(iREmPreHwNDi|wuZ=`+?po&D~M z!DdEQgW_xTgb?(^yA6-6%BRYH<$L8v@D_Lh=89{?4dPDmZ{i{GxcGzklXzP+NDZYX(ks&Ik|Oy@ou!`Ao6=w@ zT52J`DwoQK<*(&V%1mXha#RUad#EAmF7=37uHIJvt{SxGG*9g%t(oSdwbeRkoF-`& zt+N)S_0qz%H?=73Z7oG>qqDlM_tcl_x%yW93;m3a0Yf;vu>O5%p6*Rgr5DlH>AQ3r zrXv%;^kl-B6y_V|9CMR-z!+FBwmI9HrCE{ffsrP%b~clp&#q;!vUk~5#&$-|C>q0z zb-0FHb8Z2*jN8q9&Y5|Q59f#SiM*Z9;^*T){Q=w_M>A1;cPBX7G=bMYnd(CC$Gv-TXS+s~<#SHO%F-x2)){`1YjilyM zE9o_fk<5|~j&+klrT*aAFeyQrAZ1F^q}kFUDOcJk?T~g!hov&U zI?A7ww#p0YM)jb2OYN;iYv~$zVsY+^4={yn05^tP$W0L5F;6h3nlN$*KH(haFpJ}O_44N89{UCG3ZeWa{XHY=xM(VN+DfxxmcmH8SkSF77Ypb-dL3Ote?gz5FVHvW`}7Np57Qn$-y$*^ z(}js*-o`3SVbYjPW)|}SvzFP+Y-388gUnIp6myBW#yrP1Wm~W;JC8LAx-d$ZFB}$L zHeE8`GT%49Dh?ErM7y|LTq|xCcZl~z4{)%DlqgM-mVi@VN%y2@WmY!J{*b6Bd6+yw zo+z)CKamUN&*d}nMft8=OKGgMR$f<3N@pcV>8(U6Ba|d1Rhgt@D~pvC$_}MeIij3Z zYO9S@O&zahsO!|t>LIm4t*>QjtFz(yTeTW{XC+hF$sd}b9U7xEj(sT4x z`X_pkzDM7$AJmWN-{`0Ha{YV#Hd(I`(A6OI>Be+(x*gqt7HN(C3*DCyv+QG z`6;okI9i+_P7!xOqRT{2sj<{r(xgFHiL=1f<Mn#<|8L#!JS5d>o(0Z-LBx!GF!4<-LSggmywl zAxIb`qzl`GA0cz2OfyVnCV%r=khAmVpUri|mg4K85wbQIW6Bh#i}S>V;%f1*_$#E& zSGp-Rmt*8DSl>a)Cg|AX%1f$6>!MM1r!EPi-=>T4lbtFXXk1`CXsj^)X#B-^&xlJA zTzf8r8_6Ye<2XB)!DVt&xhyW5JI$4Y|5vzcTpvD?AIGQjnfz4fi;esV{&~R*nxdKD zEwmEa3UA7J>v`Fru2__OFMNzy=Q(+=|6@)9{;E|T{_uO5P|-j`b{eo6qw6Rdhc-FOktH!B0P;> zbiQEn6!(h9L{$ouc1kCta;XzWzFVg@zz%|XDf&6u2l6%na@K=sOcuuS5wnij z&3w)rVlFbZ*j(1jINmtj__1-n@fD6no0GV2xvN}Lz8(HIbZHJPg59X z>@8D_=^fK5bFkD0`a4pxL2gD!Nzzzo@pS0%>5!uN(n9FgTxpH8LE10bAUPgHR*GT& z0U2O?nXBNh^M4jvVvQJui9!ccFL9r|L)oh&tNpZ}v^&~|`Z9eZnUh1X)qwPbbk#*~ zpJfZ!#YPjSa%Z>;+%0YnzlJ}_$KVIbgT)V(CD7h0mDS36`VrDmCaCggTo$p%zV;J zEyaC1g<|2XK$*Nv7VtE}WH&7}6_|=m)CPmYP8CiMfJTX;rvNZwQ-8*MEY z@`N?Q24S<1FBG8VJwkwNfo98BN|a+*pY@>oo2lMvE48gktDG9FhPvb@N{v=y)p#{g zO;*RLc03(2)v0Qhnyt=P7phCu(QRH7u|?%Mtjo=jffgo!+{zO)Nr7N T12r6|;Xn-sYB=y;#eshS-50mr literal 0 HcmV?d00001 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.exe.intermediate.manifest b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.exe.intermediate.manifest new file mode 100644 index 0000000..1c06b61 --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.exe.intermediate.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.pdb b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.pdb new file mode 100644 index 0000000000000000000000000000000000000000..ccfece5c9a76e9fb4d8578672c116552025ca522 GIT binary patch literal 1084416 zcmeF44PaK&|G@8hUGH0>Bn%i7Gl{-5u;_ij(yu6ne*{D1e>cc15;d+)jDo_p^(=leb1 zbM63Fw#QxU&h>^xcZ|OH;;;c}abcHq2oG;mzhz9C#!^eug1~t&sai` z?wIGy%yD|M^4-}Zw3_Ty2N5lyB_c~G@)A)HY>4a|TUb(H ztZOx7eU!7%tH-4Uu^#7g=I0pek*xQ17CJqy?37Fom*;hQ%wub@EuoNr+{|pJzJH{| zKfvkDbLVK1{u*lCol}zURO?zW+sh+T*W;6YQatV=r^jnvqZUS3s?%HIDIAoUU*a^c zDOmPRQx~eo@6&vQr02PcwGLX{Q_0mDUh=SJh$eeRdA*+WOwVwqR~sC0)Ouv3)=#V1 zRMM#SOmatMXFJJKJ?~V$r*aF8b#)zsJuWg*rMafWF58VTIX8Dtox8SiZWUkpm?Ecf z?y_~x-P1UCEnkp!qzQ2)xz+X5@`dQ@12V^C8Rx3y(}L7KLwUBiw{k90t zAp5O@IdEHJO`C$0vTYda>HMC~_3UJOB$D4s)A9&kNO&t`CEFs|w>8Y+D#A`Ag!)7f_tzZWAKzvmKPI$X>#i}GDq&uH@_Var`X=y{+F6Wnf72ZtK z5}Ohy`%9iJMH-$(esEkfWC-gbl8?dcpU6Fwd$*MR=W^U!_IZWtk$c>ZZTW;t-W8EP zN#hXqT}9X|o|{Cflv38Wk`KaJ7wG3a#BVwGeq<+UZH9y)dkD|xx@B2)5m`?jg&?6w zQ{)tctk4?5N&L7CnT*_yOhN8I?nI^{(~!H6yODd4>BtP^UgSPxCUQUW0P-L*3wa2c zjm$yjA`c^vAoGy<$fL*t!a%>@>E=b#P~0OFhvml!8pFD=&m9UrZid&LkIkT?MYOuEOD@p`gf>Pc7Y93*m|2 zd68xZ%Ui~Md;_-UzRSWUJq2-&=#+t+L2DXPs&7(%jor#Z8f$ezOE^AMEA~pOqOsN> zbPtWh4(!+`no3ElQ+D1p#(Al8#&9(me ziP>s~wa{FJeM)k3xnx>b4b(MD8$h+a9JF<^WQ(4*{VJ8JeOdJ5noHAeRpC=pvQ{i!Ip>=7M?z-Tz4)|| znzmMj@4qo|NsTUNJ#$V<@vO$fyA0rdagLCn2?K6%Nxve=z$9@2^LNYx6zLXVi-S*VqI16!jf{>U^Gg75?d~HN%^H)Zno#qZ)nC zedonlyz?r2<^Fve-7yKH3mOG|effdf#NUhr`Q6G0CsTdWsvQmG=8cCGecJU<^6>^J8Smr&EXc9GSKv+X4LA{! z<=V~gLwF0^49RQXS8x*C18;*r!^!a9@OBtPTBg8y@DA7*-U-iyQ{nk=8f*{mg5BWV z@G^K0jDyqRAUFfceYqFrL2i?8B%BGy!u#P3@BugpJ_zrIT$Aqs_z;{2XTwEs4qO7~ z!)0&*d>t-?>*3?@bND3O1{cBa;ZyKO_%z%HpMgi|n_$Dlb ztKb;;7Q6wz4R3>M;O&ri!*>_t9{V1I@4|=RIyeumhl}BRa0z@LJ`Xp*H{b_w73A6Q zy$wHt8{tOy3H%s-20wwj;HPjm+yuXapTUEWXUg{`HMbD7y(xh1tfymOC{Rq;!LM}_|31eU{I2gtl>&qxiwjqa*W}$i;i&{`iL5h%> zh_ne;A{&vN$bKZaDQQJ|A=SBXiGOPSUw|g1Du2#@Kc%`mHAY+~|;?`@iqlwNGa4_YQ3S=+iT2|C_?*92Gu(MM3G)bFV)1 z$&9&Yt$*j54`^Sg@Z~pMG-z<0!KcIw|G7<%mz!UtX%kfVwX+|8`4?CI4_Tq{d-Ic9 zQ0d6K5)yPv@ap?_-_vx}_C^hw|5*Q)VVd@}3jd+okgqSCJS+9m(C}^_M{IwScZZiJ zBwc4(I$?@EGH+0i3?7Za552?hiRpGB+^Iq__ zn`3u1y=PSZGm}l!Y_U-^6D-1zFP8l!K~4t zgU3;EA6DTxjqm<#`|kO}uH7`D>5z!5N37c}y?LlYg6e#*|AGxuS4QPD4sZLw>ucLv zw`s0d`)~a`r+e1K`#RtE)PFx&xO3Px+WRUzrAsEY^?x2uPaS)0pO#%ZRbN}@Efs&C z_g~*r`@P54y|ehMN0*=V1P`@U8|(bKa$N&PJ{Vu`(@7sYIz85+=_zMX$&XXvFDxm% zZ`tS%GwygK`Ib6Ohf+C78OZH4-YaYc{FeHt9!y7MX-*wMGBnyrK2&P$OvtOKahX0I zS*;6f3A@6xVGr0E_JS9{-Y^^vp^Pg+CLv~dIFsLInYfVOQZ7oF_zH45$4I=K7dwhN zFYi2MJQ9YSgPe<;hsgQPN924LB5e>JYjr&r!*&R_7aIt+Pejr~`f2y8^sDlo^lROa zOOWme*FjmPX_q2gi(H3x8FD!ii9{iNkZ2?ZiACa&zDPWhfb>K9BZ4bW z4VjGGj!Z%BK<-4QBGZt&kh_t4km<+_e3qFdFOq0hMh4>Hxn#5^p!7O_NbW-|#l^*X8GH za1-&0J(Qo4g?&>7I?|$DMR}tAi+!P$UtH{Z)FO6+-CUR0Hu4;S`^SE^ox*+e{>UVp-bC z!{yqh5bl3%zOv)XuR)HVRCauMcvHfs6aEtYcSKA=O|C>(RolFeynR)W?aL?Bv_uq^WYY)H-!r_u7R85D7%L7 za>2$@@&3rUWRn;1ICDy};qj*5DxLc!LY^zT&Hm47v)gU+-*205=4A^B+WpDbE5@X6 z$UF3U;^}v$UOMgf+van56^uGh)rA`z(HP3%ALGZ+Rp!?tiMjDTOjSokFz z3b#RN=WU15K0~kc-2-<(X|wEvhaff`AGR887sTeGeFHnd-LMb*7D}B=pOmtra2aO( zsOn8gC)Jv27Xs;<@CoUk@JYG33Z4d6!*k(VupN9Gc8A!Ud@*n>OoH#gE8)8^7h;R@ z6~pyV+N1A5X=lCXTdXJ8+bO9 z^qdFdVQZKQFMv|fUIa(Nwouy17eh%$J1FVs2p@y=+xeb_o!~0i8A@9aTefc#yaeur zm%>oec{#-PrA5J97z6WRESwG#;KQ&Vd=gR*`<{mbptLKJ;XdLE4Yf?wc^`ApH z_BOR(yHxk%W!R7qY?w-S!*;2jpEd9@!ry^c!1rJ#d>@X0AHeG&Hcp?k?>~mpe#h47 zdjft6r9HhFO51n~+yJ*iX}5k3<+`^+N$*!Mh_rnTu_bEXK*^7P!AST6l>FEai{Y#mHqm^@C(9^!0#aSiSHL!1B!jRCX}{HZ6lwg;xp=SRkuq%oylRUE`lS7 zN1j{yEqyH@{jKV`y$*I_-3Kp)6QJbNL^urI46lW^K*^_Dp*$axq3AZ)oPG1*6u1oD z0art8(7w0f-S9&=18#-)!Jpv+ur6ty4Y8SOkH9nFe3%LsKzTnOgFg5qyag_T)8Nza zA@~fGHq5h7@_#X6v;#D0A4on(TT0R>OIP-j_vJEpD(nN#hS9JCjDy`_UnuWwf0zQX z(fh7~DNx>4@%gq3SpunN?nn{c_}h!=l%J83g?jakf6#o|3GcXR{i>c_TLX9FG7MU z`wdWc6JEJrU;Ls%f+jVd^g!>yTb7^dZP)TZ|KB?5HfpIGjeD-1h3>>H?cPO*ESFOS zOT82aDaysx>XZ7LI!LuEQ{gn$2f}%97;HlvIWQU)Kq>b}La7%^;bC|qTuFLwhSGm~ z7rdsX-v2%x&S9Olv(%5ipCIY+J*OLxQTd&Cli7 zKhpl^)TCYa|NHa&f8_nggw-Lt#NQSe6!yn_fxf8SJ5$1t6H;+Txtr1;S8+*Zeps5fB**0r>(L<`@289`S7AnWeg|i%Q_C*M(b~u5oO=>e zm@z85M|g*#oUC)h+GmHgPfrPJpFoPE2gXEYxC*oLOLCl*Lo%{Uaxy($Hk{0)k)pv} z!rB+Q^WDSUVeOMUhqb@b71n-0dgTP2RRFUZX{U$J9F@y8SX`$txK(}!pb)S$@f7?T`5 za6s(!ojMLLaq+)nVsdm;VjAn|&Vr(j-h!giT(2WsmYnQx{Rh9>cMf-yH=0ygjq7EJ z*Xiigse{XPi(;n4eY(w(JVN9*DON2l=Cj@G=*`qhQErf;}m=yvvR z(XaCU{`;BnMy?C0GTu)9@pdYYcNvat71L1`FZ;FR{5v@R5Yk?g^cHzbV+*|=7b7I- zD`*({HQPidm2KH2)G}pT_3gXZ-rCrnkl_v48m!%eI%FXoZ{ce)Qgq_PdzhirgvNY#p#$|e=CF$`v6El29#j%%t zw#|@nH4b_?7Sb_rKl)v8l;15!c2Q9$hs)*Y z+9fNeYee@hT(8`tUrEC>e(U#0>R5-Xv%ARU$Q9p#kWe;hhs2P`BFD(m85xYRGm?BF z`Pq5S>=9CZ$9g;re3E#N_9K>KjD;4l^rzGgEmp^dv`$QlQ0tzu^{%SFMcR7f*{P#q<(%)Z%|1 z8X70)&mNK&35e*i8}##xaC(eKk#LDu^0|sdJrDl6s!=|M%f9}3VT5;*a7l-GPi9)A zBkXjQuJUx~-G3(~Ev9bYrjevh_mA<3L31mhufz&w`$w zlU~9c&+}yFos8{~r4SLj$OvR8@4v65-@9cr{ft{#>3LNT6UUj0|8ph&1ZID<|8wQv zPc7l{ILW(Lv0qx=T?x#vsQu%A&zE|tz97O~}v-hK_diuCWbN`DpJ1Cn22127jk zr~lHFa&r)!Jwck3n_YUyHVKP5!wid1@5jl$ZYeh0kOU5`<28?S|O8ZqLKJn_#5|KH~I|AJve&y%jXwdLgp#O8bPo8a_!L_fGXmHiZ|n&NJkbn3a@u6Q0!{B;&4*VF3 z5B=wmeiz@@ur>S7S#J+} zK>C+`QLrOSfNb*(g`HqF6kq%iunY9U2zVXr3U7qn;O+1dDDMq6LS@S%Zr=jd#drT1 zcqx1yM#7gN{n@@%Fa~abv2Yvg3wOYHxEGQpHO8eM{Dt-Yum%rEBCG`mz`8IAN?&U- z6hGY*csjfSUI6KzQ)67x;Ki(`!|re(>3r8x}(Phc!2(zgH`QGR9>jq@P#wz;7Y_wQ7tD_s&N@u2ur+r`1Nm z-{5E{jnqmru^~spZ*>E+S1Mx4==0f}kv`64ZI3MC)p*;%mf6#cAd_P0_O?`*qLRg1)@o^~Q zO`m|$fLjDxz^9>%apBqXg~7$}68J3a1DC)7@Hv zy9K@i?}RJhboeTK2)+TOy}uetc@c>iZExiZEaioiGZBa^@30?_wYDBo#%LeH!Ehs# z_U;xa@q{Br`&g6ue;GnHXsr=hwoxnzw?irZzJjW};RX)*jer~(+#{k-P;!lt; z`gG0xjdp33`_q4~_Gf%o+5Se`vC943#{LV9{lhrdO1*8Te1N;NF4r%0wJa3jD&9zb z8|^C9-l~{yZhQ#XMH>mJMzv9JD7+p@yUYjWdVGk{zESNSiAS28UPPA3>?duZyP%ZU zj7Ro`!F%8pa29mJIdCj|1O}7f1&}ga#uoc1D`i} zBjg=Xeg2{hO-E#5jIm0`8&J~mCJckCprqp+m<@SXR64}><`&jBKq>z}fVGL2cSrdo zY=T``7oVg^_&KC3RK7`4p3DB@q3m}D+y=K2&pO(I4QZ=IAUVhsf;b9W@t-G=I-ZE${g9p9kdc77t0) z?DoIi{*R!&V7LFPZvP+a{SS05ptf2KX(Fg`V8YJSlR=`rO5vgp?1*9fipM=hw`2TX znej)@eNax!a#`hbM`J-@W{;D1ZZGW;SsEX8Zsz(bit-FEk$Iu2|J>&qdA{2?cR&Ag z4{#O~Geelz)J{fT=SahxrF(^QW*3c#DjriP&A)0tw|TvrjB~fEeD0HWPW-3Ul5*5J zGxM1W@1&h`rE$(+|8pj0mK0{^Rr@)s^BK9ocrGVOA;Xg4pZ<7vv6pT)vaQ;$!7PK6 z(#m-c(S|Q8gZetXiJ8S-vrp}0y%*y}VP^TY!nxIXDF>O%sM@c)vytCY+{!Y@Kfk$d zy;Ihhj8OF^SAX7{_q^0dLzaIU;&A(t`%c=bJKxPRVUKa%n2P5;3FkDQvpiA8SuXHD zC!Ptp_*;0K(G(8Vf4%0t-ea7*hI#IaJ&|;@>z$F+pN6vQH*D+8j!uKwxjrb>TKBJxI3!2>Z4Y+)Uvfki-RfdB|EgK{+3kOHO!OUek7_UP z%{>19*8cy!_dn40&#DZtf7skGTSs9ayjyCd!ZhPx2Ij{Tg7{eO_r|HtGsIXTR{;B~w6i!-trvYwTn zlaUsi80Q$$y_*DN6nU6Wz(uJ3Hz6rKHZh~X>)>-<8QFPiEWanhe@-85*dLqIr*Yl# zIem^!RCyZQUZ%e;=JYZ-4~b_w0p@*h7I!G^-p%9mawS=Aw^vQWWz*4gVoDlUd;T!*O7a+f^vyw1`B6RM*q9Z9eE;%(i_NZ_ot16uMa2ZdGjY@JvC6NYxNZfibe`Asd zr}Yjm?HW!t$H>2=nAZn6JjN(TX?u0Eqllbh$SH@jlKCDotzYtke-m76zz=Xt@0a4NM2}!XINAJ}10qV$@m;}e5*wi$R(9hO6ypxLFkrtmIIhqs`+h<^3 zemIh12RoRtMa7pK7ncy75S6G$tIpgfHa$xHh#Mj?I#N;-21TWFFz0oorKk3eiQ&d| z>k>g+1JzB8O&a8gOHGI+okM%`@z_jPKHE6FPijr+`Jm{)Rifm`_|{!T4WF)m+LCA^!yy<4~*-mSC5QPho>D2{S+@DO6< zrMa@GJEeFWGA68jp4(fL>CNV`l-bCnIMpXks`Q5ZPA(L)0@ikzjso>nVLXK@MkWwzJ6VFsouUmjhFWlr1t2(|?h94)S}+ zv1;yvPO?qH63fFnOPGxF7ylmz13qKbIq<4Wb!NLgIh;eE-=SExG2u&AzN?s;IZnQ) zZ5|uKk6YQUJ|bG`HI9@yO!PSqYSIviaTTk3;q(}Lag59p!1M=4!uXx)93|grRu24f zoXj~pkvP>O!{k5t&a!A#dS%{(b2%=S+3(!Nc-$sBM>+AulsM(P7FTnuoRe+JM^NIF z?_|u8>oBh|#jW1BV&ZA6^$7jfrIJ^Sm1ZCDVcpN~ZKQoHgYrXy9{zsw<-Sv54|Vw2 zb!WeU-~5YjgQ)P2-ye{a(do_I-JVGA-2a|4xoOK(_|LQNx#!~T>9gY|yxMN@q4R%b zY@-T)^779YIQxEk^G*E+pZokr4LHd`6~3cK!8h~XYy8P~t})M!A6ah+-@#)NfRLc2 zua0_R*W#=V9j@(pk$22D>lpi>!nfQr|37!OZuV-%=NF#Q;)WLKjNw+{^MmjC@Qq~O zWif|$tXkbQ_%7y?R^dO-du(mNIp1f#(E5%4mj24PW^J(wzrDfK;F%Atjc?L2ZP~?< zL1E0-t-@FJY@AZ>-MhZHqWhOa-`&0ZHu8oW9TIfaWu1zj|7>jDM?1FXHktinD!ybY zyjRcHwzpk;UE9Y?cF(-~;bm(WE3d-Gw?9<>rFCOZd2;EXhgXfxPG&5)3V->_IW;D9 zxOV!y=DBfiE)M4=+@ZoxS^Ji+%aH?)1<9`QnS)ArFrQc9+Sa!ZJh#r(cHEc`*F3go z3HM08X&e&NsyOHI)qS6v`>zkSUN!Z*v4xDqRN;r>raT+})6&c4jLmrN;r5>~xlk)c zuY?4x3-!$B9q@e~A5-_s&S3YPoJMxyj8q>!me?3AN=ZvFZMDvU4_3}=eGSX z#;qQ^@9@pPWOSOtMCMPa@JX>XV!ZReIi*$GR!#o()mPh@_gsbla@VqrTc5u@^qYDQ zpT21I+{v`mo631x_Ztv?*QL`V{ySvFEu(9^(Uvi$DtyM|iC2$mzP)kD>#cf^tn(=0 zQ&sq;2`g{gne~IO{uzBgKI8gG(*L3gANAe-Q>KPK{n)h5TbD;Z`_@+OmkQr{UH{!r zZg}Cq)BRq({l0frtzh1B6`q{A_u?(Vz4J~j@Jz@({Dp`2odLZeK{b~?IQgR??OM0r z6gBnARo`BV55EfE+u){42HiL9vbEhVK6}=P?GqU9qr&G!w0(I&wzlKESDW5f2+}B<7z-QBaFMHn2+rijz74FJ&zrU;Bp2R(4dYqEo|BC^P>r~-8N8IxI zE1MU;e&OKty8Wk8@vC_@NoUw5lJH{bBm9u33$u>Mv)k zunHf(b=uIEhK?;<@2OjNe$R`tc%Dy{^Ol6&v}5$po2Nc>cgx*{cm47O&$9~uI%vt6+XND)9aevapQrw(a-g}b?Wk$`NowBe`dtF+wXnw>yd|E z>lSy_lWXo|Y`+TsZQBcxPY=x6vF+tmTca+>f1k0=Dtz1BtNP!V_)yAQ{ffR>JmSza z$^jMri?{37@78>--qgJ8N0;9DEdBY-dDcUMK6+vD!5@aDyt{nVb>}};+~Fff>4%WwHscrzrVv!;b1;YcKsj(Cx2$b4ie zvI^OT97Gy+VP7N?$w2avNyuX2ngtgj>yT~8L8M^>=R&$8@yHNFK0vS#*@)~x4j~P@ zGS2`KfutbA5HB(fnTyE0x@+JjWG@ojjd@Ryc1R*J4EbMZ|BL>gT0Q!|v$VLR$Y1vt zJH)YU5d$)_J?^lm;$mk3^|yy39UjF0HkSt?!D;e}O&`uS|;h$J%!K8$G-W=A2IJ zj5yEJqgLN7lsKO^kIT#|bbAVz91&HZ0HcR$z3A%X=bDs3D(cb zwMyH`Ja(yZY?yJZP>x+^rgeC}JIn82ee6)}&>9aK*CLsWN=#~rS#Q72GtQe|;>d9q zIyLit@!b}+F!e$i0oyW4>!U~^2+mA+T44La1~i!x7zn%BaG`XqfzbiE6c;xN9N`DPmQoluYS$@ zIbSLA0Qx~lP{a54ZZAsPyI@iBWoZrf))~(n`6~RDOK(rSc-LdECBE0;j#FRiGK)4P z3PVWHl2K8uch8BPy`_2P_5)vxXh~h7!f)$w=aeg+U3u#-d*c^3ueGj-x>kh`yEtXd zcj><#UVY%%2Ol_`OdBYSb8oza@)c>(oiZC4f|MdNk)?>;{e^{~)M}1miMy3~Eq>KB&1e;;Tx*iKy+kJBmG2$va~MxD;%jBZCk!LO`;7gI zoq3LYcj54B3yO@&KZ5#8w27`cYLCi(6_3=p z;mFz+@;*1ed|YrwWQzk?onM-{Hn!;=>pE2Ox>*nM4v9hXky2zLG7VXbyn<{)yYdlKIEWfCYa`9iaRYO+CPD!!j44TACR*L@3YATJDjw z4;^W-@eXH^i!Wfvtya&9c`Wnzsik6DeWL%dlC^%v?&k;|F|GdT@|-QcaP-LL^A^Kn=e1w*hFw{#gHiQ@j8F z815F*xoh|TE9V282JHTSyZ>K(;OeM;{qjC;yZ^sp&wP2_*!}+}*8k6Rc62W59suQ) zV0C){$oFCNZ;?bw`s~ zb0nYWQ}M9>AGuCxa}Pt3RGa&y>vPuZNquk4j3+PK_1}yib_L$2Y1MZrw|A$s+b{Lg z9Zsto!^Q! zXFH@9Vz>W~)^p7J@MpIFD>c~c06Pb&F$b#M{?BCdw*CK@VvwYlB~)@r<#b^ zi=6|1mmHAxf70=f|Dg-8s;NI5xkX;S>*CQ{{wHGlf4YwK*dNZIPdoB=o$;e$?&Q0& zvJ1Q&igL1!v%Oz20%K+s+uU!P`>S!P55M=Q%qhfdP**wY509n3W5q#scC)?W{2XN& z`Zt`mqw3P7Vv7Ixj2*7O`-~lu0cBOyaktMaW?OlwPThHyRyVXQZQdGMbTaeq=#v96 zZBjp3>#S)VjBy3ZA)EbQL&SfEq4!!M$E|-+wb{*7Rca@TP2PVo+qm+5_pFj!HJ63# zQ-Tz`#yS%U`;75Ai`5tl(@!VIHQeQ8{DO?TXvTi=oVO@9Uz){bV-|eG_mpY_-_U4g z&x9wY-#>ImqkC6{u3gFZyH)r^=f|T~_dEZIYnLRwku&kLX_~f6g*Vvz_zQ#H==AM^ znG5<~*ZJ@a#yFlKZRtkmKX+)x*e`beI_17Ik396lS&TtZ;Y(bFLEU?feDd74ntXP_ zki2Nd%&YL=tCu{TF}U*%XI7It`<@p4n5NyV!hejsC)9On>Dz^|nXUH5l#=#&D*T;I zpRbM@`)K!D8~ya*IZvLR!Q2We{NN3>uFCm#lu_15|sDu=`&SFbZCSGQ$M#oe zG;4Yu*P5-uE6)KD=-e51t9aghvSZ8d_B`jF`f+gX<}tJPSkGY*=v*61RQ%1>7yhy> z*zMSPXzubpFP}BidJc{c)c%#{mXRD~}}3TamNi3vl~UO(@#2TKlg&@AU-7_Iide_qjZ zy*4bZ^?CI9a~i$;F1n%B+zK<){#lt%t!cD7R{PINoAUJS z{eouv-0{op)5pEX!0Yv=$aPhoBjZc8f8{wcnlPw2B&hOS7*Q&`X3WhI@8rIf{BxVI zYnFUjoWr0L6&}*~ntchGBgS}(u1fBinY~xju2bO`EG%eVQt?$Hs_^=?@9*B|fw1{K7YwUau&^89!C{il zLzgzbef+AA&YT=^Xvmf`Cce&?G0LOGh+Zb@W#wGPf{_i{d_)$CR9|fv0qes{Ak#N! z-C;IWvcj<}i{!iGtznWY1s99BQ-QU*zZQZ}Jd!IdJXMwx$sHruNWx}b#aQi=|`>Sf| z&(Zta8hZb-x>T?%ryM zr62B6=Y8kPk)xmMvE%7y60SYaWMS(kXiuLb=RNQ6C)sV5o!6~NgL6MPa9i~L!1cpG z?GaY`r26a~R&$GPQs+H=-052$y!!05SH2ea-kYaC zzK?nRlwNxL`mmLKBv8G#y7YM~{n^TX7pNV?%HCIx30p#fH0Qj12RD8E%oN9shh83# z{Z!!gK5l`UBSkL*^|G-DJ-;U5mLWR-f-Sca+kJmBtG_+X|KDPoe}COyz5`G_ z`hTg{oms3504LJ-Z|nb6d{xEP|84!>*8gq&|LD(|1UzL&#ZGM9v*>?J|JSRjYB2yD ztKT1WQCmahYNdnsP;Kd2D-T^(<3RZ2fB$gv`)9v=`3UW+gc5Zhd$fJwx;s0h{PgL?ujNJ0Pd}YL zX$xH`P@9HT->_9*`?IHtj#l}50w=o<+e*i@vVmCj!CUF(^HhFbJ@(0uXS5x*rYQV_ zBS+rom}}jK9jI<@rBerLE2}OY-%6La(#@@WZmeuCR=zk^w#47ly^p7ldpCM>MIAhl zer{6-|J#1;uVMvj>;Lv={$l=qpZTjw|37j6{~UL5L8dwNkDB*a8}`TM{;~c4Z5x34 zNT>a&&y)74&x&QhF~9mt-KJ}bw*SBSP-uQhWqZUw;Io|-n<)R2HUOilsum*vI{s-| z!_cAh=f~4|Ra0|%Jo5QVoJEgiA85&OUXGXk`g-)w#(qO~#hwh#MBKUpN=A{__mD3kAi4tIJT99HOd+O?dwC;hxZsAKA!gjvr!Do5%53+B9`8>)zN1aS^9;^b>a zk_DtBuz1aHrJl!ebr8CPwS3|ors55fXlb<&a~1KD^+zqH4@Sj&5j)RJcDB;Ltb7{+ z)xWIlBv$sNK=oEDyOWh2$;#ehWv>X-?qTICVrBQRvd>uAL*C}02no73@ztw0&+k&V z)rUpBmY4ho{~9YhiIq=?m3_?0p7u}i@vyQtTG{aewbxnMd921H1Zr2Yvg28ek*F?v zk(J%g%AR9oAG5NjS=r63>_Yn}pYjmBUSMn!zZX+b%h-twM3&Kpuf;eh;o~7DOzmbU zW#%ogHM|{O0I!4XjrET3R@Nhpbva)OV$=s}?1r3A+~8smSy}{XKH=$5>aLcs9y|j! zhG7u*Ani;j^^2^V@fvkPmGPd>e(bJY043fFp~Tw;O1$l$#M>51ys|Fw;$vde37Y!W zqNKN$Y)51%~hY7q$A4FyuceK*LmXlKc7?L`;OHy@0YFr+xow) z{||D+r6$<=f5ssDvmF(?=uhCY9k%|@_3dfCcA)xXpkpMg^h_)Jh?PFPOy%c{2VV2NbItHB-@P#Fz@A#e-m><4*sb_y<&nZ-6&rRCboz?aiQ@LjM|5bYh3t zA+cI^L5|j*E{sC?gr+;JeR3zn)t*1a$QXX~%|TN65gSiXY&<*nEs*d;`ICkVe~$iP zv495I***Za55V8t2cQ=3Z2;f@sk#YiG^!k~f}(uG2yi0f0G8PE0NC>Y#3ZFVY##vI z24LF&3JvFroU*wFZ5x2wTa@YjLtpKb8ceqV=+#v<83K-F2Qd4(R~miYXVJ&dkh+(? z5Ur3t>9`y8etW;9sXla6v$BOD@!F&pYSJGcslw`OJwrDhIdX(PO9=cofcw~Ps;)pFR@;GD$ zZ%IRs&{{rqo!Ut9&723pwz2&6^B3AS7Jc4|7Uzqtso{@H2R2$&5OP+-Q(yJY{)~s$ z%BR7$vAooJE{0Ou#`62~CQ4s5^Dh{61-?-Ho>W8A&L+GTzh$9F^hv!!z2Rdh5Os*= zi-ut^5n>+lN&h^Ls4op(1epU-yBJ;#JHT8>CE%mKTg|gL7Ro#fH$vRYd^bZ}sC;)p zx~+W=!ALkCM!{zxarvHy*FelwS|9noWW9g4I6$(*BJxw(WW_YrGKT7Ue}VF#b1=13WgmCL zWWW*nBD%g)9jBo%d&Wqp`CS0f#j~??J^4C)AI-~oL{Bzt5L1i@r9CKV>4%JBpNEaK z7&eIGPRkP&(qj6`tF*XF3Pp#n%2pt{{921NwiCxqmLx>dC~1|s|GziV=y8^3SjDjh zRpr;O71C(tm$ExBrJ_~(nqR5g8q7RIP0@mBG+Fs!1lrGivi&x!{4lC(d_bT&zm=ba zmEVJvpM;g)!v>{$*PPaG&ykzQCiJ}9vHk9;&!B)<>E2d;41xL|Soy(N`5&BYKNu_j zhCp@fK)*E*sNam0e~gvCib+J;ws%(;@8V&JZhv_xFdFc^j*mt1-}d8s~DJSCCCeQd)ZNaOO~pPELwR zNKZ&ka>Pd^#Uv&q_3e!lNQ}eLXJA5N%z&uq_=Kcbhog6D`T&BH(qm#0%&+db%dIRHfeKk@nh zb3IO{J^#Nw|7Un<*Km2QU2eA?`>{~4Ly{DNG2?0f}PLJw~pRXUQ-9xzO^UoXWGg$vK`{r%E`}fB)Sn1tX z`n8olZl!lyjW-CoK>94lPCESNhKw7VX1gzV=)kw@QGTrS@#^Xe4^)2-bbQOn*6XeG zcdKzPR(g7%`o7h;g+TRst8ot1rSDhQxQObq4~QN;6Vc0cy}Un2AuYa8QxI9&q7O?Q zAbS2Okiyp23ig1lAvOSC8`uk?(pYA_4-5i%5(FfZAwT?NTW1xNHF&v+cbWGl%QC_XHJ} zrrqzSd&}H()z!ugbPfZnK60x*??83<>YCf2y85uKbZM(Tc&oVz0_~f(($zaE-FwiL z-5**x;_|t>x^(*a^5xI`Mqee$M@Z1#1~*+Y=)P%}t?hR4*|SD$pYR8C@^h6xa;5LV zSsslwRyt~+I=__;AE?f3HAjV&O~FcsxAGyd@)fYsv8{CVY?RHAp#Svw zdBt1B%O~s_SNFlMe)wW<;NL^6t~mfjXO2YlGEgrYMQ4`s5p_~6kDqeISx(!m{unEGzTKIfCE=f-f}ouQhxlCpR!5=>`&8^rVn7hQJ< z`(zx~M+Eg5dNZV#IW;|6FrZH~vk?d#1ENwKF$t-$(diNpotoY!F-CsHB{>Es#KiKG z&+f&?MoDkAY<5J)$42+(u%v{z*fe!eT6&@*DLF1VH9GdFaM`i<7+{a^Q9wCDe0Y^lHcZqNT=`vBNJ0Dg1jo1O-V znT5lnQW8YJt!gg)^6~BV{C{O$8bu{p_BQ}e-2dOGs;Wo(7hSz>Xj`RkM<=J69)q-{ z`pFtS?56%7DIMx8_(&>0fJi9*|L>^&@}NQ89CWG|MaeX}Rr&*jEUnrfpfO_sT7;SY z0GH4QUpq9UY`wek11Mkb)QQtp*r%IXuUmFZ59JFl$rb;7|8f0QjnkJte)HP^%ZzJ$ zhvVuXYvg#Y@$@id%l ze$_{3PL4PuRiDDNL^!U0n4@QnV zyyBbf4#aNop_To?%B~ftJ;BQEVWrnw*&nR*eJlOl%6?*H zm$0&{SlK14>@9)XNv!(pt?U@P)qSiym*HLNIVnAS{Wp;-LnqxBy`tNTKh(TV)2!?* zR`!tUvSa*JV-m!El85MZh+em>!G2Pouna^N#sQSg2N1&gnXJp$0ID6|c`yuK2+x61 zIS}TxYKH5ugIYfM`;mdFhTm|RC{7BvQBX*x<$RD-&ROQ!j zB0dd!qN$f+{@!E%#Rs?+d3fUf{{_-aQ0o7QjQxMYp7+n5_s^d9&z}3=9{X?i{SO|( zsDf%9`>$71)uQ?zOYb*z;+$YASQgqrO7EWnXQ2}p&_I)Zf0ZP{L?;e|<|1vi;0km7 zZSb4xugTS>&adnAGABUY@-Qq9x^MpxbsjZVKwL_dJ-{z4+zk7we7`PwSc6bWx*7wJ zb@Uhj(aSwfubPlgboFgjj0^Ztk58M!wVLAqW5N3?M*ir#^k1HC>$9xCmtm#zTj};zI=q!_!s`11 zwr+pZ4|Aq5rW$20BYbpPJDN7?+9}9;-(T&;1C-nry zx$ka>KJI$}%6R^{aAKtH=Wsl_?(xiTSC#*McXZcTz4(@2SH`O$i}@{n4e^Bku5Sa| z?eV`%DbQOc3;27F{coR;+mkA}8vOr_*?0^GfD^F+*#7_i4g|LUPfYBfM17`?GB2Yf zX7rF5In?|edT2td1a(srbP&QGvXPNQ3HJOSJu{d%-t2hC>nx+lUF_8#GJ6gHKI+5A zf-*fWXK{zp?%mL8%6;xDZm{P7kkJT?`~OGJ0bo{D)nWxWcK^R=2Uum;0nVW9C~d{> z={J%2>~eg{4q&9JYCFKf-?0NMo?zMmA}%3XrQa_s52NX+hdojj)~(DAP#zXh7B;qQ zzX)l{miYoyoC`p%LE5f`&e6(6;56(2Rrv>4*$1NZ^QgXj>D!-atUp4Y$oiMYy38XW z>!;tSu2VC&=Ns$(^9+bDK%nz_1*%tD^%Vzd*RUD`QC)U~ldb1l*(t2-4px29mUfvY zqG#UTU~2Hpht|e7X_>a{;>e&d>+elk^_g4grB;3ER%0mw?JKsjFIdfM_oK3d)PA@A z+NXzP6vRL4UGv<&l(^~}t6^o&vie@VmA%8t&SK?*VC8FIi3CJaq`{k+E>EIH7DzT?)if93Bi2I}Lm zTg6k^XTfR=mX-Z6P@e+v1!zItlH=3MNvUU+A(4bPgtA0pKag@e7M=omywrE>6JQ(G znPb3*4MWZEC*v=!U_AKAiZE%1Qo&smHJ`6q&-H^w|=Y=oAF~<62 z_zLS&;9NKza!tPb;bU+XTm)ysr{R3~AY1^Wg1COz2R;d-;R=`t*TSpdMtC{=4EBZF zU_AT|CO~OFqH(Cbsk>jx+ZDPMaYD4-X z_M=5HJXf~=uPYm^%^v@I68Zq`_J3JhU)uk*wCb_{Wvl79?fF0K@jv$XzkkyFztRU# z!9eK9%FoG2i%pDk4C&rY#;IoT%|v?+P~?eIKYV`T5JQ|8-tsa0E}F35kM z?++?_(*5&_p6j(?X|2zr&!5xi<#)ejEXHY)Z|kBrug}qvF8W|#T(4VSIC~r4TU7i1 z@7Se$lz+!#XE+`kga3Gps%#5ZekodZL5|j*CQP9_-#y$N);^iu;ba%nRwoxDWBAcG z2TA2eY&=1+@$B5UK*AH{4>NAWc_2-LsD%8w;bzXdD5h(P_gs>}Z*P(Q9f_4(@Z`; zW|S4GtjKpP(D;YRcYB+V%Ck~~%Q(Q5?Qs{obG>10QZEWiF7mnxTw`5@!^5K81x2oW zzA0q4|Lyj_-Twdc+W*y~{}*S;{N?uipZ5Hp_WZy9tndHY{r?q*`ILQgqv8g8{LlY~ z@jpgYRXy7OvB&>((?S?Et!eA(%HN~E|FMw%@cx&n@jng+>2l<{3Ulzjkq>TGoFh{| zy8N#>N2VH=GfJN~ROZ<*&)1@!zVB%@HBEfIo53eI-?*dB$Ja;w&L?w@m`g+Rhe7jv z{@?$UFxk&P4Kfbs5c7eEAGiz*iY@ed@Z;7be7|3KlCxBHDH~rTV~M!>6pwS13fs%R zkCNV^z8-f;k$(I^zvI=8#_^Jdv9hlbXr@br87Go4M5`io-}puBE9+-R9lhSkSeI{^ zN?u-O+`Ctdyj;wA8X?ahPqEKk`u_U8Q~r;l2RS^>;Vw6-cBOl03;|<{zDOFD8Rs;| zHT__$k2KO4+(%F2Vl&Ogdc`z)>+5j_2kG~_4qU=9QELC7ifJ6K=c=AY|8yPieV2RT ze}7sU_gy^;GL>yCH#2j%Q&J_@Im7QdByW2fc{{#=p6=~6ct-j8C0t6n6Qcdz3y)JC zCF4oe)9jy4|KAeZ@jk1vpFH#OAi4^@NzT#wb7qEBe183(Yyan0o@@WSFymFvA9;~u zq)GD3c+S<~{`pX9T*TL0e>?Wy0iP!y+Q#|igZ_-^1qAy@86kOQE>`(qTwfY_sNO?) zw@-@fGbgQuS|Z9%)UC#+mOyxWJ?a|K!1@&X<67U*}Z0v*Fy zUAD_W`xvbH46JO{|6j*yTKObe*|=q_9b@8*wwvCjYeRW0WnUs9OJ~Mh)rMVU3>NGK zdCj!m@O&s^yV}5>P|AB56V?IBm@tWdF7fGYHR>eVZW2G^@wDksmh0G0&Nmf`mGy4e z0^SG1;Y=t4Js5}Pi-dC_RjT$djE9Uz^CiKBka2z5Q}Al|G|Ym}K&iZ!K)x%iy#yJD zr!9xK!I$Bka0Q$OS3-&s?RB^Sz5%73^fp`u--YYp`|u;U0d9gHz%7vRZfb1bX81kp zpTnQw7w|Cr5^A(*cEFl&Cu|76h9PhlYzn`HG6sDY4asZm z5R@^0zrjK92+V-BYG}SZSR2aNzhF2XGM3UOWB-@~$~O&$!h2v-C}aOlf#kW?3_b!G zf2YR!oe9Yk?JUT(YiC2QNjnE_hv&g>U~5PkwF_W-~jjvOoP$}8w`JeL*T#RRj?+{A94B`z)UE* zL^hP?bS7f7gSEx{mgiL3Pg4+C#?|D$u}pxXhfIW>;LWfrybbn+cfb^Q7tDiqL%I^Q zd*MVl6HbTs!+YTzDDy@?0w0Bs!YAQlP@cPmP|_#QMpPeZLvauJBk4Pv-?b!w}l;wQ(lk%lIYz8ldl5f4>xv)2kfKl*L z7!Q+Sf0zZYfcY>Lj)ZBD=Cw8m-T<$Lx5I1TEa-re51DWt%z+D`6Eei5%67@ z4>v+L+yzVEemDyL8;*vD;5b;D>mCmq!0TWrydE}#H^7$gMtClq1SylX+h9DL3>ixkTO?$5>lpWPeaN| z?HRZZ(k}K-?I2rq>n!x*>;QYL7h!7JcqI0$ZmL*aJlfM3BZxC7?Fop1#F8jgg!;TZS>ybJya z=fJ)2QMeC22KU3K;4g3mJODSqL-1pG7=8wWYV&-<8c?3!mHgIqBhJZhd42~Hz7)z* zO8F$uFKO^Kf|KF7a2BK)sXYXxz9miSJ*K|(UBdb!um>cKYRvl+P|BsJp}gOVVIJf> z>iK>FdRbo%Z-kVkYVN+*;oYpShEhhp4QInQ;Un-}DD~ue_%!62eM{koP~Q7b;2ZE$ zDDU|u_$mAxeha^V-$CA8-%oG{l;?jZl;{6jDD~m@Q0l`UVOzKtb~o0gKHSIp0C*5G zPF(vh%z+x^jy&H-SeJTG)?M&7)(c<_)}>yo3+4F_hBv@^@K#tKNN|`wr z@@!~V!dKxn@B`?8+&e7`a__Vp$i32>a1Yr!HviM%Q6cQ`_co*y%?4j%@Lba zkV7TOAp=wC^k=Eq?=L#6|I(O#HwhD+GZ#fv`c@CCxX?9;SJs2)>h>;kZcK9y(yc~h zVn1q#tUx3!@oIk~EreC|Et0$!kN*Cs|F=k*J*>{xIx(q}n8fHCtR32*JPggEQdsvY z!q8mw{c4mSD<(p&sp2*3bB#(Kco6Ahly8v8x+Au<$m39-CefsSbw6QiXyjP+p;`5r z)H_$%4xRJ%9o+QsGgBNl9(s8|_EYFaN_UxZuzt-?8+_I}en{KJr%f7*t`=xtmQ~-& z$sVuK;vBiI^A3NK-DcT&-I_Ew_k#nsMUzR@)dyHzeX#%ZaT!mxk$isnyvs`OyQfay z_O5fk{a5gt`~LakFE*>|>bzp|w22K)o&L<_v3V&k?&U507UOV8Q2pBXcW?AS*!-Rg zhSe%q*sZm7-)wdDd0O`IUL@)Hde-)RV{dqO*4J*RdO<~ogi^tuAOCBNmoP2faCmbUCC z^$K=XHIHaVC^pk@cs-rhQCoV^(&tiZ(IR9+pqMrcJCJwlOQanAO}9(>Q(+ zGDN-&?np^Z7!;Kr+h-sj2yvvPr}mDC>5~}KtxE)7D;SuX&hOZyKhO8S`o8~>?Jg*A zd2RoH+yCG8|F`}B`G|&vue|vglEloy;ZZ3GqE+UX9mp#Rx##<5+e8Q}>=0%+4&^5osfRfQarr;da9gplZ;nb?@Nc(Y-ULl4dD_y5959{2ia#$ytWcq%|<;T(-6WzOZXtW+S-0jXO^R4!>Ete6yInH7gCg1*$ zF!Q^hRyw)ZCmQMTmDwwr8|$XMVn6YSU$B*K{&&;Ct#l$Q-NDMQ*-GcN>MymLE6_>@ z4^+pu^0RiFDLPT>egneqx^#NPe}}BNWps@VWOwXav?fvLwGUdw~X~resk?1`nLLp=qM;U^B72Bt^A5GtSX(H zf?DawqBCPARXVf8jcV$99Ewi-926b-RTvH5fT*Cp_h5f0bx$Jv6ehsA@b9ccZzb_D z4*UCmjy|bTNzw6+w4q7Sj_BlpN$Hpr21O+f~r^Vpw)QRK*xz%=@(Xd!C%$>x3X6_ zmA*4>>%yiV4trzK4cFcB{f@_Xkw1Tx9pgcjp0h6NP@~ILKYw-hJWt=3w!J`ynw4*W z)%a{H`+${w!%Dvk)GlFZf8YfQ3A(Rv+7s8d_-xM1**R;k*!4XMPoVm&mEIbt9m7gL zwX*YA**yZa|D0?)iIqLU>RYRS)!dSzkE2JM<*6F?$GBLAvheO1h1!+qzw$E^O8J`w zUP809<-iRZZ&uF?D^j|9_#OgQwFUB>g{0@I&HT(Y~q^YaIPAd!RMt6}Oc1n3z zkIG?Pj4;Emo<>2DyEr$mFh}{`o4)n>c!P3ZdlwN&KmRP!^B&id=FRkYONtT-bKPnj z!guQ0m|K8tVmCN-h)S#Wsj)uCSpUpef40K*dU~3*Eo?XK3foAVm7e`q>C;x@hpS6( zw$c{^)eEc3FThIw4pcw3(s!-=Dek&J^!w7o*MAeaGIY|7(JQ*W_)a3lB@_zB2ofzQCV!EN9>;D5k(!EeC#z#qW(!T*8lz@I^B z??^ZtIqx69xG3^FfiJLqBDe|c0)7RigWJK2z^_3M$afML4}J$;4ss5a?`IPz`Ax{N zr1Jfcn=Eh#+ed(s-y{M2C&;-2kAO#klE3F@@H6l@@N1A9L@Mvs2_QMijP@X%p1RJq zAi0SGz`qexSn}fQWAHhnB z#7~6AO}s~aqp=gz3m4UP1pFGMF-|I!j~c>$p|KQ$&Z>*2$mjFfeR+5CBeiplgvL|^ zb5yI(hV$Z>+KAmc$C(+eYPle5{cW}a!t3)^mzGu5dEA`wzcc>#`+ViqGSxjfhKmbw zQ!-Lr`TY~5qM%yK>F`&y_xj(1b^PCyd&o4RV&z_b!3?(*=KMcp)rEfl6o>!Q{#KYk z*!RmHuaN9Hc?ja++=nJH8?hk7e6}k&JtrkesCieC3LlfT)Jz!pGJlf2ZF|OR?5rdgjIyoyf z)#3k~_@BDLh@=1MKB>6RfWE!rOzdr90)kbkPvDOROo%t@6MFR{>ZdY+c-Z|F|6_Gk zzc&B3vYGU-W{mdXU=c;)f7Y-fc|gp|+8g~%{ahF}FBYI9KiGINoz6c}yqJzB((w-a zTW1n3|E1$WbbOzVFAJ9k)bV9HUTl}*xn`Yp%GgJxe6`@?HW`^;z!4yrh>0pH z{QK%wn;vdb_}{7*uK4PX+1TJmN&N%oe7Mjv?DH$H7%`^fBX70%R?mw?ivK!LJeN*i zq~o(bQqOJkgsWDqShr-=sbgLp^jPNwJRhCzC^}A_=dQDky6%!^-#YQyKD+WiIAO-B z@N-$5r_TFeW=z{=i!M7acU7l*Z>jmA2f1jEm-c6P-k$i}@GgtSFU@?qc*gsDkKxW^ z07s%YD)XIYzUzd?3f9eZfrpUyAKDm|Pa6tA<;9oymtoWo1m)c*^$S3orH zC_e!=u{WGVC?B!6S{Yj>vxIBfOIy(y|BEq_+?!!Qa%yY?SnLI%*7+AdX7*1La# zTFK0FjmlPQ$tj~V)JmPoC^K(x{N(t)@iN}FYSW2;nH--GFJUk=%O3I}t)yq>vN!8V zS((Y{dFff1t~8`e8R?nB%vN107strNq_p(R6qjpoPToj$WO8!4WbDZ02=i>c;(My* ze`DkS$k_ko)gI4;vV=Y+1K`a6TaZp{Rz^X2S&_TK-=ng)$oczs{{B-B)*L_un5#BD zuH!&W9(`ZAa{DY!{tr7o$cg{?mF@r9t|~GNz@GAd*m*$Xq8ZGxTuJ!@6kveNuGmIFNf&by1G8njOd+$>iRcQ$H?E@Z*|?VcopTo>hmVDy{G3T`>f|0 z<(XDdMC)YdIcY4<=uQSxI_}{>aWguOM3-mujBa8J{^Q2W4*T??(YIgP;a?qY_Qv_gPKMJF8vKu-)L#W6=`(n}D6P`9U_Z9!fJi@;7HKRv zk8R#{fzqDM zVcDEksNP1$WzFYi_J+?|$$mDcwU%|6$0syrY153|Yiw+8E4mH(9`@}+5e2IMG${gSh|5sJ!sqQhke*)P{^}=9HPUHF=z0>~-pVzCOt@knYJqMBg z$L^{k)BW!;{?F#l)>`@>Y@XUV6GZz z0uGRCo<)&!mELXMqe~_^a@9Xqd-kyx;Rt7jaD;1vIKpQ4)N+LJa*XhSKdZJCZ`jkW z`?0Q0@rIJaxUO!1I_~wleR|1pq4(7$i0iX+8VfhN%{GA@tQXES3d$)Ia zu)`6F=YD_Pylvi>JHr=KVxq?W-(x>^$*4Xe=HIPOFkV^ul0Hw-p~5ZdxU&PL?+TY& z+^)W_Z{Cer-Tn4|_FKK^>qn1SIgvoCNO6mi;t+M*w~m7gH@-1aoTH8-4p$GRi~rPd zkvh&XQrx1>7oy`db$T-$=a_JUydRd9d87Idy!e5R&$oX6l>E{p6{}~==G|!at%_%C zFjrAC;^267s<=Et&Wx69r+}?M_&|dg!2mp)B|HciQ5xy5?)Vnk`lKk z9HPYSm4n0*1}1?gf$*Hh$>0pI1GodsrkH)NVaR@xA6kY6$rM?bn_XSk0I(C=XM@6# z4g{q$W6t(K6)16j(wEEU((VF^+0UBm(k>KWEeA^Mq2fRv1*P3(Ab-Zj3Q#)Ee}Te@ zJ_XA2SP3SAt3dcq<2ew%(|7^A0DKV~2fhZ%eXjwp0p9|p-(L&P0cF3NK<0E;*C`#h z*(a)8*y{e9P{*rYEuXWWd@uh7yMWuk72wQ_J!=8?CSGxGcvPE1ZHzzNU3{X=o!-s0 zuB2?F2-!@GqQn0kEc~A{{;xOwZ^}<}-{=3HR8{78=Kpqbe>>ctllxoeCv&uaq1>RO z{ri=*e`Yro5$11ib$@-1q=RMY%^?3o@D%WyW6+abVRB;Xl5Ei*p9?RvnRpy~X=$%0 z>PzMj9~iq!abkxx;H-o*lKpR?{i2!@^*I!j;{4oRztXS8eQM6rRaTvp;d2)%&QG|m zc&om=#OyR@*% zY*jeB7P0N^HYSZ!JIlV`a!=oK-4#V-JC}=Rk$vTS7przFr5V+(951#HDa)&U<(?$5 zsbt?l*7@w~mFHpKZ&6Z?dLFV*C4(NhRyih(?e92md5L*aIVPF94zdp?X>1?4-tF6d z{`q^3tMQlgcUjjPdaUf1_TQg>{=wX@XM(k(aCtY+Fu%|r`AoJSv$l6x+xK5-e*Uwy z{id~j*svcKKlpRH~C`1`GG(R|AF{5$0H=GU6X z+nP{2uU)nXd05s{i12Z{I26bdy(Je)<^5-wF{n8K5(5nUp+nO zq`ErA>k9uHRM%hH4yo&9*LmxX?JITiT?sYrr`UPzcK5d<``g-2vA^ZJwe#XNrUe~x zxSp5Q@jVABf8CZi;Za`t=;BEa4ftg7Khnql(E7g4i{LCym-tTYSbX z{np(zU#|t#@tQi`SErqGw4)z?wPCwHubnPVH(VY!TYbMpm4(lzoI5!BiW5I89y79> zcizFq<3@^?)$x)0TO)d@dd{z2pZnN%+pb%*^3$w$i_dC8fa?-fKjeg(_RHqn7d3y{ zXT7g)wtYDBD4wSN?)G`^dL8c@ZX9s9Ji3m@)$zW&<8e1a4w_>na}2crUULfX-?c&- zvW!JrDdVaMpzxR#U;^j`2ZQwUf$<>kY&Cx#KkvW-wkLyYK=h=6cRz_!PJUd=C6C_y+ht@GY<%&*BSk82An71^)+L0`3C; z1U7{~y#;Imz5oi(y%v-_bn8Iju{VNk!B4^WIqpkv1GpXh2>dU&5j=`#E_`@9@L2Fn zQ26fzQ26b0K*>)x6zl>PfTw~*U{`PwD17iG;A+|nfUkkGLE+cuf%05$0(-H24=8;1 zQ{W(Q4Jf?#=U^iE6*v_9KQIYAj4@j>7!9U?Z9u-Oz^PysI0`%$ECh4FKZ56jSAZ9S zbHK6SU0@-&0`!2Zz!LB+a2)tPSPE_g%fSDD6ToKl`PE=wum(&7CxRD&{3Zf!@OrQu zyaAjE{u#U;oD1F#&I9iTZvvNsH-j&N{B{C6!G)j%)C+HaI`|~m6MP!%2MT|m1j=(B z2EGawfp3Dv;CtZZ;0NG5@I&w>a07T3xCwj^{1kiy{0v+P3LpP6D17{z;FsW9@GEd5 z_#bd9NG`*`E^r4Zj*V}@7T|Z_(ct&su^_+UfY?I2z#aVEF|4r0jOJKU{?22lV`Q@1A{f;~MY&JPvAe~|2g^p4qE-M)m25r2|(Yuw|;=J zLN>BIM>(4EAf@TC%&~T-@SIUD^Rnt30m7Hc_}5;fpSBjPzsCIl7gO&p6%MpztniwZ zN6B1wHPs&1B*k|M$9_2HtSE9T{&G9pvY)r6Lh-c1@eB9+Cv{DX1KC%$@3gjUZg`co zZFAMDt!~k>+)sJ7cWm>aaOujGN`4}QKuVSS5wdgpxuZDT* zW~uLM-k;{()^yXh^JX4#{*q>M{zC9>xICAR&(h^A(B-vTrmnML47QG!kCd)BTwSG( zN7M0TI(|*34}U^^F9i=D*7=Gbwx0RIfComNTix^$I2`r+oVD}FChxX*zjIoCw$Th+h(z?sKy`{E(r4R1%6tebNC7uc?y<$G^9?}*MHFdZ8AVj zT;9=-OuTX}@!YEZ_U?;cX>YC z1`0278MqL<0=y5r5_}N63KTxX3qAwVmjs>#b+PI)k7;nc`g=p*sCw~RfIG0~Dh=g3 zLgy{j@f$LCsU5#=a~h$1#@_N6bg$GJ<;7ZMe&2kI;N%6Wv8VpI@pAB$s4CQbv!I{1n4oQEYT4g@M;s z?i=Te>z+kaT2@(i{;8%+VP#k`C5M%iG^$e4*m+om)Mv;)xuowa{srm#o&0}J{BQEm zl)OZhg4D_XSNj@r^8Y#c|3v$D0P_D^nOUt4D&DyRj+1rc zbKUqk+;MNX_OH&))s3GcWoLyu4%hJ#I^ICX4}{BOY*Y5vGar3&{-65Z_0}cbA3yt% z_-7gL>v)4m+56%043Xjo!nN0Qe1a}6a(~A&hHH<{3bLQJ_hbWSb5}~XAims6Y^8ik zTh@2t|CL-_GAG_x47wrMQnM*yQ^{O=;u{vfaInoJc2cN)w3+=vZ6iA_jVoxdL}vV7 z?Qy$HCq%;k=ZybN)6DFqtm)OrNbk)5Wllb3X7_jU|GSd1Mv-Ga$2B@JV-&L=$WLUX zn)V}-AJFl?)nP?s_`gRV;MqnDy)t?)^lq}WHbLQ0_}EI}lf?(<;;L$hwVC2_mwL#Q zedOU(zAcik?(3~buig38*zA`dpE$kCz12OQ zH;hQ-ZaLI#lpVYehEU9Rkoj(kL3?{Rb$uwZfC0gN;1OVdFd7^H9tmQG1TF!^#?kqU zMBf^&O(S-Y?f2Q8T{9PZ%FNy8@c#@0o%w&A`M>sh{4e(ZzUTiaba}iJmHEFfbAK&z ze1FO<>)8Iz+<(kbc;4U~kDu{8rai91p*e|}NolU!^D>hh+rP|>E3qz9Z=KxMthG{* zG+;lxBn&7mtE@XPl(q<$&v0`8x_y-%BogPBF>kjyhxb98``7HIB4YIK$>%rJviWb7 zfig?KD9IUM7=6Lk*!=$+YuWrTa>G3S6z{~cDxbH)wK*`4YkgPyuW?3*C^iLhsXZfXYm!r z%&!x^YT2sGHwZ3u8f3?3`^)AqU#_0>htoP{j2}0x&+QwI+VRIEPu%{W+gztv-om*X z%Y~xLNfNG4Sojulyjt&nvtN*0t(~bqgL+x`nW-G8!mmKeY>g6yMmAdCckY$J&cNM`|U5||EN2+jvNCa?%(pTG+6 zV(>Yz3Va!KgKvVx;5*`Yayw-ul*~6Uc=? z367uMNxRY6hSB{H(Ec^$c}BqgFY;CS9s57nb?<0@9qsQS@b~Y`{o~C2Q%9fp3MUk# zd#%YH>#A+8<$U)*(mWJax%^oC?tvw~s@y}%{#V`9uVVk(`4KZL`(MU4C(_rFKgoE9 z0IyC{#qMohC|5+a%8%F;jj+8)zicki91$AtdwA>s247J|4EYfgu+xLL`x(c>3%mz0 za8Wz*<4ksYD*a_XZ;r?9t14EX#sA;WKVQXXNj|UNGudx_BG=F&wkx0A5qw;>uZ+E_ zs<{T?15Q@wHH?myy?%zZ-Jb1WdtGM->&DUH+Q-Y{WSrhe3?~ETNW|vp?9^~IzB+qR zXWxfw*XrzSojt9yQ+0NBxLV;zX_R&RN~HLYNNJjN{D)2#@R_oA|Ne*DPjB*5VEMni zHx69s9(9l2-qpo0pQe82yXJk@{>T{jn|WLM7PU$|@^RusRsDmRF>RYIy6n8%Rh{m= zrRIkoymwBJzl&B~9@;f;>W0nV&${WvpKtq$eRZ+KI(<;M8g89dTNm@7i(v>i7UD(> z=9s9-KmGBuAuD5NUY_)1!m_WLOxMQ}x8oVbL|t?CGcQdX@cvC7d^aY2>HMMS8N^HVk=>AIp21M<+S<)PhVw%`4p>B!pSn>!*UrY@Y$9fbpQ%=j@~GZ|YR8u@PWC z_2+`v?rM&i^T7+*z5vALRx`mHj>oghCBqJ1cvAE?CU= zUqOilz73oJJ_=TVkAXGd>mXfHU<1f2J+K+1?Z9E&Z-D>F6KGrm-c9`qY}?)0;OqH3 zI2W277c4q|C)AQ1^GF@qj_|?)ghYcnGG}+}EIx0OQd1PWzT6$)R%QZMBZzR>3dCAG? zuF)wuxg5c4rty8_d&T#RR|?zQwDfG*B{^m2sA1AXX37{>Zt@7#URG-Ap_luoDPPIH z=l{ZqLE`So5s3d?>}UWS4FG?Gu2GH#z|jDVPRjH7%B#g;UtUw}sdeaEes|k0wPG0R z)jAr0A~HPWrevhL^2vlCfgjZ&=?Tr~qR@x>8|MJ*t|}rdfQ$p1#dbSJY~Q3Tf^p1I zgKK2saM@zKW6#4oNQAO1YqiM&$6q0ZXU3C3Aujj=H!`)hT?SgD;?u4&MvG%gCXB*v^uwWZ0K3# zSx2k;=kuUD7mlPw>=(4xiS}xICF(iLHEayJhsLkOU+) zLdf=rknOP{+ml1KAGNmSnxy>~t!?=&$o39vTfRTpZZp|DzkCm}-P_uh@80HnmN1Ca z#op@d*M%L#-s(T~;Tgludf=~fp8a;iV|_NYj+~vUSb**yQeSRw61s0LDL8F-JK}U`Q;<7`F2_Ab5nQxbmeyiJ?~g% z7&@M&m)ifk8<($L|HyT*pEbYpxCftGfWtP57f4Lh1!woHe&qeBrEh(*sigJo|H;7y zRr_}yJ~IBsv*z~sU;dLD}(H**bsDoy%A2=MK^FF*^QC zr<+Mtc6`tFU57T=^4+sTKl%IPXD5A%y{^-{==2BS>R`gbhRet3ct;&i6E6Sv zA_PcG)K8~29pyXGzxmuF6Mo#W=*!NAq2q6KJfAKXoQ`kP#q;QRIN^0fhhg@=X1_c$ zM2B%2e24VaJwfR|`-9RapAB{bhk)^5A}G{G639L(_k0>Cx{FMZqUI2WmIw%slLrcK zBRmh<2gU24ZBTrU@H2(f{}ZI|4Ll8A4DzR=bQtf0bWedzAl-04I)}@_X5b94IVgII zHsDO~I8b<-6T$1ij^GX8nc$6J0yqa82+jqEg13N~;H_W|csp1G-UZ6IWg++==ieO% zWXJk+&25+$PWT_mf4`@EaQ!(}bQm%Zugw9aQMbF8AX|@-&wh43`%>0xQx7>BV9bKx z2m@#QU;Eo&4Blw)yJlin{sa#B@jpYi2d|KsR?CijgOYF+-zbo4)t{wIjD zb8>%#_O#CYzrXVQzh*ZT5$11Cyx+IgeEE8;*>j1e$DgvfUQ?l z{O#=2!VB&=`0{V)=7iPFajD~VbUeWp^?PeL7p+b=rnCFQK0<&-s)t(2yt3?q&*NJ=ak7l@D0T!pJ4Ky3RySlc0U+l4cy zoy=z^_PykKx9$7jx$7k7hs;?wmTS>zJ=9!v2~0(f5$ELoc5;7YI=MggI`@a<{uBFu z-}OJmMdM0IR_5q`+#0Hg2?Tt=j8u%a)8*`O6w_oLd|L?2S~NZ zefG>`|1R}Ec2^Y<7QiqDT6sPmZbnDS5^aJ^eQkWe*m-r4=qHAVyvJ4SDXJMa&Qs-* zzLL*njz4SnMnSYG(w?8<%JHDindm8I-PT>a$GR?#U8jGDlwG}0*{eez9Pe8F(wuLu zy}97{rTw33&V1uX3tv!H>U(|5@GmmHm@?pK_lOPn`aU{I>NiigYSoH$OIDpa=G8&F z&le#6S@Er!&(D0Wli9|VuVUo^<%bna#2+codnhOmbVZQuvXOOXah`zl1R8$=ks1Gc zs;Vk{#ij>P-ia^=V-4Pc2)c}ua-wT-WSP6lSM4kD$92s)EiMazVnx|hvK_@G`6?>Q z$~{$vqyGKlBL12dD0~s+gn&(Fgll)W`)mNFJRwxvO42`odINb zRS_Eh@5v8n>;G3<_Q42xM25+do5}dgF^2IvB0kaoS9|;}F3nfvl8&^YZ6N)0@FF^M z+cxkJ$Z6XIrR;dH`5bi`Pn|DPXS?WplsZjoq--6Xk1|}}qRuy@^QG#1k>UDS_qXrz zU1bZt6Vv>x%Zo02=O%b-qZQ&oNTI#c+LyVxy0xnC~#dkawHp zvXl24u0*w*j!k|zYy3n4$AJkTRDhaGAPz(~9q0n~2H985HP9U#z_z?22ZIvRJ`*wZ zkkNXdWM?$Rjrk-)W6gu-PqSmpZQtYWbj9`@Y6V<_L?rH22kYbjb2NYlxdt#Y_WwAK z-<|+~zg5Tnw*>ai-@o(se~|zE+a9$&8IGotZC~@{+h=j+|Fg|`M+5vTYk;k;DnjP} z9^-$$r9a$ACmXH~Qm1P?RBQts2ce5o_M9gE!D;L7YxCyVrysm@`c+?ka{m^dn=VfL zK!-M4W5Ienqx|wavInKpa7yWQ>Zn%B5 z*L$Sorx@;vVxv1*ZN0v7-#A}f_bg)6vdWmYCaanaxRk~!Lynv|24?P@A&_H z{>nnXyELx*$h^4jc}a2I^9?c^8hbfD!~R!YRfGtDJ^BB|{+Idd!u4tE?5l9?BApL8 zT%V!N4hc8TRmX#bYj5c6aGiY{DJ_6bYoN39_jjCYxbfmT4T6qW>aTc>4b3iUcJ=nS zEAGs_yxR|bv*W_g_ZO~jS;rqmieJ$2N4j|Wa5WS%i;E_DSs{)6rPt<;Cw{CBgT#0jqe&j4+I{aUug zXTKR-0(T(s!l7}(c0MpW7nsBgJNAESN-lk;3_mkmnOUh>IY}wxtj$cf>yvWw8ml)$ z@G?>oGhHOoca3C4TB(i9OU@dT>x}=C(i0Dz@qdKu|MF@Q>ef`71c2lJcl`fO4lvAn z$Nmq=+wb`QLxYta|NpP;|F^rU$PfT~WB+H<$?C@Uks7auYd`DkaGkvxt{y>WXC5dG z^Ag0xF;PQKsA<1!&V5nyr+wD@`exgQ6MLcZmp9ChuJecM=1bG%AJf^fI{PzRd;U{p zpWoSj%EH+lhIYy5wyXR<=YM`KIi1_f{l5Ibna6MY;vwG+Z%3D`n{xXX`uWdv_FlO5 zwk}3Pmq#mHo@)!-Lu2!^Pg3#aH{VtH(4aLBH~lcF%N?zrc=>bYYU?2Tclzlach}{e z5?Z(D_{I;{BtgpQ>~kGornB31_IbE`LAX3xrP$#?aqgixx`MUvGVfYr^PG!KZQIgf zN9$~8Tl=n^LZ)Dx{ZjQ6jd%3FwF$Nz|G(q^Kgj)m5wicQU2bxklm%_D+boO4j zcDT;oj+8!Pf7@@zklZXLYSd-MS>>Jj=5=4ab>&6P7BX?uZ%@ zrK>)PeXe4k?e;!NK9amm_S<_%8#%`^U*`NKENnmKJAvVe%jiq+DNuC z4UZ|T&aNVHu*_&(m#cV%m8AFx=JISoeN*rqNu0~Py-`MUm&<%Wu5r~Rg=OV6RWe_= zWRG`ck-6D1)b)%>&o@CA+{e(J)VT_?p=96AyZFjV` zKd`pjTiZWa+vi!^KUv!sux;l&i(?Su*jFEawPCw{p5xz}eKlO!SML-pdUDk}mp=Sv zkMS?ZC%?{L!J%$ng`4kN=ePg8*<(9+Z^?Ym<~z~ zb@%|tYuR`Z#GP*Z9qb4$2D^g)0Lg1+ECDg;jDLd2kd38a68I1}9DEqWC2u?e=7W!d z=Yh*W7x*u*2z&}02d)IigR4Lv_%v7rJ_GU^HC_a#gRg*BgRg-%g3R$8xCJ~3ya((8 za&5*b;1l3!;PYTNkYkJ<;5%SExCtBp{u?|Cl;?F0D1E~akb9`LXKgz(pPRXaaw}ye zCDaE!2>Y})VD(B3t8Aff@c{nD@WHg(@E`xwS#9%33O{^{VkpT%z88$b1G+RaEn z56(4Z=H@9CUQ$+Oa(Z5RR;DWr>2gMT<}h}4B$DRLyyWC` ziPp>I2o%lnedBw@_l%ctK38s9dbaG6ObFjFsmM$jrv_@dWuO)MU4+`N&M&9s0SW0sgf$z;)eKL`VSoyFK{<&$i|X8e-Yr zf8m&8D03-dd-q}hD0#q11n#b>D)Ucqm3hnjma0az-zJ9LYL}L^YvzjB-`juw`R5hw z$ieHWDslU(45`~m-9^V5M)j0xCfo2Ro4ZV%%P^);FE)6MwLOmulI`gs+gFBc-)L=5 z=Xl!&U$Pq;e0se2x}I&i%0DZ~?KykS>Sxa1d2$|ekE;6W*Z%U^4IAEh>gFr&obpZb z`3%UPQT2i46Qcfk)SS^T{CwHGfvJDLh_Rs3w9eXjWRrJWyx%!3zuS^_GpD8)M!1^U zgUwg^sk-0ddp>FL*J;Nezs)%Jfg5i5WHdaAy5Ap%e)Ht>)sJ7cWm>aaOdG!|&nPBp z?EgLXW0#ETBVzvD>ICDJr7t9fY(d*(#L7_aKx_xAkp!arnHKX>f&-Hv~8!Mld>d$aww z953H{i*@%uI{N9JpD&!haK!XpKg}cO_z6oX5CcyD6UQe-K_ww70R6?hux2l;a~YCud@<5G|u<;Dz9Y<}$V0O8KYOfVO` z2D|{g4x9ka2AQtGxD&h>ybGKRE(9+Dxt_okpl}Wxt2hVgBZO<<9ENRs>wL?>e&u*b z2GM>8+u6af)xrAjy|t~AX|s1PmlFg08{q(&@|)WC`F~}s;`Wu7JMq8H{J&2AuR|&R z-?9BeP5fE|KJ){}_7`g7eD}af?$W|4mtU=y_^NWprG;oY?Zc`t?#c zpf8zL*&fi>-ZnoUkN}k~^$F(Z3GrrqLa%x_Dk)+^C}?zWULVHyXzN))D`K@qJ9x(f?bK^Xa3T zwr9UsnD}y+x62NFTUQsa{d==%b+#*ZXstgs)bAQ>1B(48Iks#+tIq!08^5aL&Z?@P z;ntd*Bi~ zYMnE#xq0YVK(qztQ!eAaCR*ooG2yN5#w0c%j{WT=hLyl0600yoRUK&zj9r5cp&eUC z8HdL%R&5%cGcx<(Ydbvl@3q@K#kHpDZmO%pf-#S)t4rX1&HGtRqve(IxXavyJknd6S4=b!~KhNCOHv|qKhoj?U zZahV7|H(i7@v|W-V`pBT^kl-aubLnX*5yFgaW&!Qrgvh&Cx8DpbkR%r1C5EguEh<} z^KW}Gt@ZJ_%TFH?6=xXX=JFb*IGooPUB4^w&yS4_TrhEG|A`|O(f&!&{;j#Q7GF0! zYTh?zeza-swAc7?AL=>=T@HO6$LMfAb$SJPL7M%5*+1l?S7<_AXNoLm^6Hg7gZ%>1 zZy*g(TK-Db&3CyuXS*ENg1TagELrR)@5>x82ILhOI2jxRb^!B1Wd9%R&ug;zP_sTba=EkX*(7ccsS;2iP69G1-C zUY=GLDm&UVph-NVgm9;tll2Woz?aPzY3 z;=y(PbDgcPv$1qGS#+G(rn}BM>bgswee1+)`|Qg9;Di~gh$&IF?7ss|Zyqpf&dh14 zg~_XjezG{>tZo4ue7=T@*h$sJv9948yWBh+)?Eg|vxnuv=S4TPeA4mUlkn4XO{eP&;-_ie1 zC`fOZ#9*SzdVm)HH=zGlUDdB+|Jy$OH5#9OzVbEK^XXTTac^Q-)i3wy3+J#C&f(?b zWo$pg^Y+B&hId&sere{@#WUWYO&+KNWLxQMADur?XZz@ETb&K1vx&mBeRO{HNafbk z`R8>uxQU;ga`eSUxg#Wm?{SRDtYvtf09`2EdI zh07J_I3*p&rQ;A>%4Ux_>Y^R#g%hUuD=)|zSm@qv7?I+>bh-l_H>KmCbX?Pm>iT~= zwdp9|iT=&!9+~jtjzwQ~*5@nGaZS3M?>l(^#Z%08tLZ1-iC!bvPcHHH;wQfs6hFDd z-7f)4FVJ%qjrywLyKLUVp4+@J?jqD@E!>42e?NnBX7{bjZMT5!J*rc||4OPn9#^^B=bh-{@*3#*rJoL7M8__A{*z_SItaPJpO z+v)S_>FlumZEuBZKkaWGLTBgc>^2=A6Ddu>TE%B1&#hi@>xM<|zy5Gan;XxXjo?^k z{~c_+SGYWZj`!N%S_2&)qvOwXd`!5!nvVApJAMJhd`B9FygS#RTgP-(c~b%7XY6)) zpAs0Q?D!@iuT`1TMCsSb^B?HOb~CUi*c?PMZX5|Ffi1yIuoZ}0!DtPR1!KT*U>mRs zJQ|z={sEi;wgcrIehfGhj49Cb2BG;FMB`v{1dZio2<8TYd4a|_0efzC;RCvI4UYZq zjQ@*^i}rf_A36KqTT|iK|BnA3L%eT1S-q0wzf8yf@A&_N^5okzL`I=^Tw->*E{}fQ z9M{hLf3*~jWuDhPo7^N{1*T#>XTAM^;dxy}<;4ZLDH*A*{Qe0ALi|=Yn!G9W1iu6O zU%ILTWdElyR+RCoj6KEvPiw^f=kk8B{lD=5+W+C|3c`&|(AlNo+U3k%w|>4YXoXSatN zo3m84-}Y7ScN?R9uFbm^JU;Y^lP->&4qXik0qze~;1C%FRK;MuQRgr^T)?0kM*#Q*O$k8(5s4Q-up zZJKbogh*+w4>s;W$F)R?lL(h%C{Y~4QC~N0I(|pysV8Nu|LKHhkNzrrjv-u*>OgT4 zI*#G@r~#PCyV875l3S5=(E#+|Wq1kevOI*XF7MQbLD2>*1CIljgB`%fK(Y0q?34!J z32*@0E5JnXNiYq33d{yqg8ASoa4h&VSPDJ^R)Wuhlfmb}0QdqZT)}Ga&)|#TE#OPw zUEs^$1K=wl*J`{9at+37AZ;42gY09h0pTr-H^2|TH^EOp;V`y=Z-d`}?|}aUh0{Rq zpg4^d;QMT&OHkZK2aq-bXMpR$exPt1LqXv2L^~U3sl+hz4KgDoY6dT={x611)_l@(#b`ycuaabo{})7pQtn~I3xzbCu@Tl&*djCP&< z87aF_XD5Vf$A)X~>9pO^amu&eATONG7aXoVrqg!o>@=O787UuqxcTLDT6vxCI9%WT zd}SxUw`%*Eo&WGGdZP8n|Ge?uCJYaq4_fEL)%jk-)w1hi*drCAqVs)EQaLVezN_+~ zL2Djv`e9O+J6b*Q^5=Trw=P#eRy+AUzml}>)nX&_v^Pej4l?I;)cMwRc`U>A^@ppC zS-|@yH0NINgCzB>$ew85o>%eJgdrG3<8idN}%YXZ-K> z`+ViqBF5WWbzXrOxlZh_WYY>zQoN z{%1Jx|HTFAd_NflPX0ef|L^Gk9sR#oa_9LyRo+4-v#%d*pX`~KTsBbxAwoYXE}qa> zwWI&{`6~q^=}OSRz6&R{=l_C^9Q!+s;-Oq z0~6GIf~{(|&G`iDw$1qjWzNC6y1wd|rnSfPXTCu*mqPG!lRB9vQ0kZ`aAIM(nPb7e zx4G86iKh5)${#3m+4pGGZd9RP21L|_J};{3E%v#;<)-cL%+_&iO@h?Z0`#RjXF4 zTe9lZF|Q7Ktn&g$R2_d5Zq7;_4|u5aKKq+r*5zgmmtWS+Rl0+>;e3kO=P8YbA-Ni* z513`WDNE5E$m^CqFwhp{RUF_oYgB^Lw@d{yz)QjNK>i#8>|!sIE`0CyyeF=sBd{Tmv2jz5#Xs-vYaU z?}5B_l>S0;QAq4bcW^xzZ`I3r@+oHDh`xe#c@EK_+?y=qr%-b=vY*nqoDWLBa{(yl zgW^-|7J|}l5tsm81WNl-XZBCztzcc+m+S6Kk>wutI~-gHO1t-h_kp(7qmqVZP!>=^ zxx^LJ1?R_nh3&h0@$B1h9#C>)2J?x*T%ye3D173Y^u3$A!_IlJk#op|lO~_L2WsyxX*yTy)5i)VgdoHQlDUco)B-=C-mwU*L_T7LZ7(q zsStZ(@|kZ0%Jlro{zx=aDO2LQm+l+?SKC!Zh5^JxHMUc9IVE&FN2Kg;on8AwAF;px zeD9tA?6`B=C)59S&&eHLEZrJ8`@22&yo53LE0ixNwv8Y;>LjP#ff)aDNzURt0p|&X z@dP3|{_o|C|DEx_GyZqR|IYZ|8UG*L-+pDCy>g)919W=)aP2dlf7*4TJpazaN5iow# zzweefx!;#Qx_Ht<13p>&kM!|Bw7#$NBE$H-#Q{9b`=%`pK#9@51P9@K%EJ_i4Tz^c zn_}DQlUbijSwdMukywFHzup(rOPqk@mv1bOy~GJLmcu^W_y9Ww*qQ&!ng5Ht;d!o6 z*=i2I>PXgui^h2uO!3#&bRz9;RtX!}JU7)K{w z8FZlN$L+j>`LY8`GUa&6P|8xy8?ersTIMazPRXVBE@RfghUaWaUGSnir_4tv`*x&M zO)OOL1w*ZS?ykY1hX^G>QN^A{GCd(yp0HC0s}uV3AedIs!T zyTq^-_elNNfcJu81AIAlY=G$c50CZMZujgdjY!=Eb#?MJsCa<7I*B0&daikeRpUH< zbwA42i(qnp+wM9IxlSt{t|nZkc@8(GU1y_*%bn;r5}mEDv*~rQ`ujWf{sP6d-1^_B zMI{67SoZ374=sLe(W{7U_qR4%$NBAVZZBL7e{`I1GrJn(1krIn2Z{^Sad7T=Zjf*dr3D`eE@pca_z#eC8%sdxqn3i>K*_N<1$+j)4txUq8@LLT z_r25yYss^V8gfnL)CF@)GuamH_=DhTisYqejBDD;XTmp~K^a83l^c+}6uYRO4YuTh zmr%F6`4i7zpEOE2WfmnlIbBBUx#?M%GPQW$_+Ifn)%4=7+_dy;hU1yZ4B>~d;>t`J z9qw~T!R1GW4FiKQFosx0y5_{)cRyg77L zIAVIQpXQNkgm+*}RI4rz?V2}r!{+a2-E`v5w|#YzVd(5?U2KHdtI#LbyN?_ItPeS? zeypq) zl5^~>z9E{w5j$QvrWpI(c7ZrK0M*oeVn8!Lu}fLclDA#0IR5`%%l{V{`@h=b_jo5d z_P=BQ=faltjVG$l@&7yie@U`YUgOyRj{Se|*#A~n6*>EV_x8Q`j5qGa*4E`k(&c@N zls-IMAM^6=GPeJ4TE~p>J86bJ9fVJ_CIF1F5mUUNcZ3M zdCGJ)zD^(R_^Ky=|F$8d9TDZ3S0s{^d`?lp6@_#uePEty}`lOt^#_E;D?#f6>%ycDY zGT%HaV!xZ!{F|IL#u@+r9{v9es>o4XSnkCCJMsUH{eLLwf9!Phd!nTs{l6}!ft7W% zy76@VRy9nBlmB1xfF1DsU$tFTWEg;x$nU=l|Nr(oM3e3+DXXgXyL$HO=PoU*Vn~dS zzGduVd_JV5_^(`U|CCCPi@wrT?VnPPfO1dz09(i3($ev#V+$TlyTia0w0q2WQy<_e zsq&~!xXfGPQ|E1iXR>bL^z{~78xYH0mC|{anc4vRe7mgk-M~?j)A)LdoNw#|>wJa9 z#j4BqRC!AHEL4lIH|IOb7!X^f+CwKw+p>LW$o8y|?VCfk7lv#<9kTsq$oAHtZHbep z)n8aK3O92vM^J8}Jk2%zN9`Zwa#eXMd=ovvxBQFmxj8}%zP{RfZWprMDP%i7WP5PP z_IV-OWg**_25rkdpKZ-QamkTf1Pd-twTN!`IqrF;xxcHXQX*2+kAojo%4B;ciqP*` zd`Q7d8{>%BXJ{wZs5F0C*o9IjzqMnnUt2W?)QeMTVYV<4Xh|7Bd7d_&SJ%SxuYWP> z*|#?jQUA)1z4X!gaUu4c5fby!zd}88=8Q1sgpj)Uy1Lrfkf1u03}$RdUEKf`Kk~Kt ze55VB$#QSzToIvvOTzsP!AY&-&JH$iMaM#pv`9y7+(t6$kKJ%?TpO;A5hW zytX9ggU@fvsJ^++igR1E;^nTJD`Q!l?B7uD5-xYVMYYo~H^;Gxd+PMlJMON_J0-Mk z(eaHRu1VsLMyL1EamV56X>?q(E*?e4aqBpGU0jroiwsxqrQ%qqyG^J<$UlA@D>nyQ_bx#1iYPXXhq|1;055_ zU?F%9C~=R(rl|Op`$6W7Q1LBu!KG}okMR(=9DEpj9()9pbFTomf&T)(2A=|d1fK?* za-GkC$AB+@CxNemJ;2w&!QeZfjOE@1$km;-Wd^*p`->8_3Mz>C4}!AroM z;Pv2tLAlN)6mx837&2y(>y&XR&s8l=qKtryqYek9z8NU}b#w3(@JR49uqD_VYz6Xq zGFpQ}KzvGpB(M!A-`mk(Huwkde2`}vC5h%#!71Pg;0*9YP@dVzpp50>K)LP! z#TwtJagSVgE9(3dSrXY#Fd1wPrh+mw;X4Yn1~b89!CX-8XB5~GyaCl?+|BXg8(7Qs3f3hi%#H_>STJ22Sg=md&Udt$d)Z2f9!DQZ$)*U)X_-9E z_@Cd8EKNyEEU21abx#}E-@e*~yx!UvoE)WBUZ}XqJYmD0Mb(K{4Dv~hV+C1n z>qE8&E;RZ5H?7aF3E95e+WwjEUygq%Wcxos+wy&V%YDc=QWvk;90Rm54Q8aYq8%_e zW1>E7^2sS}`m|ZoZt)qn^jmk=d~7LIKjeg(_RHqn7d3y{XT7g)wtaZ`Ifl2yN&9=x z4_@C*_Kz7o-m@w7@}^r}eYVN?Kc26=a=_*Zm%@ulkT z=+*0UANy|Gb&FPhn)Pn+Sxw+ABIRERmmk;h;L+;uv|%16ou5YMS2#_5kGtl5*Z#;D z_nUcJ`WCfHJo0hFI8W6-m>JWy*`mwN%U#v!-dk#ZfW&v%}Y@3hySIr`hMiBeT90-t7nw3P~-eLb*x*On<2OQ+{@Vo@{d&il>BgQ#>AZ zipPV0wE9c4AC>mv*dJa~Em`bGH(=y~T|sEIzz`6sDgZsD@;M0~2YnW(1PLt-Ob0#S zr681(%BRHZE$|52KJX=weFJZRQvW4LUl-U8)_`)Klfc8c2HH^k^JSp$&)0$p;C0{t za29wLI1fA@ya}8DE&!*4e*@tTjk~~Wz`H>>L&clJJ*xare*qU$e+T$aP{t8U!L?k+ zFW~^=M>OOB=kl9T|uq&VCv2F8#>-jRirPPmUa`ZoLU!|uzv-FiQW1p$->DAAk@9&_S|IZo! z*Bk#cw2c7&H>nKkWt%{*$`u{^q|L^F3_cf=v)Bhh*{lC>!MTY&8oZ2m8PiBCh ze}d?H-Tta_x2w=frY+iE`K+M!v!W_b;e^`H?PJHP&kKuuRsIH_6|2wCj#t!vWOSol z#`{L8`Yg%kt@f3Bk~no5Imhr$yVAYY{=)KdPw}4h#kT&nGMWZiL`wqIWVEM9{;_^} zCO;XJf6RP>YTx=AW2)*cHa{7ff2^0KKmFD^XFraTbM~dI=Xs4X&&i0Q+~=E6BW6(p zU%QN3f*0{Q%QHFJ;4p7Zg?wh`DqlyNm6P3-e!duTj4KY*);VWe9B3=6G2tjbWdB*} z8Vr|<M z)@6NcNRIQB@Q~1?aFU0|nmOTO(4Y3{BZo=d38QU&YwrZRu2o%~JYuz9v)a18>XdS9 zU0rYa3e#da>g2qAF@NRx9v94{BHhFK! z1AigUx6)s%u0c3o`TLWv&rM^(wMLouZ)~JqwokOSWj~v1?aGf)m*Xl@oT`qq)p0~R z&81Egxs*TPn5ed|dcWHk?Q?D3wczogPn>kIUhApTT$H-(ob!jehEm5}A8cClxBAQXIHFVU z(HY|kSKR)^n5uuCc`77X6GUt=QG?$oa;{%dGoN%rIUf1FaRa(lkL1{0W zVvYe+-h4yaYero%MV6_Dne9yjTeE#7*a^G_l#c0ous1jdln(9BptLI;p4G1_&8M`> z|3+tuEO)b?;60%9*9)!VR?=^qeKtM_*5x>9SNx{3Jd1`>_J0|S1z!Q%g0F#c9a}-} z(fAxp1pf^R_xuLPPu_S7q#HH90879x!BS9;p9Fpj{ux{k-Ue;}?*QKc?+4!l9{@Lj zkANS8E5Pr;=fR)A)!_euuUOk}f=#%;cfn|I1K1k;1Y~Z2z`wyZ;D5lA!Joi5uqpT4 z0YuMhoC*rh-4#TqYn%oi2lfPoYwiyY1kVD|&#LVV@NBlx&l-s!dRZeCL>FshfaTyw z&})6}2Zypf6-)xrwHlcqI#y#8coR4pybZhn6z;hIybpANGUgf!Zsqz{^H4WanwIgM zQ<5n*KV883WXgQX!<03Y#_~rBpDcWEa1IO6uS)(%Tem8?D}uRd;ig-p!G7M!E8##8S)3 zAs0(19 ze{-xljz-6&esq$2@0%xFwQ9w>C96&y^Xj0-IxqMZyZB_Ozd8S(V^3{=sbsZ-t8>_B-%BI0EEc#z^o8?kx)>cGx%oOT{4e8w zhS8DW|NOp+qS_fi1`4Y>RycdHiO{{@?DZex3dw^AV@1-hUn` zJ(13))!Cvt{gKW_)p092eUOeL3peMOj^opDH{o(LI<8R13F>mG?eE-Za}{@!^VXe( zonA3+yK39Yk8U&WxlkVyd8l)II&MX$=hEet)Nw#Mu1ClH={TVM&GG5T1sNJ^|31{%^CkY zDELd1*3BzO)Grfg7lzc?FX!L%=h>_w3blgL@oPo|})o~;`jv-v`WPfuK&nr&isIQ+G z^-_x)zF7VIKjK>7eb3*2yPQO%I0hY;pyM9)H^-2xbS%*qE?rzOrq?H)qSk*J)-LJZ z@O43=S0Sv+ey17ouB$|>BJaC1D6%9NiVl_iRLqRT1jSJgy%3lsh;nfk@5M{dBDomxQe@Jo18YoGUOjD?$qK24oO!LuE^Y!j8s=XuExg9_wuqLcZI)4 zWpNP=_+)>$T&H4NH_zTO*<+0_vhUO$@CZ)M@&&y15C1s7u%uC5}jZVxM z#oJGw<4E<|OHD5>E-LhTJ;gmrYi+wAyRiP>zvKUR{Qv3Z$iy=Af8+MQ)m25t`WO3r z@A7{?YuW$37~?jlNG$qi#Nu>AR4uVM=4Cas_hsxHyx4I#pK@%qpL9YM^*I1tZe*PX z|3K*v&gdo@`G4Ga*ihiSSntk?*38$ZY>x500 z8AiDJ1BV-E+osd;{5x z_-N;+|2O%2iJf1f>aS~YL-hRHUQBC!eD3noheXAZA6CUi6czq`b*oJeH!1vY)eBdA zb;oQttfQoz2hRC$p=a3VS6(qK0CT}ZOd-wyn1D)_{$w`V8GGt z5gQ1w(CH5jHf|tXoq^2(nC~_6w(#l?48m)epa)E;89A6`nZ{A7n2m6E=LoTDvczl~9I@k*T}?cF0kV9&+1*5U__kdBx|#>ZdMe!niJ zKEPGxD{__8c-_>WWb8igu4n@6cF(W`1OMbWX?qFfO#;3L*S5=*SNn_0d=1-eOlx4b zJHz@gj`n0BMBaFZd;iLA*W)d=nhooEcUkQw(w4o970@+6WBR(*}shdzf; zu0-ZSljejgsVp(`y{4*mh-2p&1V5)4Ldvj+y&}(E?2+vpmlb+mb_hK$yVhIm^GY0i z8AY}wpNix>saD$qVy9@XW0!RuxHHvt;2%}@f0=b1HQu@>u(#I{eE*MxoY&5SJo89S z#BwG@?!P-l?*GY#=aokkk!P?u0P z9gDu~jL%uaG3osH(Q(pF!@M8++y8ze&mksi@=t&KY{<&knU^O$nXv4uCeyK-xrdmj zkG-o3-oEU~otF-|y7l;L)?KIPP{OsiIOx$-BQf#0wz_VxrDZS?tg4dc_q_Bz+k;2bu^7oJU)i>>3Pg00w|2DSw+0eSr! zyoLhOw_gRyi~kyM1b8i&1>OZ_gExW~fpb8H64JiP|8f&}JKL1N0;~S-;NRF@0?P4n zooN(n>|n&PF86l?DE*);PqCliDp2n4SrF>kcn-v6U_1}Xb-n;fdvZ6KU_5r^J&jD$(wq0{fV2%|++l?kKj9gzUP@bpc zy=e!w1v`Kzf}OzjpnRvYZC|h1$D8*Y+HNoQlXm-o^1YC|M&0k(U=Ow>KaRAQ3d)#4 zw(WM!KHTivL)*QO{iNL@umwo|Ae9eD^5Mw!R)KQ8k_Sh|1hV}tSXn{;M7fnRpC7~$ za5ZHkWhcevaAUj;bzJWaH;fhhSUU6Nu1Pl<&86FxHf>&aPjP3$?UwTcviaT4{4tyX z*Bi?7CMTJ5zJ+Zg{h<-6SX{J*Y}@@gi? zwFE%>!T~z@Kg<*-4hQJyf5=mE-ryWZ|5L5Yxnako)F-hV4p5)Q&58f9yQ+w=fWm|9 zo&M)ri{F!Z4rG<_ma&6@+#tqEO8=ueP?J}YKG|M`Hws>Yxg~dL9k*oZfoi!XKL?fY zx#)!?53g)bsw(sQJzm9osWBV_KOJ`xE=Qu9!&=9kggd8mxH(gGwv>*;NKi2lOUt}b z{RdwBK*#4>zkf=8DdR<*?Wp5UbexkeCP~Mg=<=9MVo)9v)qXSf5R;vD4a1J1;+Q=JDIUc*u9d+tDTKrriF8 zK6mMKjH{TaQ|_xcrl#WUo1SrbM?W(0%C&~^S5<#|_rK`Zhl8oDIGmB-AK4xS zUJB-e0sbEg3a3MxiqokAuV;G-Xmd9=vwaWS!r?pw3U{*{ybF91yc>K5TnN4d-V3e+ z?*rF^SAg&=#tiT?@P6ubCYN`K1hD6o?DZSEmhKXx|{LU@X{a0S9U4C%!@JCwUAp*pfJcv!)l!|vvq z-yQF8Uz@tQd~EKavH9kM`G;d6&r&i+yHaz~Q+|!{e^Z`UB<%kxkEfOZaP0pA(|oTZ zjGXvC5f3`?KLqS#=cJEL%u7*uM_svjIfIjvN!*msyAM*zQ7Vbkule`y#Q!)Np!)R5 zZ(ajrbyble0QT1Yx8r|$(%*{T=}d~~TK;gSW&fL3)j(Gv{d4fLyZE2g>>RFL7b*Kn zXD{jOXWjTdT)QP)dqC%d*4YWV_!*s@ud_3C_P;LPMrZ$rYme!8fpG0uoxQ5FOLg{@ z&W@U??Ek9&o;SzkOK5)hv+>zG-~8qdy%s1^yp@ih3YTxv@mV_iKiv2qowi4p$5+Q^ z>3A!h9UHDauj8}A<&Spo(#xip??>j8Wqo;w51)6a^5J8`Da}G35L!!VAUc7ffjAxP z56b(NpV&beUzCQ;*LVUECkDWY|KGph&3C5E zW80EC@l(r!U9WWOV*9sXTWtF{a4^^zJR20-KN0K>rhvjBq=N7Q^8O95k1+ydA0q>l zcXAdu9~2#XV}9hl@gMK6JS1b`37q*qGM)UN5zPM=A^X3&#IgSyPT%AB|DF7w$wO1} z5(i6uP7-*IcKrWpmT_nP4||pmXa0}wlZyK!#P^O<*sEV$_c4_TXkk*B?rcmxbBvJt z_Wa8JWMNOGOo{7W>ho6?`rYKV9m&LJGAmlX!MtgEo4v|qbyWw*{=b#6rTG8A8h~ku;v))A8qc5A53u!jE#+NILF_tbErO=y3ljT z7$A7DH3ohvOUet!$!B&Bi{SHqOZ`p`*4ddl`|x1119kSE&aT$kfjYZd=W`9$-qzXG z``bR$*>BsF{d{p%$0n73%E_4eLbooLq%CZrk0H?6jXFC}$7e;#uGaBlx)>3MFZ*S= zV0FBJj(5;$=MOgCO?Zms6!RTUyt%v&qtKXx4^m_~3qC>K_rgCM2MYfnT7G!oz^Ndz zbj4E$4C!x zY*1nepqkNo8v*HiXhY=($Yt}+&z4=1k?;;<#g&;d#+92qLQPkYm702J`Trtg|GUe5 z)tDd2{{V(?ZfCT30`gQI9+O8@x1i+s3|8~C2 ze9JaR(yaVgGA5HeoM)YH>HqEPqF(ItIM7~VSXuc<6XN!VsH!84fw60d>2JqY8{_cU z_f;E4=ZwsrV)CoHH|pwoH&QpCkvegYsbg2y9VndMU9QkjY2nwpl#A{YVzDi$@u06GQ?!Qh%TmZg=JmYFTD?6sL$ z&(~sR^|G?Ew0$$R#p;!nEzj)bE3^OSckaC~AV(b?lliy88d2wr6$rh|cdp*AA-ddl0NW8*JZYU7rMWA=thd zy1o{N+CM|$fJKCPzMZ=3y}@5Cd;IzyDMwE_BVHfNp|07LI=w}wv*~mdo&KiNsdTzr zuzE{)l-&P4?%xvc6h9P;p#Mz64*vo1{f>ri&cPQzn zL(v0bp!8WF<5qq8$#9kZPX_JBR#$!v=fbmDMhj5>4A}Ck|L^$!|G)D8w~DIJSpQ+vXJ-)))MpV9-CNa}Q zYE&>w`~PK^2ipIx60Z#Jz;89%=%wu?aN3(6z%ol~koF+ijyj<_)_=`onRkFRIfm3d z?J0sk)~=|oGrVi*3@y+ZE~a6L<{@;30ZJ#BP`TVQM)o0E#JH+7r8~%WRcZazI)kd# zu{4LOaXRcVO#?NCvV!u8B18teuY>p;=<4D+twEue44h2-cI_I8f?6WU^NAumZ8%WbXrERaR|br zXW|_LPo-9X0vKOU!&_6?I6>*RP!jC_MF$5;VSeORSfLw!$+b{--79!`xtL>L- zl&uB=x{gYxpv(5AbDhQxP>a zhMrXWOikhZ%CufFd#3fK>8s{?J^R~f$yM80T0?`#K~-s$z7gH4(rAJ+_jgKVTJOqj ztI~Q^9=A*7dcA1bqW`!x?x@sN)h)L=QONz=Z23(Tl$Gt5Uq#>;r8}*AZbA9!iVsP? zd3^I7GH?4H?~$BW;ACqlpYqUkwzkg3*V()}+xCelv7z=HH&SQY>TGSDW}>r&gN^AJ ztcDY8+z6cwsI&cbnop=S9G%Us(=K$e8w*aA=ey^4p*q`EXUpqs_Hgz7?7s~jtk!X< z(o8mHmxVw0pGNhXJU{2H@1(->{7!Ypf5UkEjLrj%P!M#*#?nu3(ei(q$nE5ye_jd_p^@##?7#9i=Q zmj4b%!n>hp6!$~2VKwG~XcbFYmNNc9D90bmY?j03SeG(*r{v>V%rfP%o|M^aqsEOK z2iveLT1`jDd8)A=kQY=e%Zp(g>E%$$dCD4foSpn_mcfj{!ryY74q%)7&GZH9HG^Nm zNca_O4Znu6-Z$_>$ofh<`X0u!{9lMqiNXH;nQ%9BLkerZ)LR-tDes!VNw6850%aV` zE8x-adMM*y-T>P|?y)+~->hZssnL$~M`09v7IuLzz?0xh*791&xDkG-b99Ao!H!Vw zvs4hQ@=)DhxqkjP>^jJEoHvWc^DrE;Z~qdwFTa?>r75lFfq%hVp9kK7IVZV}NB?v5KS%$|&$s*cl;>Y0Dk!sAY}YJp9sTdm*8i-cDntU{ zzWje|{jW$Wn@o)RuJkhM$Hvdc8Af|5rf73k0b09HPWofD`{anfO+Pwcvn%QQL$j%= zTSZr;_1`ni)Tm_Jfc5&>>vgHxR_SjwH9+yp5&K~z#z~>KtT>lSe|29S-^AP^Gfghp ziWgFlOG9#?<`k@Lr_&^KHlI$@(rH&Z8(pU*=(H}K#-q~=g4MuuT9-~63swWuX%9MW zOs75QVy)_8i3Y2s>0+TAF3n7*jXkWirTYDE`uhGiWABekYIb+qvQ`+Ohf5pN`TOX! zu25-0I&JKUsM@t*oi?V^z;u3RIt}~Fc5>XN8y{FP>iM2u-?!+#bEfy&eG?feB1lBo zoSjYTebDs7E=k$lmbaNbHGwfS5k-O-|5mh!Ma*XYZsz-2k76IB&0&_QC)$JLr8F8S zErtx-e>Tfx+A7wE#4@G-j_Ug(TGJ4gv*A$q9*l#c@o;)-d`)y9|4%FrgTKRbU}Fjg zd}{ns-lW4;Fa<8>cY93C1NEZ`93wMn5V{)kWagwkTq9f^iEfp|wette#G{=W}h4`J49z! z2Wy|`>~@{Kp|fvwcC5}`)!CoH+AX27H$G8z=7fjOyJF}u>+c`QgxB28vUwCi}6GX{vG0c3_q z0|-s~f3@a+V%Ctuj1@oUmgF`kWQ138gvVXxE%z4sqPk|B8kJV+^AvlgdIG=whaKBr z$s5kMpRNMxI`MxF?fAb={EwRYA+7ahBYn*ua(zX`h0>DmE~*I7Gc30`tD(PU`@Ylu zuWbCfWBZ4;`Ja9kdyoIo$!Hc?K`EGiwD_UATm>b$c|`@T(tgRIX%||^{L69l@urVHJ_6>MlU7^YjyE5z-A6#;Z=6Ki70o{Q2%JHmlLnDTS1z0J zII5O=_GI@p*6F8~n^bNiu|Lds9sBYDkoNwwtg%T}Ti1r$fSQis-&bf1JK5Z?jDPAf zFVLkzin>gVX21KfNK6pNr=a75ZF9``rM8$6x_&h}+f`@l25ZymG#8z%tJ7k1Hn2{k z3D!2&X*zXHvpa^i)rhd8*O$#%nDF>lD}H;f^-0ep(ShM`X?ekFcA?UYbQ<50>Qj(S zAdQHycUD}p^ZT*s>sC#ie$xHrJ?Io6z6PbtW;tOPQhtc9K~tE|EE5^K@-^rO#Zhb^ z#6LjA)DRy7@in**o((6$SU3yD!y6&Z_$rPlePR5!vn)Oba@-hZD-WmNqj}uG{ucMM z9?7a-jO6(buzU~vkrP^fag`QzsCB5Z_#Vt-Ue33(uQ(n*a$ezOjJ@KF|FM5vNOC4t z6963hKgTrRowk3c?cZtp53rSO-y&!H54(SWe|P3?V-AMd0o3|uc0vJGSP{$N%xS@6qLS{XLFU8&s#U1^N~2uPqmYjdiGFGCID$ zu`*hvD=s6;6_=C{caCdBY-)01LMCHp$GbAKQe3HNiD?;e3DweN?OM`tzQ+YWB_TG| z6`P750)NO}%w+zJPaEy@|NU$A|D|dYg8u&#s|5gX?0?7pkB*oBQXTu>vHu{`5P=u;=q{&2W-0iFG?(+hO= z&tF9^(CGp?9YLoH=yU{~{U5B3@K@0bCMvxktZ!P*t|6GlWO`Ceb zk*W(EOn>q~9pKOGQ||Qrb?kq0ppFr-aY@Ok2`<;*jI0qz8mU?F@yRZy{XcI%+y5E0 zE5D-D8UMqH|L5p`e=Ysb@&B*s=I{9bUsNrmu+_g|qU!k&6@8w2;3W6>+%lI>{Y}i5 zOt-hRfDkF?d7`36n|=FxoniO&C(4TS@vl4fzg1L)#`+gu{Js1Ci~TwLj={nvx_5!czbkC6aeMu5 z<6isrl%$bwoH(Q3D*ad=jcLn{2)nst-qH!jeR$`hJMv#S_sj2a5Z38II-hTyPNcK{ zb^aqdKao)Rjt8s1==3<<7#g~GfWgL*3wErGndCQi|Ni}Lhwsljw2k2Ydm9%~Vz||e z+qO49YN!7%UH2XTU#I=Aj<^4Xrv0BvRf)UU761;W{ojfG;l%zB{mnJf>HnV^pE71} zhSUGQT-WE`wn>xCxSZ82Rci>+%KrFv*MkyoN%?qBsqC`$ZvNUIoc{lE%l_*2zu7a( zF0eu(1Bi{knptP#1Z#WhY{?DE*1P7XdFSW6{QI51KOF!4Z5xjM&M-n{6Gd?iI@@7+ zq}gvWeLwqAvMXnD?tpU#>i8W9S^dALpu|;v@Ug$0_+L)_->LtPawTRYCph*0QE^#b zZ;?~~mvI>^>&6-X)9WkE^|{AKMUS8llSD+#Hpk zBtT0<*aerpvFXGW4?o#|X8AL|V>*BM=hpp2?{_BW4mfw<$hiX{tN*)meNO$~@&9xD z{~Z56XZ+u)>bevEx9<7>nMGA->i_%l|FdKJ?r3Dz8GN8QGodexUBvc1YqE;%i&dh| zYL9rg5(hYNvSaU-QgFNC%Vl>)J8Mkd3jTIwdZrd6mkgcaD=3%WWPM!F)OfwFOw1%E zdmLUr`_PT2IiQo+8}(Q3I_s!kVy^k$_gB7g;#1dtVi-rN-(aX>yawyn7pv^Lb6e;A zbkPl0Z<+jS+UeWx+_>2=bpC$9`t=>^HmO7HHyEscV1>G$$2DBltoZcj2hIBLeP0aO zxoNs#{MG&abZvZJ6F)m5tVx4g`ZvEd>Yjo3jjdn2q+b`q_(b`0esR%N&po|y`E#d? zer?c`T^1u&v{dWglKs!Ir!;QAsqN}n1^x>OA2bZf(EJ1|mytte zGRs7M>2CmA!QSv_cosYc4uQwQc-R&amzMm{@Ds1rI35m%QIIijjm~fkJOy3=PlYbn z4Hm;_DEV>^D96^dg?6DGc1SPIoB1B**{uv?Clo)*Q2J2Dmyw@4WB;V6fk3LF3_0Wf zhG^^`hL(z|&wHMF(BuC~^`&%*WB)t$zhnP9_P=BQJNAEF?*C^NRfos^A5VEJ_J1L> z*#G0ERNDVifI9Yn2eD_4+Va_b(>6V_`G@EWa$2|QOoiU9(tjQL(=#uXubT1Yv_`jm z_Wj2{8b+x44IVE0|9)k!zF6;}2S2Yjx8C$2Cw@70z)g=h_WzpAu_qV3p&!dg*FSKR zx}VKA9UD6^bNsTjH?Xgk7J5U7zDF^VRO4;`skL{(pa-|6j=L|B@;J;9&ayIsU)S z_wRiFf8O`+#Q$;P|5Wcj>CHXT2YSU|D%|9WB)%= z+5T^y70G{!1|WU`tvQzR8Hn>%l+f_z_yw$c@bq@yZhO>w{d?hs8>if{E%-P-xOVdHh<$WW|>465VtD2CHS%Z)tI$Wa45^z`Ucsn@&gdR0MQ4K z5dCRT^a0TcaE0)T#vpzH`S2uoF%-W5ah{5XSHc*Gi-ge&&Vl`)_$>5?3!#jq%d4cu z(!CcVLHQZ?ma)(4hTh;qZ*a!{NsUj=N={34B{6hSN^ z>eT;f6CGq2lbMtH7{(kJY%Er(|2va&2WqOWr@dg<*yJ)<8tHdOx2y75VZ9S1{aF9$o`g|1)h zq3-`0?6@eQvWsVvuh{)kpT@3xlR21iK0?_)l@TtAeX4`1Q~%E)1YJ&`>z*_Ahcos^ z9UuEceE&k@|LgM<7nB$HtOh{G|2M~sGVkO8P9E?V$O9z0tZkh;8h|tY#}V!SYZq0a zkpb}4-TU~Tt&C=oR}i3PUJJvhXZWU;`|^onW2U`nrR5XGOZ0-}6vn!EKB0=w6Rh15 z?0BP>ohI#qug%ST^5<`_UAp?;X&>aDS&y*;-%+Fpb%T#RB{e^I zeWWpteGy?s!QI<;Zg}q>b6l72dTNCGA^K3Ct&W>?QMdm_m0i*y=7s<4{%Klj@Z)~! z#$M6sEWzqEI$cJmQ|R=Xp6Ys=|J1m&Y5N8nmqg!xy63u~{|nu?pE{jIXK(BDmtgI1 zo&FN6KBCj7wEe|TkoV{I%sCHVJ3Q>BpHBZ|%YtdIm+(yzh$Sd?Mt#>3U=L^}ON*RMTTJ>>&+o@)lSNN>Du{>SI`|76U%t*fr){g06IKmV+r z<&S?jb^JS@Zz*hf#}66Ayi)0d20pu`+w$q%9;n#5=!UyiuGRZ7=yaK7KK%bQ+^l zr+FB%I<1^}3V#gCXU^b{gd~y@k{IY}Sh1w1$Um7;(TVxy7-RdYCpj8G%;df?+~0Wl zFV(64*YWzl*#8IL{!hKevi=Xo_IKL<0|i7&TIEY}WymNO@vad=#K-1o7|98gzG^I&1cd6-MaD?~$ zHH)c`SpIFW;Uxz55;~LBGm3J{eOc#Glp2ywRoA#jrTeDlmU=463-VR^QFKJx*}>ES zJSBx*d;NN(A4mPYo`Dij?#nG>2eKbY?_#GHlwd7O-{8IL4Lew;YXig3)z@`)@}brp zbUK30P7Bt)(%Dh{=olCg_K2q>tpC7^AMEr}%MVY^9v`RI9cFit^*<|lF6X@&&+NQx z$gGwVuHJYp&w=L_5jJOMlX@RC{jf_?cDLniW=~Bpj9ckM7!h_y^uz00&At4W#7U11 zpLPAJrw+aDpwmHu)fb+KlJjr2ds61vZ^drByV2H7Yp?zc+b~b*3zzi&>e#;7-zD6A z+N`->-t$zZzW;Hsx>B%l%)eCU`RXSZPkMB~=MVoQdBQI(mvmXmcSCTeh_KGP-*Pjr4Z3i*z&$(K0r3?7it3;dDYij=3uuXBHif*K!=E{*Tc^ zbZ}80QnOsishL@Wo%;XY>;Gc^AAJ08X;kUg$Las;_y9Tmf1UonPXAx0|8Je`|7#Uh zArS#6JNIRe+v9(2l|VI25_d-aOk-||ec!DswxSW{GEd8l{T0q{y2&2@>u3sY*`}1v zoszcmk3asnkbpNOKD8^Og)tzquiCd^Od;Kl?Ik`2x;nMa7T4KEIvYsWmaVhtb+(bN zEmddhYi%Gjl-k;GgKbk@u4yCI*-W8olMUAP(%CvXO(k}SqpAemvp!3Zb zqn^vM)QHxNmd+TH`CRAwZ>#vFhrTbm&UVykR5}f5wX#jewmj?kvql)6TTgrRibt+I zk#Abp=SQc#>9i%Ct*_IZbUrFNO-QGO=rj+VwxRP;30C9LX*S!*46~Wdywc1=lj<>M z8EG=EmrN3iOY&7rXg~6RioF#Bhp>!ktYU*9`x(q?Ot5$;dGIhe5ALs>A2X)*@xJzE z+Xve9d$Z}qo)=rbH9;8zRr(xh*>l`xGY;-Vh1 zCY^Kk3@T82_@nuZqn@fU3cqZ3%GE( zc3~^Zf+5pn8^gRoq_?21CizwbzlqW@gP^arb_OOc_jbB&pvRhH{18d(~n($-f?>uKJjYH7Vx z+O}$Gy;WL$_3jvQ-{s}uooW!-y(-PTPwE%*y!u!DX1{Ri{-vAg_A^vl$TQD3)9rOD zuWxqs^ve6Oyn1@&{i}bedcIZX!;_Q!H>;V>laut;HPd-=lHQ?aI!{j0yVOj#-k0t* z(>YUFzfa9{u1wO0)lBEfN%~kbT|GCsIr2ayeSD2{xj~XXxkkF&AW5H7BVBHg*id#} zkfffm{d${ufwbAmce`GFH|8fIzu4QPt9@3Un%c1K>vc1CSGnC6>RDAi68U)t z$B}FMQPn}8^))+p@aJ#EhV>m;ICsJX(z*^2o9D+?*S>ng(rX|5XOF~HFWk8BABM4= zZDvzZvhpRBPmyGN&wPngpqL7%6GBV!9(1h+!2 z(fAtP4!?z5ld%on1$j1puEF>L-Vgr+Y5J6B>Bse5-Ua`1_%r01j9(zvV(f$~;jfTu zFm}N;kOIm7k^ruS_28?pK70)}gj}1^2y#tEW5~4_O(54`G=rOAbNDfA2|tCAkZUoH zf#1M3kZUlGgYtg0g`B(59&*k`2gtb^CqT~8I1zG=Mn||Cc7|QUsQ1I}kl7ytd%!-B zzy1ATFE|L2<{tw4!Fb4T{viuF?NF7NOIfV1F#;5BeNoC|lrdGKer5dH#hhTMDqE%1NvR`?rS1M45f z^M?)LdKeDhgiYYPuo>I{TfogQ0)7fx!7pHIxD~d6+u(8VN7xSTfF0mYcmmu7qhLJ> zoKCO->$n!ggje+FUYg?_lARDe>eo5 z35UY7U>uBv@h~1Hz+o^Eiq0?$Cc`9{0#jfbOobUR4UUBAFdLo=&xaZCLdZMf&xKhq z4~~Rx$UEcjKt7j3;V_|Y)_SJ>-=SKXY5(^z)2iA3IaO&@#+`LuQXX*K zvc7H8tLu<5f^_>CR(}5r%yfI*%IjNMJ-zaNd|Exd^8WRi7;wL3?>srNhX>V6=gCQW ze9iQ}W_n7^^gd>KR?T$leaWer&Y8;o^J}JaWs*LrW;#z!(&w4!l}5HaP)T1@BVBHg zq%WzFE;mThpRSQEHz?5lpRC>m`}H;rcH92nRWA3?{A}bGJOA*jeO8{D+Q|Ox3$;J% zH8OWsxm|0uUDYchKkwjtl9+Rbh@Ibi)w%taEW2`ggJU<{a{BSDPGMXdl^(nZfb6@#2+w?y&({uADRFwN%MP6^I zX{UY_us&u7HbaBRurl*}ay=z#O_z^GY<_FOZw<`fREpTSEzRXZPl=~|JnKl>G1fX> ztIw(JJ1Vx+YeU4|I3sV1=c?C!D!=Cbn{z)1+eKqgSCw9Sdn5Y4eUB>do$PB4=`zuV z=NHC9c^?yCIHZlv-vnak_~rf$hbleYMNd5!o&Yo8NiY*)5*Z_5 zcX&RGhB;8$8!v>tpbPeaxv(G1g9D%&N;@OZO|>tMgVMgp{qhfm9>_Tx6Cmeol)z-@ zg(;9n`zps)?W@J`8h+>4#&uBMiR+=fm-8XVR_(1*;6j$=ey^cCSpzXzopwRq$syc_ zZ0NKLI_-i-db^-}{|B%Cm;1`xQclWxLjuxzT4}=q(xiNsdV{R%H_ulcM9!cSX$#FXm#eVMTkPU4@xROGmA~Zn zD1B4*(I8gp8`oriGhyYr$G`3T?r96l)^}>Z_xgskJzK{#US}UNNx8b&dVZAc%D)L| zTa|zPVFbSqvKS9ru}qn+>J`cGSeA!F9;vE-NIiqH-5&)ID8oI2KYj zQuPwf&A=B)`7ueogmX~!5{_-)o22}icqLRlqXdp2UFs(sL)A~Dp22Nb^$h9?s-8hz zLHRdLf^2K>?kNAJOJF(6Qa_RUgw#)@PVDD@epB_6%i$F)&xBXOE8#52->RO%->RN+ zHN2MeYoOFu=0d5jFdOsWe8}v-5#9iAg8c2j87_dgK+^oTL8;ID4f30RFYH^S4v~ck2IqOF`BDLsR}2mmXo|KNnQJ{Qm-*K&IIy6pw3*E=LB$ zPf~Ohm-1VU^1r97ZfgKmouHSQM%id?o9|T?f4M3zm)mJ21qBzYZ5u>BR+Uy#K$?`{ zPuppwgpBg!N*1lQ-)yH#ra)St{XSTgr}duuY!d5U7wuV`-4IBxNgHj&0gQMZ`FdLo- z$H0#8JlGlLz>}erho?dpJPnS8(J&XD4)dVgXgBNwxflL3AotHd5EjBga2y;0FM>lM zH_{&u$xl@|>w`%wSHN@NM98%%yFCL=W0`AF=k#AWpNM!)F{D%z^Op$%W+j{`Ch(uNlP))=rW{MQ)w4grm58` zzjO7j#!9_;$(r}y_#!_3wbN#N{e2uTWFgP27U-7f7l4y!A($(zZrIfAHmM>b9gfRH|!3-fszkwgOUk<2YbWs zVPE(I><@p0k`Me0&w|`0l^=wm1I4ji56bgS;k)5+A5r!HkbVC}1@`BEBm3`AChxhVbqq+0AuX^D@MWxg>#nQPzdQZKJ|h<_8E{K`?-Mijo(K3j z5$igod{R?1|N2OmsUP2r@N6jW)DS4&I)_rS-|R>z-|Q&Ju~glGW2m}=eI8 zK=!Tb5bRUcA+q6E(%DAUAzYAhTG_;I$gJuLg>XE}6QBncLH_m^L;hBMfB0LK**sct z`%<>H6uTF*JQbEfvRzfj@IxQVGa&0Ko0)a}vss=DuYpsb+}C{BNeHLVeE%Zblz805 zW(DA2+W!{TjsCxt7i8uD@_t7|%5A5wgPL;rw;{7o`uV=OM189+S8lo68 zsi^P2y_r@pp}=kJpFF`#%l8x(s@~R;PjoZWe6_XpoiJ4LiQB%59=dLJlisgv|L^vt z-<`$t3jPBjCO9>K_8-d*wE%?=v3qZ7ZAM5}3^LbjYe?SA^!Ib}XB>o5AvbvyZ-~VDha+#u;<-Q4}{V#om{r#J5SAk_dJF2z)Ps-j& z=61B#Rg{(FdsMkS#Z0TUJf5$<=}mnnZ^~IvdS0uYi%MR)x9|OXFOQdVj@J39^JUSH za`hhhgNZWDFV_+a<(uUY>RaW#SKnqlly6eX?MRpiW&2@JzRe^kSs-Pd`ZkBd4lGmV zDeZ0qly5W@c82NjBuLq(d~&kjsVt9%^39Hcaz5Mm_F}B{qvy z|8oxxW|Dh-@ZWz?QU1g-tM-3H`2SfKRNemn!utL{v2M8V{RqSO^hx#o6M{$G2JC<0 z`BsfrRUUHc)$3kNdGOxe4X=CkiKN~aZrA4_N0AXkgssi^_X{IN{o}){ zidMh%Jg4zco`(m@bC>|-{#^tmuPK5<;3PN{PKNPt3Y2?06{f&xko->h@ufqt z0m#2pyTB-T8OvOw+#FTcp9$r@T?xm+t6(0S1vzJFbMbR-s@OkY7gg_sy&=zsrK+|;C=jl9lRgTgG(U$ zHXeWr;Zn#xReLx4Qt?#ofRB;B7(NdF4p%_-rQ)iv57nN{K2&@5L+~kn|0jF~J_=ug zPr#QUzo|GYPs6uZ{ug{3u7>Zx=i$5XMYs{NjjA8M0ynd~4t@k*gCD~;;J@LU@Jq

    Z=<7si7486U}f)0j&B{?u+OPAY0IqhlQLKSJj=?f&W7PE4}nc! zENlUXLM}u3-plq0EVqHfpp>b@VLNy(>;N<12`~$Gf>L&$3`fDPFdKG9*luG zP|DZ~p}cb`TFX-Jl;6v(44E_Giu$+uf%T_=D0C6Vt~)!!vg zIf`Y;3mQVn3&LS0lsrYgWyw<{PiO@tPiPJ0+m<{<@`mG}eB+XrNZufM$@wq}N**D3 ziqzM;zhgU+bP1Vb}CRH!zT2#H9b5`|o@-^kRgAJnmb#mc#Y{#{z z`Zw32>fc<8s(06i`J4#%z?jK;vL89lSa=o8gR`J)Hyd)zhot^5=MkFkze)f&0_p(r zCCde=44{|Q_P50PE^gwU$t0BTUmAz_$(3tuCBMlx+|x|2%mdE0mhC*C*jko%Sk|9t zE!%m(WNW!*9&o4119p5k;^)lM-oAEH_$?8hu;D)*SRU}BT$6bmbv<(KXENJb2llaF z(&XEhRnuSt$akS^_H$ulmL)G}4ka&$gnTbbCzQP47?ve3I2KA?&>r&rC=Flof)iPm zdO=6X_oMW|u~6yGo z`SO&(1eVJobsW`>mkcXdJ_k;OBj6;+waUf&xh7)<TFo4z6W z+ir7TIOVO##WRNAKcW4N2lh=b;&|3MsdHq};55abR88G@2jq_!SP$|Ysc)JxRF&s^ zN2)yU51aG*0N4@^gtDI@uodLHQsw(lcr43Yg0dOnVLO&7Q!Cp7q@1U0^~?LVjCW7! z|IT#y??7n2|M9sc`9$y!X#WozE8`r=TO)D*ZB~!}pH|8UMxLp(k443Ki%UH+2EdAj z`xpP;U9>;p|Kn16BxENT?&5qS+8q^L;w|!y^F~FdF+yaTCz^lCnWyl_uzco>sOW?w zQWBC_cUZ9`r^r8^p<71y`=?YmFIb)qDS|Rik_6;r`H*WZV15q zo9%iWYRByn#5oX$m z+$nh&6S8h6_7P^VeFh`Duzd?7Jn~`+@$`|SGqW7X+idrw%(LH$-FSDSt((?f{TXk^-5Bo?VXtoZ=*8Hn_x7LF{6BAZ ze6TIzm2Rvz-B^%u>OP#=V1AeHZ#$*vnMDn6{P^4a`+0BF^^RXR>D#jl-*$J}{?0?U zp78STv{R_`qqcl@-?UASZ2lqof}GZ^I%8zGRr;?(e|qMn@>MgwoYv^J&%XcoN7{Dz zEh4Px#s^l6dcNn^_bs~boaw!GV`NWlA?bI0xAAQM(FwbH{N%ZA_{gum;r%>b($_V- zddCxqFHYUQ`^ukldfxd2G3HhJ&+}KlyZP~JBfn~VSKDPTF1`kxLZx>ZJ|brRnG5>- zmi^3Cla6|x=dw;6cV@wR6JH#D($Wc!q&}ZN^TT=A;VS*I=8FatAL%{b_r~723%$}NnZ>72q51#$eeFek5zVeE5Mt6Grou)g8^{<}$t}|O) zd&!!2j=!eQuIx?iXFkW624o}=VQU7qNN>Du{>SI`|76U%t*a0o!gwbl!p=XdXZhnF zP96Ww=UWO}-thzZkV~C^M2iczC+A)?#aDWM+Q3{l&!wAMf6%~Zw{%-Rz1ss7TNmAM z*UGi{FsSof_u%R6zTNhy_xkt33pY-=V;lLVdf%F#^ysderhfdzf9KqM{2zb&9%FJg z_aY+fvy$g>-kb5v&dY|(YB}NRjR+vRu_JY3LavzgbT8}PSm*NB-XmnNyg@GW(ckGB03W!n~UKUFI%>&$J8uWa%yJQU`dTjON~#?l43b2HZ?vaId#}zwY_WT z$mEpx5wUSe$*Bo0*Wiq-5o*i$_++~vmtFUb=@rvc?cSA{l#D$XpBkSqbmTDpaHS@U zc4fw&qxP4UnD`eO0=z!Yca#w(ufu`Vfh_BkT9nJ1HzCL6_Dw0ZhXGLSfCs7rJn!fL z?wr8dW{o>7Z=EwFsG|oA&2aPprOH*a{3}iVYN?JMkcTdrnUIp`%I@D!^vfKE9NkMU zQ~PbKnR-O&0#*s;4uvRCS2O|JKA#*erM&XrFyzlP=3#A#XLYHi36$r1#(9V?OS)(Q zRMl&lqEwpQ$KA)0o4JkEr}b1eDlPK5Jtc)+Sy9q|{Ns;526G&O(Yjp~rTMwO0%g0~ zzIB_e^XkF+q8oH)UWiulU-P^?=4tIsGl*gf(Fe@Fw)Q#M`oMT9IgUNGcY8`_*XZn* zQjCg-uzOs23p=iBwxQ7rFMas%_!-OeI*d+F(CGuA(g8xH=iI97**l^iUf*i&<-a6O zdUW`#>sLJ$I^9607u7XgC|LXYOFm8XqE9ZK^yq-kAO1)3gkM@N>9Q1C4x=?9tn=>o z-Cb99?$@$ur?-B&Iu89or_Thds|0J;>+JXCr^$H^XXK@41bB9In?D zx04UiZf@nNW?n0O;>FIAd`+eq*wvEdT@H_iGoj?gVwcPINz7*6r+n`W*}frZn8Ioj zdtLHLvColll)ss@uj4M@kAm}|97~p$!~M10qejvv7rS~8Tw8z+kqghNZqJJ?zX)q_ z1L--~^R`XDmE}O2el_a^W7CJXqW=ZQu0M@_L-A5SaVcxs)4P8^^#_*x$aM8re8Q*{ z^@r0QnC%*RkoA9gFGFbmPYM(O#1HU5?f;h?`+sjWbH^V*1Q5p`;Gn;M<;Idz>Mi$~ zVpB0~x{g15S3yAfx6&vfI>0vb+&ty>safX*YMG)` znobK?M1@D!rWmXZtn)co94|J*%#CmKZa-_qy*Ivna`u*|8Z9&ooh_lWjq92Xe5h^I zLv3S+$~Pid8+b!Uc^}$NYrE;T3r>9Hyk`?%f1&LI7*c1(72Oa zuTgOK_MIEv`^OyD<-495;eN<4befmg{*o`7d9nHTSeC^`v$caWu<0e6WId%F41kh{ z4}`LP46~VEnsI){vK;6;a5n2n-Z}(I{u&DpSR*(HZD1aHT5bBkShNRSyT51zgU|=E znFCt>&DMU^LLZRo_ydTAG$KoslEmcv{Jh+fl7jpm_)1o*-iOxy&-Hop^UOd1yjurj z|2ysfdu^F>?0?7pkB(0~c>T|@|I2fn{s5KTm8yFgRhAY1f9!v&1Ph7zUsr5@+rEC& zvagp=HXp})3v)K@^3zwCw!eAps`+2p{ayLpo|@Tz$f`_DjDeB6XxDE;OTKMuk78f> z{3gq?ekqj;;$JEsfC!dr^9|Tsi*JDRGZ3GFk?gN1zh^HtrSC)2$PJR#h8Q&^`RVLd zrEMdvg5T+i#L&7um0y8ez10Fuy&))J`C30dn;b-t4lWg{N4F8NB6vOLz`W{HSQ93_UOyKl|#+tDl@Vecf%xen}gu&bPg%TL0TiXFaoR^CyGPI(5PGi&wP2P_It~t2=~B z&&q5s*A;%jBM;|{?)7;=Ud!u-wTZi*Z%FllIQTvjIz2+C2kCt0L*?snsC6!#4pnb} z+~>h><++}I?a5_55*D5C@7r$}O&=69J*Rw1+S&g+sdt~3ZaVszuaXQS*gicvy(U;) zM%M>Or(^2$j!@~FI-e2IL*_A?<(pX!{Kzu$l=xhzi84;bBsv{Rp9j%b&_tBJk_x%? zD)x`)D`Qwb6Hb8mVyH2?=$4?y+!9@d{uN4Bxe|)5B5^r}Li{wO4~816l22agG;&^M zIaK?3O=mqRvpCPnz8G>|b6A%1nhUewbx_XhdMM|`e${!+hnKM|J|J>l3!t3WO;FBj zA(Zo41aE=NYAnhF@fF#H9@F{~v?pfi|FO1)E@bxs5x)`X2eL2U5$O+dApRri9Dh8s z-6OfG3$EFr)$#w$bNqkz5xe34Tm8R2-+t))e>~-Z4nPN^|2h6Yj{YZVzT^Lwku@|W zJ|T6KD={NEfqsi)24@ubay^7UaP+@`xFUO%sGRbDKf@LMzt{gNOR$hAfOW_JM>L}4 z6!xLg1avmL&c{V(%j<0R%}N`2GH-IpD}NsvIpvSEZtqX__QiftJ_LEW53Oy!<*|CX z|0{d>itq2l=+VXDnA=Tk>WEPj3brI(UjNJ2*3_GOQm=BuI9xV zUpVTH%R2t6vC0Lt%rHWVa(X>t}6UI&JbH~RmwqF`o5EZ((9XeY+SZ(vK zqG5(}?;^tLPP~m!X+%07yI?iUwd7lGB6QT&Hzgfyb$|7Wnwo^&k7a-K2+<?sjPXB+W|NqcB)&Iv+?)3kuy<>f-;($8ef4tNG z&$0i@b#X&$4_)cl|Bn5?f3chG5-ddKf1T<7WBbI|F*;XLuNL>170h{U8Efwj(m62(gtZy(pC^S*?vDh65Y(=qa%JlvRs^7ei6|6{>ZvJ@!fHA?7RwdKzfN4vX!)& zu|gzWe2qG?KZ!dlF~(%yc6^UdL#S(n%D&av)w+0&8ly&h zNbny~j&$1po$){{=Z5^+2Nve;%`s zc>(r#)4!WqfO#*f`U6F%watI5O)FDuEkMR1DV^l5>;okIb!cu^%d)-n)hS^Xn_lXu zwe<vFT)teIm&XYyX8F1Wzhocc%ms%31(`?+<;j$3=i9MQtOAAmqpsX z#ybi2ngaERLv8my5hZs1p5t@q`W6K1cW}1S5eAifH2(6r1#kE2_}S8W*Y`iv!8YT0=m9Fjg=qJ%_Z}e zPB`wvI~U!N|H`>ves_+3To7FxTb-Vw^JCJ*{L%G^(fL#8`o8G;;^@Y|(Zx>D>5Mwv zFj&7T(IZ5IFv|+F3|Y;xH!1%=U zG!@ZT1@vUuiT{hwf4|;+xD}55pXb>Bj{V=KU#~M5TeP$v-$Y^@e`Pb;D1BhgEA7t+ z&56uYqM}1EE~r(49Z~jw52Ga>tMtpYjP}!yGVT3nScF|%?>1%ct4p6uB}wdJsmIw< zGy4yVV!O+(YTE90`@h)yC8ZLf!}JLdyMF@5DJaQTsj{BzpZcER$;&O5#(YVWI+mmr z7UhnUdYz<6yMHJCE~zNaV|Z|-8%WHLnmz)7_J6iz|DVQoV*j5i+jIP0f2+DqE_0vu zzZ_q#v*z{4{Yc-F(b@dL+V(nIJye>`smf;Fb<@usnnbwY z&ib-%Uh~){s|-UoHm1%7*4fND?M7$Ahf324m3E`E)pgp9&Q{mi5qfA!*d`-ivL2m7`h?X;2#Gcgk|uba3bUy z{FlIg!I|(mcny33&V_5>eE1T)5k3U(gj}ot?~u>be-C^Gu7&I2Yw$JrA$%Qv0^fk& z!}s9N@B{b@IcMa(V5|byhN4`3lNPa;d~xEp=8cKfYjMyl0~9wB|2P`=3+)_eU6i zw9JfkjmVvn$G6hTXcXD0t<-Bt%OG?=udChOmp;;L@7r7bzdq}S&i_Z55EncCKYI?W z>F9rGS&se}uSOytnvfN%{vd$wsDunxdPef7*enJUXB=`@W>&`F_;^SEt6nHp`MAVo zdA&tW{~vMTv?>f$_r~%6@sv=X8t?U$=K9>kx*w4h6`gG`%5k+uue~@!StZyJrT^`q zjQ=a?Q0<0^Z_BNtUv_m2&l)h|f|bL^oU#bpMc2me=ujtTkB_52S0}k|dyY5a=upE) ze)SFUZ%&Z=b9?5Thp!zTcGFL%f3ju4wAV|p?TPIW5w_>pzQOtyIXcw3t*fra7C{V; z2s{6*p5>2!ICcCxpKmE_dB+bKXg=ybN3^(bdvfkYQ+%c8rwz z@jnlG{Lj$Z|2Y46Cl!=g0)P|$(^EOHhGYLb_P?0-j{RTaE~*HK+d0u~Mr2OOEg2V^ zo~+Z-t884y{;%0AP<3w{`~P6=f4c+=i2y)7wXXC5vitlj3+JSm#1C59X^)1(Xg|2_ ze&rkEvaY|@_#f7$YJ2&|?jJ*&p^E<@enHLnyQW`|r0*@RhnM4fO6({grjL)r29b1c z1p||t;X!D&tm@+v(OAyF?6V~8kKLGMtl+zssOzvUW8XeLHLt<;+xe7=k9F zUlE;O!J+mO(D@7L?6%`NiapnP_xtXyD?9gV*|gJJzg!)Ms1z!DPUjyLtiQ^ON;is} z)A)GzxMgR~|1IKV_sLJukRGf*mCip(XW#4mWOTa1m+jnMVJ2289l8*BC$%XvwuYltBvlwQ>yW#n8In05Nzzg9skZt@g zL*zFcP7h{negY;=)4ZnbZ(h5rT#SF|10?I%li2lvlidf%hn-x?fJCEmiz=u-DNR7um9c;G z%YBvcKc#M1?wMMUTrzZuub^D@?P*OK{>R273F--DpLDCKRX?aLCW!RUkzr!vVU8oSX0WO=-`yny9E4PlX`Az&6MpXT`r*yo}*41kg!W7qp}YckG)eA3b% z#V`F(aCh?ef(fuM6nz5CLFp52DEb7&nsF|~JT~Zaq{hD;3Fom)n*TO@Qs5+*1E)agA2bch z{fJ>U%NxUxa!Bq+Jt)^I6X#l~XUP4yf#vmZe|3sLeWMFEvt}Qf+VqNTh&-Vjqt}i_ z(lymEvId*u1B-U?7!To1X64W8wEx-eyiWfgWe@ao>i_;Qv#(F8m%4GI$d1%a>mNY> zAG__Zw)%g4@{-W?|M8R;=jBnqHx)p~|HtwFarD2m#Kh#dfYn{|2X=e*)7=7 z|190FPU(M@C0Ixlz`EoA)6a?lw8YW?v4;%#6Z^=hpk9$TM`?hQUFrtN2s^dM{bluP zldXN#^!X9L8qwxN_w%6xidGjPe>1zS!GR=AG(bta$V!v_)u#1{9%k2LY~LTz3L7xD zVQ$YgJZ@ftc?m^kU+Ddf@oS%_t-Hxs$)VE4boxTDdcl1i#h&j!^|6`5&V2CJ8`kXj z_{lz7(6|n@KgP*Pa@_A8ddE9w`$zA;_5N24Z~yN@f5*n=#z%y$kK6cKzL9$BTO$(( z&02Bdr|4@K=n-K<+E;XV;)W$*i>7_mdv2p2G0=mJL9ElKbUIYFI?t5M-+MpZIJ483 zK_|5wJMU=T4V@kutX`?}=h6A+==^XtsOQ*rTH8&xU2x(n=RKSF`U`CzKn&3N=je2+ zx~6*utLJ{8^w~87TckH$H~-^v`+qX#&*?v;^EcD!o;rO~r@IBKujzD5o$jyGb45=Y z!ffW{O7l{BlH`$nm}Pno{Ydit_n~|nn;^|1D&C8f1)s1i`w?Br%umhu={0q!2U$<{ zy9`SHi<_N)2|N&8YAxbkpe|*P&AcxSO2%dGaOR%lFxMOd=(v8S?j)L1eFVQKnWN+5 z(o*A-vy#(NT}iR2@hQov!&D4XmswFC5gV73oSNWr4bI3K!P===@$t#hTR)R6@MwtX z8`CSMXAF&%blFczPM1~U>B2uu5>gXJyE5a?QTwag_5aY?|HXOE_`lBhzkxmT9Q(g+ zfB&lYX-=uP95;JWtBQSYBCj&n3ZMWj*c<6ZE`Z>IKO9;bsePBTf2MyWSn z3FSC1tK)>J9$4s7! zn@1>jTJ(`!yZ`tji-VWvjVi$5x=i+^(g~I<=@q3l(tkJ8>6=~ogY0`3`<~9Z+o|F! zwbi^|#_JqYuDhQRK`onlHqTzBdzeMn5qsiNwmpyM5N3qATotAHxxNB*&*VhawX=>r ziT2ZVMLqU8L?y|!ze^u4u^n0&4I+P6`!Twtr1s>iFw=gnO6zT>ZLdn}UrSm~JI!CU zo-%#pdcWX!@@(xVE7$v|xt_6_>y_n?1C|4y`)cd@FW~_4su5pQy~n%N`eEaEztrax zmu^0Hxn6scI(Fb>zstu4yvsV>OU~((Lern^H=eC54-Z%ld`3&;!7%-sw@Z8l-iLmk z(Nwj5n2VQ_SGv?yLN9&yMdo=%$p+SBZ7xfDyq|J@^8QL@QS}ANb24?l!27D+Tkdt> zdns`5Rzu`8=f8JBqYgE3wzt&iu zW1eT_@5=+01K*o0=_tc=f(hk^@;1l)pSiw^Ox`1JjxRT_h}T`+S8LT;A^YCJFS8rq zXQO=cPF_-cY(?>VH^1O28TI(YE8o?}+|l*H(rKu{YEHpwt~wt!oiC+Mi=BV6XrhyM zU;5RM)seF=k9(%y6W`av_eJNcrqeRNR`;_>gIoGHzcuQff%lEAU%aFrFZo-P=@DT! zT{H87iN|hfQSn@d!525Yx0_+;e837$mG!4>UefCAvCl8NZ2DE-eg43gWK?t6CL%0i z)P#a9iI>;^^0hVf=AP7x8p-S=N$>exyLE{_oYgI3)r%7nZ@cgmy#^et)~nM*yQ}m0 z$NU*b{rkdEcU;!-UmdUidh_hiX}Y1(_=DAeby{z*e>Vjmrrc4Z3{<& zHawX;*{XLNQXgMym7hgcE}k4__~p;xur(Y5rEEM8QXCkgAlZysJ|1SW+{s$*4AWWe zYAut!7$aDg`o3(x9cD9I^>Oi^RL2j8Da3DU{<)gHjos4&&ja5Z6S*4`p97 zAk|glawz+k!rQEeoAqY*i)6Zi^<=-dL$WS|`09Qs)EC2{kof8*s+%MwF`xD`>Fbh(-_fxGj z8o)Bx3|;}7!)ss!ybTg}UY)0(zs>ryip#Iw2dUcmm}P3mejCFMur)j$(skbG2q^}Q z&QRX7E^shB1v3D1v{3CGas=3ZI4-!_{yiTni_`H{oQs0ZxIR z!Kv^EYgw*y8q2%kB~YH1EX(&l9m*KXmqOlEwamL}_*ssFGaz}caV1QLS3&YtV>Zl% z*Feb+=0fSabsfAIE`U?uO;GZIh45NNf~+js55^)uUJX}-ye@+tE!(xff%T`sgc z9K?J($4kd@o5%bG={4IGZNK(FpZ3X|_dMn$%xjpxU^ZrOEzJFy>stN4KBpHt|KH-g za`$*U{2$}O42ldJOMYUs;&nr(4AD19RiK#SFBuEA=khs$sE zpVSS}IR88N=RLP|C{ZelzH}Bu^aDhv~|St34z204Dz>QYw;Jl0Z-M@+?$T11H*bOy|6kl}o%sKCEdD>Wr;vKX?LSWr&g%G)UIPf$*4Np%IvZVQTkEueVEd@*v=*IT zovx3nPQ!3)ZgXrpoi=kr2hlv%J$QP%Z?`?_z5c!M!i`hz*oN;5jjj=4d-hG%^+gTV z=GJLHI=`i%>O7Cy^4WdUHa)WWhv*A(TDR&l(oyvhPaE-0A;UxBLG|`+tTJChi@3HnrE9jem@LZt&7JzVUI>2H0ranp^b|I$4tv(Ih5C8P8@K-Vs^{@CB2{Pm=i z@`e%LG;eRLf8^ERV_50zM4djMv*UF3n$9lM^&`^h55ejTI(OLs_S^t;J1{Zw!+l!(+U6^JV}%axp( znWZA|IzC_3VckKWE{fKChO<4N(0+=h4Zz3;i%9o>O62K|!ucZ-vKpZBvv=lvqEqt9n!< zTq6pKy=7AjJO0bz)EN%7p74y)6P~?!$2fm%+`vhhFDzX1>s@qq*Yzz3*011j=>|Hx zTc;c7?9IBSCmb%_K&N+w^Z7)C?K-o?wU?}U=lE;-?8@HMe&%z;cA%?lL|EO4i5;wN zpwpfHDtdxW_Y0NIu${b8`rn)RteOAHSObCm?{^UzK=Nc_38=UTWXOK$L46dII285a z39va_3q!42)a*;YfS?LFMBtmvoJ8M;t!FXjJe$YvVk4#s8#V3qPQGZl<7+~G2XxwxswEYeo`(N(G2qk+YCg*CnOb zk!}BY-nFeSNc^u9uRB))&`KO#Ij*GTPvRUCiBe9CQb`xFVq_a)dzEIExjAmJ8)DRu9n()faMpSiLeuS33)1!cY1Ol`l-iu)T|Qtpx1(Q*wttu$HHRqmO} zC98FJRNseMSJEWzx~F84xo2x#dmpH;6LX8qc)@?DXN!+F&spLtN6uBx!B~J%AVK1L#E!*3>YAnA{WBE&KdAGIyiPxLQn`P~PYmMc;=DT9Z@1h|Mv|^8M zXexJ0jrFa@tc&Asc~{HS`otcu@vh3c@}A1OpquPi%r(cYlV@tbTmQ%2nZQR;WDUFn z2;q`|sHm(442lW}0THsGgd~t)NMaIBqr)&Wfq^6wk_o4*1A?Nc9HOG4q9OvKBA_B( zsHmuT0V*mUh$4uqEXsQ!iYlenrY zZ&I$RD{oS^!pobKtLn;Id}Y{Rc{9tE7=u=p_6Z|tYi)!z%bVPX)p~h9681=Ba8@#l zXxRg?lFeDk4x*(4^sSQF&5QLko%U+4&zCN|_iXo-L-`h1$?U9T0e@9lo-MO`d}DoH z8Y`LRUsY!JXOYcCQwG<%hS}E9+dyKkvnSun=J?CTXF=JVrRof{;J6qf?$SzND)hl@ zSPBQhF%T1#+E~cGb|u7V6CqAxSS1jEQ{k=fJ$MiN0Nw|8!3W{D@KN{^d>`(ETi}18 z*bdzf#dhc~P;3TcXE@LcHiE-o6IcpQgySG~M+1{#JQN$C#G!1xif!C4ImUi$UyAysu? zFq{pszpC8~%i!%0JE_{8@MbsM*Bz0XBSK&&v178|G@TG0?%05d1iaeZvR*I zFYNaJf&IIYhdnF zEB~)5&o||+EjiCUCWECSCGtv{es!|cIKqa@Qk&w}g2qcxY07YCS!Eo}%&=i{Q)zoi z97Qg#8z@<7Jglzm{w;w`*}ceIMV8)zuB(Yi>v_E?O>(SCzwPoLzZvQ9m%3pP!tH?6bsZ{iOUa$XS zIX(;jZc^E>QLm`=p2!9z`_6`4@y~(DFc)URJa`!#2#erYSOJlD2iRA#VD^Lhc;}KBS(HjY`g4}~z7fodSQ#dY#cfwJSwF0#= zxDcXh5RmrL6HwCK1*g}&`WS-bCry)}Z1_dONj?1%JOPTVxd#+k^E#L?jyQ0cxFI+{ zZVGM=ZV_$`ZWAtaJuI<79<6!DvIdsu?#jAXhpWFEy5`lllQgaAWb&Don8P36cCag{ z&{vk{@|Aj6HfgZeZvXdB%gxKk&cdKtpX^*m|3qo;)@j`06v|0Ji}|flUb175hhin| zQqw<1Q~p`Lkg##OUtj0Ap|qJLY)j&Z(KtWXQS0=ej8Ee zABSs*lR3Y4(ts72pSQ&8FLe}q%U$eBMb7Uc?*o>b$j78@EAxLnB^5=|4-i>ZXH$36 zkTbTXO)PT8csPSL%aY(VNBc&49AljRa$gC*%k@Nm!`!5OqHofwK2X2-RO8z9NnvUE z__VR5WuEeKuZqW%TN%Igswi>!+>q(dVx!7WWLDC5DarJf(6myzwapB_(p*ou zr);!G+E;ST&HI1*?Fw{^)5f|yEM4j=$@RE=Wp4Fb`(J+h?HYbd@s;>JWBtZ2a$o3f z^4mS3=jHgkgesGUpU4ZZ>8i{7`g379o@pGnFplpsj!!d=R~g3xjpOHy6X_>RwLxTAIP|y8n`3uU-yEtk#MHJ%TH=Nu)!+_I?2h9NI5y=vk$|J=5CJI_5@ zy1`aDz<(B9-&wrWaWP|m4PRf{YTmxpJpW@&4jQTbI4e7GM@ya>8!ykL>O6ldU3ZZu zwa4joGJ2itOU)-$VggRK9>|U4JDv=s4(|!&JPD`QtC}YDtsI|@|9+fo*AebSI32cy zH^Ou%^0+K`C(MWQAR-|ppBn;2K351uJ}342d?L9d4^D#j!)b5470c8-#F4u_II+*^Q8nvxGvl`1)4yy4#hQa=XP zz`*3gm)(iHZc7f&Hj(Ftn~7V1GuI8&E=2}Nn9Ube&a+?NQJ-hupwoeK?L}6XaKYi( z2{hLZTt~bMNbfq_x3~sZb07YK@TtlN+H()i7dK`vj&@brNp`yn3}2sfWI=|QPe$%H z=Fctz{9kMTQ-wxR|8I=4pv3x=w(QTA{n_^aQf=9vE&H=&e}5L)pI&=KMf69z``B3b zFMRw@HU-9L`cdHCtON={7vzFwZSp3|SDe{a=ax9Z

    G2$E{rjWUFSnZGZq;A^v&$E)*V*y$y6e6`W*4cP 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%0Dl1mHK8f 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 D73+AF literal 0 HcmV?d00001 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/main.obj b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/main.obj new file mode 100644 index 0000000000000000000000000000000000000000..323cc86f58de4223db0136a1f5c7f96c6e380b83 GIT binary patch literal 31734 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 literal 0 HcmV?d00001 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/mt.dep b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/mt.dep new file mode 100644 index 0000000..56cecb2 --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/mt.dep @@ -0,0 +1 @@ +Manifest resource last updated at 23:22:52,54 on 28/11/2010 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.idb b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.idb new file mode 100644 index 0000000000000000000000000000000000000000..cad23eab38d808d87c64b12eb2068e6521b8d5aa GIT binary patch literal 93184 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 literal 0 HcmV?d00001 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.pdb b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.pdb new file mode 100644 index 0000000000000000000000000000000000000000..0e9fa342b48b64bb6e0dee63a62b057ff7f9b50b GIT binary patch literal 69632 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
    cEzlV^(D<@ zEI;^!)ZPJZ6A!q#mxktPa#2dPiMBsaG}q70TL|{>daGR>jM3ka`q!HJg^Arn+v(uf zmb5<&?H}}beu#_@GIO+GPhG>z44zER5e;1ZP%~FY^mVPPChHCpZr4ED+lhD6!0kUF z>{efCR<45P1YHU50`M+Je3>ufgZgp~#$4O3^+k_JOG=F;2i(4wg73{eyay0$Q-Ll0 zEO*=6jV?K{%@J%iv$|K(KPL+|m*(n6M-Q&L5lt-Txn_JQn;c1SPXX>HNfrx?Uu%Lq z!fPA+UQaa5gQk5V*zNvMk2|LbHWz0-4yZ=zUX8jph@N(AmA9KwM@NG+=$R{@2wi-@ z?TOA?tGfP(EZV%tOJ*rASv*8^nSvbG5qvJ}x*r<>&e3*b!ufdO*F3Y2`!I1k$w>+D zD)MO7*n{a4;S4S6DW%qQ!GhvL-t+W+(#%4LG`a&Y&f2K; zN4=~K&UX+^XB$l~NS;Q#DJ|Y)hP>HEw7CQs_%G3Z1aMp@+q?3bD_Ex!U&g|hszmop zJpdx%aZaZE_d{%X)@=#F^&X>f#8tZ`slsz zsc}s1H6rwszE8MaWVjV27J6{+Bm4Ur;>lK`=}2h$8L4}r zsrzACGos0GXtL6SD=r6cu729YWcY*hDxz1G=wkQbs+BGo@=1)`jU(5xYRb=i`BO{& z)DGGZEW?21<;a+<`O8M}!l!=TCcM3W1Uvr~j~QrfjS(>j{7bji79 zT{(A(V714GLk(8BCP=XAeH{AD6TQDKGuWPSm|)omEDI!)u5mz*oqI$u*!})s=v5rq z)*cJ$dGPtd(dAtnV{Gi~)Osmnbg*ro4_x;XjYmM^0fNniTgR_2MH{WHEvNoY{nYPG z=&@DJNUp55Gtke65#EEq`)x9Y)s+5{MR;v6ymDhFdo;TuqM5}NGnyrWj%0i&E8hdD zAw!u;}8-f@+fZ<0vo9g5786F|ej0>wm1DOLz?cuV2X6Sxb*|gmV~| z?H?t@t=natjsbQ#`q^LVJfymeI*mw{&NEpmOxz`HXx{C@KcAbE_?Y-V6#g&q=sVt{ z@4dv=du1fkvALq?n<+n2ejfc!V7gqT)={sV{E<)F5-o+R9TV+)b?iMyxSoqKVI}dR zKfIVOI9+2-DfEvUBYjWn=%QoYS`Ysph<1ITT}R^6S-?F!l5N|sr(?XSEgHFUq8WXP z=zAaZJ)LOM51QQW;oP0zKEvQHN-XyHUO(ci{jNv0V2)w`BXi^>?w0zlaU~tcJR3Z^ zS8bir#rkB-lD}$8-x@%$p9$=Vo$f3QfoE^h{-?9qI;JZ9^k*;!@Vu?qB*g8>F;$VFRzgt#*xEK#7li{ zTn%aCX2Q8QI2RGl>kVf)0wsAIZ1Pw=?PpIOQ`d#$vk;OA`B*zxPx3TP#+Ynbe<#7; z3;36Ma`6{Dk!H+d_n~?|u4DE;#Iw8L*}Wdmc8Kno(uVa*_k%hrmloA4$e)_#+pXsZ z7YILBUs7&gN5(k2pVi~g1D@KK5Y3vJKKN8JdzgC+*$-apL^Ibu>s0alH2A*9ll5Oo zCS%Ct(;n>O$kB%;l3F3)~;+V|G8N z$3yyE=6uh#dkCh6#;Yq5k9&09;=x<$5%>Nn4IVU%WWZwHXEKl%d>rw}W>2q4OdviC zgb&X}^sxH?nJH8L3xdz!ll*G>hh3i!pNh$N*FZ9`J~CEVTzYTQgG9@z@b}vYC)+pm zShGV!4~ywe^og5AYuEn%6w@CH6SYKdS3U$%@TSMZpNSSVp~cgLQv;KaoY)qR9^E~; z+eiBNTH$jt`uK;$$87lcR|va$d35~LBaN(J8Og~2*?PPpxD1GBI!nr<c<@YZIF7v~!ML_s}{z7OJAF z4OczWXq@<(@F^)M(vR>toNVjiQCx;9GG;cB z{?&@;S=Z#dQAQQQYlY#J6Wc{_?*;A|1a~Fi9^k?K7SXIbG%F%)S47*F6Tg;;Uwa+6 zB?`f2jlKW^8Pvw%s@DvGfYkc6qejpZGb^ z^pBj_bZI+0o>}Y(XhG^aknx9DOmqYzzn^ga^eMmLcrKx1?plBZMo0gZ%G#MO6 zG$}^^xs7;PEM7kC@KXEr9(cK)U@i;HlZj>%Fs2lXX5r%_-$x@?9QDRqU z`xNdJ^Cb3X8$Ft}L&jATtC#-XTrxgDybkBUW?X%)Nqp>KeEc=|f#`8F^w>=Fjv2jk zVtWXu(#X+z;>UR8e5?oiAD*1t6{@Q`9D5!V1j~pQOW?(4#OIab^LCF97Yd)S*4B$` z!LW;Hm4<9?@@zX(bkJian+xR=lYi$(wfK?alnt4e53NUGJBWvijE6zchGgJIWZ*l3tt7A= zB)W_>UdECq5lvf(rtc8kdXLOuQa2ZMZx>#!?5Wm`h}I{F*3T2X`RK!cN9x++r5sZ? zBPb?bbb%N5dT{k4TJMI|7ZR;ni&lw+M~^`@J2qc>w`nQ)_d!qHpFKLiPxKgL{F{@^ z4b=^gJ=yT#H&5F=i4SYUhv$$>+aFSG2SFaLK}-~_N(e8zUhhHHG4BS^Ds{~%Y3?5^ z6O4^jA{euPaRc$}czCvp=-x(juR+=_fwsRV`qf09-V>hoSkmgJ`<4aL{}!STUPLnbhM1}&x8Bl|GbY988e9@<{tb2K}8z6W58-}hk8iSqiopX! zx3;3&G~)eQ@xDz6kJUu+Cc)=xqS+kbGZ&na72W%;(b$Jn6HZolY1(ks8vEp|r@DL^ zRa<`VC^*90Y}Pb)wcmGde@AHhpNZ~;z<4|HwG({(gYcXs+s{TDjR=mtAspvLJhD9W zxT9}KYx$=+1JR%MF7?Vp#~R3Qi}&3++;XITg&_UhqhnLp&l!e8ZfrQ6=eTRho*oAj@a=wEkuwjD>XT^7RDljvI=ePapHCIj01P4d@X^4E;; zo&w$l1m7hge5<8C8^0d(aBoLyPe$#ViKpwt(?Wvlq7bfAi7si-r8}uV&eYF}O(43* zp!*`h;Ocwl8w|CBb4mR;>Ib`y-9D9Tc@8^rvx2mL4%)BUHl@2AvjQv~#Mg^R?Etk0 z5$==)HDa@$dI`z-P7FL-u0X}=)U{tkjM2m1X(d_P@$f0@+( z6ZI>PIMz3-ZI*EDPx9Oz3;g>PT@i(OQZ>W7e!9LnxKQVaY{bT!xWHd6Rubw~D+ZuN>2`{(H#@$8J(QJ5;%_)8x5}3Kvf< zo;`EL;UTmd`F;K6&Q!MYHD4n!bGY+eEbkBv+-+T^Etu)FAZyrnM+nk^GlY^U9`Ml@rvbv{#i7mVELS7 zv!~B04i-+IyC@hWf7Z-iwpi2-1{sij3wjM76Qtg>VDZf93uY^Ak!Qus<()be6s%aZ zX!^q0vkF?aY&pH4)&J)=cIuRB?EhkeRsYvpESp_8QxsqRe^cbhHpft#UC=Iwz91B4 zwQ3!Gx$VDR=+tR-^tCqsdZAON!su)NYPz+frB>0cS}CkxW~a$ZmMtz?Hhp1!;oJqY zizhFfJ9F9M;>Cr_^H+_k{%~- zSTMI}j#Lc&dh|u}mg40z=1woxci7*K{G#iX)tbeNX3HCn{(AH`^OiLlV|@dDu|Ins zDQ|x(D4xD@?xLdNf>h1XM;aeFd-;lGi^fi0utFLduxQro)xq@QnOdiy*#0KEE!1I5 zdXM}ntAYaajyA33J@(g>$U(uZxrK$gc^ip>zE{lFAM&rFg{jwdBl~+$U^>Xr6`_A> zNz+uIW1so0U_nqYWcuoYV(shts*Y0H9)2~yXk&Oye+`U&3TNT`R2}{82!Q2lWQdga zq<&cR?c0J_`?g0o>T9XZQZL%yLmW~oj&RT|Qbkk09o<5K$ZP#(FCVPq(`bWJJS{$< z*kB%Y%nCthR}>7HyI{fGQ4-sNaT@j`c`jIW)VCwQ2L-D_PTAjsRWpkrto==eT@}*P z{+@bOA*l@ds)2`QQ*=|!3Y-7e4!la+Y(7_Rsxf^y#cROO!4|#!DG>f&OB`Fl2&V6)yQ0Q6i4b9C-oepp9SYH zz}lW1L#~%&$VUi<;lS_%!O$&)VYKI5A`kciEcUzQdd*qS{Y%RglBWrVVZgAJ93LMq z$H%`Be8-3IeM)@D#d$_W!KlZ%x>ybM8o-axFZjTHoU7MPULn|r0{cdCoZ1%0soxN6 z*}ztfVEX~s5`xWjjNZZUuN~CJ_V(D^9>e!A*LHG)w+a3s27h5V*S@=CsmMHSai0%zvVsbJmNEgZ?M5vlqg5|w*>zco@_oua@ARKm09Un zUG4cpn(<|0M*D~_R@d*1FS)^Y1ot51q6Wd;9JsrL+E?wOpA{Y^?GHrz3yE)MiSDfJx4o-VC)Z!?~&|uk?f3?`r$llHgwT3uOZ=3o#YpT26=a?pSzmdxNgw=9#H+Ut&s~>o^CipVoZGVCo^};aKOlXkHTujlg6m7*nnHN#tfTtmtd`Klj?;F$ z$;I``frM8&cw*iIqq|BLl=2=;Qo-kEsX2cE8%_QU8;Z((V^+Bw5 zyA&piS)iDuo8)Y`2k$ykHxqSB-r(M6wL(V6O51kaOX-w4R_rA{^@dlA1-rcmQ$gA1 zg_RdS^f|`%bFH%pmNbJUKe(1;>T_gjs)y(K(zbdQ)h8xxze;>LPkh-;YJP^AYlWNT zuiSK>nkGNXftzOH2gyYEc*^E!huJsC{JaEPT(Iqx`mU^L&gXmhZY6D|OPk9)+w~zi z>Ml7tpJcGC=~oL94|uTd6g;Yd-EYaSX2}pd3HkF4@nD0FH6FYjNIqL4pO+KNl`+Ph z>S=o~(J(1^b4R*8y%wH6O>*-oaOH$m*v+hBnQyJv%dg5aj`1mcs{2?&kOuRZ? zw(ktD4iR42!fPGLK~KrS&!qNysQn+pEdaNM#K(=|aNkR69|ye#d;ENpaQ@ZY6Oj{pMA~-cBv*1$g5Y`!xV|Tx^~s4(6U-f;S83Ar zyJ-7r;&U(Y`AXice`9-1*S{e4)pOHl$uf zsdpmr;3DxLQ|h_)>#YorPZG662Uq5^&}T{suHmAkeNTA*{f21yCiHxQ4Z<*@VPozO)%>fg^NNS*)bwbym}d!hLPMg zMecHTA3L6>+6aQi$j*BaE$w-qez&AP;X6?HZXvi{0IqQaR}Cl`REe?wZnc}J4r`hL)TqXNl z`)r?5axd{&gnJse?rWt_PBd2x?J>rm0)@Xm@X7du49I}f~g*G-9&i* z4c_^JJA9mI$CWL(uJRPYUQe)pNbo%id|L&dOA|eo8$)v1(B$-;*fNrpA(EAQ38%ln zX{V=dD`ds`W1qj(F4RZACcK^kuh)ns^+l6M2(~}XF+dRH6ThzzzdI5yu7nrA5?oIM z*KH)DwM`Z>V;>RR4Fvad1ouzC{g2dkW$g*6{Uqw&Px4q(>g)58%rQLib*T6{i12R< z{+UbM$Lf!veg~rMm7?u;gnJ|5zLsRM0mjVti8gSrzKwT^p%>V?jxvsGx2hmcv%$TWY0g_7*17#UkI+> z4X(E7IUl*l#iqjPL!zf#B{mrwM{=-5a?qG?dKjFRMsVBl@N#%@70F6{WaaaSp7uCQ z&$KENPR)ckdl39>1pj9w z46ZiqmD_c7`0!k0IFG3HI(pub<)dVuJBbVBC+aMf>Mv$oVHE zYju&e7KCF(a4aC$+Y9#91lt|Jb_4PHYVkVJ!99MDl#C50n*0P!jw6^W0P|de`8dJ+ zB+;uS^g5kzx*eQ)No`l(DkrteoBBcUg48G>*|VP5IqOCtfVE^7=KPUM!X@4`? zzm2rN2kp-%+Lt!?GlCC@r`yHT?L?a{qRsuJ?k%XhO6syP_QD8fo3rl>=iFcr@oTjB zl^lI+7VNgopvR2_b63IqB*A<$Fn1w2tBsr;CR$x1TAe|3)|qB>zj7zR)m?CvA)GD& zr|Tkm+5P*sMz5~PmkF<%!0SO$yQkE?*yB@ef@_n(l^0w_IOiD7g^9fc_aEjQ@Xy41 z1ow@=J&)vIjO5@+!l{>V+UntSKf&~k(Xwp(OM+=PFjacOy&t+(GVl_?)LSs!;K9@~ zqOt{fB7vwb3dF&LsYf6@Ts~+0eNqbA$E4 zr-V~K;nbRFyhAj82<_T^rPV6OoL>|rz9sGUmv)Clbhcy1MMh`2@15}d3VaWfwg*Vt zrI)&o|93&>X9(_WoEMFYV6(g~L&t&2XN{yW0? zOT+oA^qYvsa2+K1t%>}0 zBDhBi?p&#>d~JL3t64N%TZmTYL#s20|JRHE;|Skt!M7K|G+HpVBwBt3Ex#h(PY~~K z@aQy)V7dmFZj)`p=iBza=FjE{I!SEGJi31%>uK?EH*36I2>=h0QM}!ZD6T)fXQ{l|;>2OXsKYTr05xx~} z3^#>a!d>CV;g{i|@I?4+cs4vAUJZW_(6N6#Nz0SgCfyO`jB-Z>qhe8+s8UoTsuwkg8bvLlc2U=;S2Qph7LABT zMq{E$(c{s~==o@FG%uPTEsj=1Z$%rU_o6+~-sq#~<7i*>Y4myYMYKOU5PcOLj1EU% zM@OS$(TV7r=u~t%`ZoG5x*T1Jeu}O}*P`puuhDPO&FJ^Yw}B09lC?I4O=HvA^!65e ztIcR{v$xwUHml8U@31*-E}O^ZwfSuUThQKR3)#Z9s4ZrT+w!)OtzxU$8n%|LW9!?7 zwy|wyTi8yvv+ZiT*&g;j+spR0eQiHGzz(#7?frJB9cD+^2kb~Y+CFF>vJ>qjJK0XL z)9hpR3Hzjd%FeK}?9=vH`<#8=&bD*xi}oe^vYlsNv9H<%cA;Hv-?XdkTD#u9V>jB( zcB_5Q?zFq?2lhkzvE64sw_n-=_Mkm%kJ#h(8#_ICI+z{I4Hg8i2P=X#!8^g`U`Oyl z@JaAR@aI$d=TrLUQ~GCF`e#}CXIc8^7yr*M{-5>IKkKFcTlLZ_Z9MNysFz<1enkr@ zwX}uU+KJmcb){9)%fzjU3*j%JNxB7Xic)A*R7AU?x@uXpNxCm-VA6wVSG1+C%0qAvj2t5sp(YJ8KSo9{8K@UQA^cp;co`Q|& z9rzYK0xi(?-;lI9>0HteN&ilJ{akcD`ab$0`uE%AC2UDs+LrmZ+TmmDSo??_|9{aQ zf6XqoOYBnn=k@)gevUt{@4w!!^yl;Yuk`2rSw8$({<1&x&+_+Q>B0MdFMqFe@w_Y; zZ5d#`k4oc<_pi#~_jwo(^}KKGS$os|@&DAmj*vE6ki0Bv&6znd+i#HD{G(8ydz}CL zCr>Oq~CnUh?7u2$VaId*-2W2GAOD}(o!3*r z3&Fx*Ww0UG8SD!V1*d~cfp0RH%&51GK>O%=@C#ZCvRg zNi&mXB|ZIjt*4b3omrRk=f3~E=KovI+Oekbd%o0r!}~{}0p8uhc(-z6hThHoR{uSJ zr~gFopZWDcrg-1EQWQw>kHQByHon)dVJfL2DgOBX4|>IUl}}M2#Xkz4b&K;F83h=- z`{VyxA}^MQIa4Gr8ym&fp@G*mVcb@ZOAWwzj$3D zUm*Fr1NtMaupRfwzj%LfsvOX#;kKKuCF`DW4afSa?MQE@9-hV|gu#DzTx;;C@>=}< zySEJje+$r3_Piyyd)ycJ)>vb5om~gTmKPOAGW@NyE5RRAJl4BdW|x7FP#iz;w-jwq z&+Df+?LV+be5)~s7q~l~t<=@P%&PWv@TMw#YT=8~p7p$1iu0b~*+)$ab7(5g{5)@w zT?Af4@$|ykm*#o76~9IJ0*qpNUTW~f@njJGD#o`l=X~Yl>%%wB^XA+6;MW=^bG)qh zdj&mqp7&S9Zx{Y7#?C$OZN*t@@w{j3GvMiV#>#jKc zaKpoooeADR@tneEVC3HO?o>Rt@TV}Qk7rNuyuzp3>EPcqNItIu!k>f|fah&f{4U{7 zKtI6q7Ajs?_~Z6*@TrOy75*4B3OsM9;>CndgRX(+wNf0fEB>b1so-T5FDZNqv=dMQ zDPBtWWaus6eNnuO@JH>V;3w-RpBGH|@HYvX4xab1;!x4U-$eAepmwKtMd1_d1n{R6 z$D@J2@#v-TyupfB6aEPLZ}2=TUPJgeI}W_G;lMv+n_j#2>gwP=7;CKtawx5qwFZ~#}scSd?fm* zalVST5dHwPNN^t&N97ZLBha($dG{y|H75KGx5L5nD2@(z{0)Of3jFaXj><3ohC)}x z^G@O96XjJ$;X~{Y@Lh_dVSvB;q1}S}sCYNwgQ52Vf2NA}5I)Eb0-vP#eZmJqQ^xc9 zDBer>0O-)b22=4q!u#9);CCzDUwA)g-FRL`#Rm!R3w<2V`wcfc!f%$t_Y3c1`+%QP ze5ml=(BMIhUh!eVdqKAcdbNrV7v9tM1bXgk;azMO@N9~Y7v33KO1K7!PY~V-{ibNG)lR-X zlZ1D)9l`e~K3RAN+X4Iy#it2xZ`*@ErT7!V+u3&D4=4_GYW%geZNWP!K0|mL+XlR$ z;xmP}wynWSDE_qYd+oj8w=4dv@K&}JxKaE$;Vo@T@Utis63@f)!dpO-%kw@}`~~67 zZFBG~iq8?=47y+V$}9eo@TRsY_*BJT7T&}*0Ux0FeBq6uMdo?U6kjO3k!=KCLGjmw zH-z39-fP8Q7v8`&0MDTK65;isspfe%YbKxXGU4@XJ@9jiFBe|d)&>7u@s+~sK<5p9 zV-#N{ytb_kzEttm!fQcG4qsu#*9xy`Yl4qae4X$b(5r);iQ;byzsKGKUSIJI!mB|O z&-02azDamhTNON$;#-ARf$kpOe|#ho_x(NLm2GA4vx@H!UJ2TM(Cbuur|^olBKU`j zzc0LktpL7G@!i79LvIkZ7sdAoFK5ev&r*D^@Upfn_!z}M5?%%)94IFh|3r9cjD4WK zr}#eMrEDqi8j61=yd*|TJg<=AUkEQ@OMquk{DAPg?cLzlt0!N#gTgsx=y|6VKP9;kQG#*7NRA zJeBa( zfzB`LV~S@Ho*vr3o;O|bY{Jvobl_tYze9Lh=ntbdrFahEX`or`d0iFHB|NoF4c=Js zJi=4iRN&PW&nrBoO$lCH@%+M5KzA9}Q}Kerq0|hXLGe3YcY6)z+_33||;cd2Uf zbuJo^A#^4+_ygXyNZ_*?r}i#Cg#QlxY_!)D zFDv{eG`CT&P`teG8_^B$v5Hp|{u^|?QTtWAlJH-l1&;focopHlM8AMHQoNe*>(Cqb zyz+|I5Pl6B<>&=ayteS4qo2VuDPC9jRp_9jJ*aqn;Xgq;-Sci#NxuFKg#Q@*2!28F zM#8T^pWXAmR=kPu%g}U({|CjJ3cnOx0^g;0GvODbi{NV%Zz22#jJ@OfE8a@@_ZXG; zyeAZIBm6>i0eqC=?S-GmIKJogRlK9{bJ02Q_KJ5Fel|J_-bC>(!oPzKzvtCaysPkU zq1})6o#NevpNY2E#|ZxlmKAtk6dx=6uhCz@+bI5s z@B^^Lz%b~gufHL1D;LsIl|wL-Ud&h@_$MA`e;4)Rdm)sNIUvv;p?Jx z0N*M8itx8!vxDoY_^ZO#Mr*-$D!x$onrIF9D#c$DzB*bBz7RZd-xmvC6|Dk)LGjmx zzZty=K1=Z>!dFHs!6zubRQL*57vX!M_%h+kqvhaz6@Nqc8_^r!ofKa#d|9*%yrtqR zgfESjf;UoprSK)u67VA65OO`K9qD;Hl6E9{UJjLhmGG@eTft{kNKU^-_|N8N13wCR zlKET0ubQjijqXV1>xKVhegYq>^4Tc-in#**R>9=-n}uICm%$Uh7}iQfKe#)EzAEK_$QQ_a1Z@?4& zw3*Kd;U~-q@WebA;rA4W5{% zi~0W`{E#^Wo|spS{F3m4(9T2sQ0@1M@UP5Q;E8$6nEof>2h0KR#QbsOKMUV)_Jb$p z{7dsC_)2yBzX|``d=8$NcarIE3jfr63cf(4_X0lW`^-M@S&9e3KQW(xC+5~< zKB4fBVAY6vzDgen|ImC0{-WY3gzq(b!Jkk(mGC`g4|rlu7UrK?_-?Zse7(B!vje=B+HZQ{+hLc9cB{(&7UA2>Ht^Dg*Gwcp!>Z!{ahYpVTb5&n*O2Ry6d z*@eGt-Uh#fL6<~%l~ee7vmX2t#q$VX2U}FnTdjD0;cuC@!2hE7ox<0ewcvvlzf1UP zvl_gl;)R5-GONH-DqdLlo90dMGZ@HA9A^>XD`6dr=ST6P!dI9T;42g_CVaVB4&GPs z;=-4iW#DBMzgzfHvlQGYUPAa1vjqHE#Y+ia3_V(mXJtx0&eFmc!IBs6OW|Z*M)-^7 zMexTIFDHDCnFBsl@e0CUFfV|2RJ@Y#*=9C)J;kdCf8IO~UQF?-!e_$5*z+3!DcXc5yeLcA7loBXI6Zq@PTF^xUcvq;r*e> z?s-3`dd_I!{Y*dbENPRk&x69do9^JhrAg)w3GZgQfkznRPLwlagm;0RKHlr}$$Xsf z&ZaZ?3dP3@?*z+!&zq+BMByD_^N)HRD!qyP9~IugbO3Ls_!Qyo;Uxh5MT$=o-VXi) zP!Cc3ap7%ETkvD*e4iBF1|A2{kFEGq!dt@^0s0RWpDFxab1(Q{#h(`53f>DaZl(A$ z!dsXY;N=v5R(NxGJV5(D_&UXx3NK=c zfX`R_4dI1NVel!6uMmDGd{&^mRs2oi1!2L3=TPz0!VAF91^R6iUn@L6JYe8@D!xv5 zZg|LmzK!B<3(p1L85ozzpM1U>gy)1;4UE$%{;u#G@V9~bzv7#PXE)iwpHY0P@N6a< z_(;Xy6Q0#%1@El*4&hl$7Vv6{zc2iDb31rG#diz84IV)7-YLFEcqWqx{8GN;^ZHPD zMw1cz3&lScehYkwVBAgdeZte5^x&^5{+aM}CLQ>6#lH}q)}#d=qWFH{si9|z(OAX* zDm)c5P;tJB9~7R_qy#Ud_+jBGObYOHiXRaknF##nyvgT#OgNNT!H+3^LU?FG@C}Ne z6mIZ`{z=783-@Gdp=pYLEByE1cgUfNpA~*HxCz-s@$yd7(KKBpZ67trxE^T@Fn;h#rgi=O@zM`d@mH| zdx(-J_!PXi;8nfaTD8inE+W{}Q~xK)X-zyu!B!+rb}KoaHrg z2v{x-Q=H{KYKFl!@Ro|-C45V;1-zW%{H~zI4L>ya-YL%S6SOUZP2hpz#f85cuv|Zx zGx>b^{X@wB&3Rl6#rYleym#Q~2IarvWrV*S@Ov^xaek-K(ht^yk5jyY@U_8O@ZO5E zet;1Z_|n1sRlJJu)d9bIl@zZgd{wXtJg4H-g|CF}Kh8;U)?@Hw!dnl@4b=};Tln%| zIru@v>k5A(;CFkg;`N0u4fvg3s(3@;OM)ff&nn(n`0MZ!gztjlO@%KG7K67@yt(j& zuwL-I>Wa4%z5pJFpsB1l>xKA21+RiEdtKzJGqem!UeeHF{y9u8S??|`~iuVxye8BqSc*T1P|4Z-}@F9x# z7XDno`e_%%`v{*E%mS~fcwgZ&gPGu^6lZ$_HUD55cwWW(3!fTH1<$1T0O3;t*7rYE z<>x@*lY&X$8x?1}2tBLN?Z#Ild-8k4_7z&7!6V?u6lZ%3Z$vN-{A0!0u0xM;Fcy5P z;tvQP6N~|0syN$|(1#4zewwW~+ojNl3`T<|e6g|ritSjm5Q9 z_}wFaMELMvIC#RxAUWIND5-*B;0b?>9^ zub>zByV;WO*E7O<20g(Oe!Q9fIpOyO_kqt;>7N(gE$9ZG@D0!OF9`1%bOj%x(!VIY zOV9;8;oqO>=L+u}bOvv(($5p#A?N^}m`j4`=L>Hivx5Scs(>fvb0mLTc;%oncw*j4@(sc(1(m?(rA~fM-W6Uks0iLh@y)_31QozX zsD6yC!pjC_!7D5Np71h38SoNWlk?vpymU|+oO94dca-{_XsZ$lmJi6>C1dR6kaSS2A-IQnEYemMT4T?oG&`D-%o@W3fLcfF-0=pC%iyV z06Z~AJ@ffgc#a?k_&k-*XTt9Y?f{>z_~*j223f(!D*lD=EI}6V`xXCEc;+B8_!66Z zJo|-b3NnFzr0UfNgx?z63f@lT^H<>+f(+o)`AD4aLE-6wbl@B#Pw>OS(*|k5C#d}% z5uPeY1x}saMEYaGQwAx)shgPKCxoX6Qh-y(E5T0+hnf#~dcXufEj$cD@Nbin`M1JN zV8C}OepYyZKh!@JKQG)5eDKMNe=q!|&vB$4ieD6d!@mJuOYzIXfAcvWb(i8l3jfvr z75tkF$;WwB_|HDayVfgyP54#+D)ivK43NB>9gT8iHke%Zeao>_4Z z&j5M=e2&-spvsLv_(lIB_+G_B;Xn8s_gkcRB>aNUal)~Rrx1SLKM&qs@l?Xk`5bR7 zs(2dVXZ^F_^HlvIo$xdM8Sn=bzeV_I|1@|r#cvgU%0C5OOz}*@Px>4ewTj;^{DjYO z)UR(%zAssXANP-guU9;q@MHcl@aGi2L-aEc|oW31fUm@uI>%^Er+^ zN%7*sKlMKa@1uAL;ro1!hkv2Q=}HOz$o~j@o#JJLf9QV*{+TK#%M0J_?`C?%D+%A_ zbKHKG;#Gw2@Hx&uNbzdIxBJ_{x2bZYy72d4xruQQ#cK-R=5GVPTjf(*_-3Da2k8~B zD}0l`3H*x6r@rua{dd6+Dc(@{Mt>uC8dVN7629JF4}Kl-L^;-2_&R?bcnNjgnh9Ux zQ-8!N-ctDMKJ`tGsqx3w!Wa9C!M7{kPWWp+^;uq1yrb|%{vznq+(_$&S^;JFmy#1inD= zUcz7WUj(15cpu@jed-@ISG=$A=X~lhC@u?3*i`~Ti8X$b8 zKNEb7;sb?0=065LU-3c0r}@*s$0$Bn_*8!?cvr>m7e2+O-dQ8XhX|kKQ-7_p;zNZ` z^e2L6Qhb>33H}7|nE_}Q{9(3V3gZx3@3lyIue4sxN ze6r$`g%9utfOk}!x-J-b@%w>SRQxgFef_@RnH7IRcptwH_zm?voi4n$-y8fR#b*fb z;r9Svr1&i1-Tm(1V-=@vhUazjyMcFB{5j!W{jT7}6n|cLXTLLelHxB2@8ov^|5|-# zUliWa?+Ct0aq2jshURwwpQZRb;qCl(;DZ#OFTAba7QBJt3xv1v+kod*e39_herxdW z)i~o~;Vu1^;5!swBD{s)0(_3*%Y--gn}d&5e7W#uelzecic{wV8lip@@cN3c65iNv z3|?IEHNqSE4Z*W0{+93tegp78@%6&%`}M&us`1TtgxB-yf&W$Ujl%2rb-;HhzDam( zzc%=rif<8K%dZ9gisIXZ*Ys z>DVCgJ^D^~Ha{Eq1=apNCp@d475q!ZF9^@#X8~WW_(kEj`L}_OSNw|bOnxTtUW#88 zp3%<;evjhUh2QGm3Z7H(--M_0(}7=9?eX7*r}fivwa!Y_Ciz*8$;Nceg0Jot50 zuP!S5Tkl)&6N=w0{ET-7{5{1>2|w+f24AFjS>dO=Q{ZD2FE9LrcLKb#;uVD-_l|=% zQM|J7W8N|F8j4pHe$+b(olK?Bkv<{+Tte4hepCb^gaZq&0&H!5x&>k3r^dx z1aBsMkGBV$wj2rGLiqdM`{2~APw-a4cX~U)Usk-e@NM2U@Fx^+D}1ZB6@0Yf?S*gg zwt)9jyrb~V-e&N774Iy3qqh;fmf~H7Z}2vNmr}gD@OQj-zzZmTpYXT6x50lw9IK>X zv6t}G-fH|lpm-nQtGrdT)Lj6SX;lh`AOTc}_M+jfwEdalaGB$Bu4+wwNdlh_#;v?7a*=QSnj2 z=X!I&D=R))_ygVp;OwJFMB2-4GfzTR(>v}+rL0~@A3tNRiy)nuI2p3$u2@Zg4IV9_Ej8}D% z^+?$ca+8Yx>f$9)kLCJ4kWah#Dkt|ro^T;;QA&`PJ=w@;&)-Zo9hNT$$ZzS z_%AMA5qbbjHyZK{7eC`<-rVuJgshC;4IG>AtKz@Ac$>U&y5*3+ zAhgIA|4zuh_&vh0gj|N-7aU8IKTco7N$#hQiZ5{SzdD(=0Nx=4p5tOC(-w?>=XbI) zWP;UmY&zs_7r*Xg;XC8)xX&&s{bv5!6`btlm-l2Sg|-A<3n70hLbBG_q+HFC-WD_bw+puf%k8@lZTvS zK56bw&a=;SIV--A5n;-{Sa)yWKH zUDmUO7(J$jQ-8ZgVoI60fi2zq92WR`p4bS<2m=;RtF4@3IZV}B%-A@4=tb(!qsY9|jl>D7qW zmvpj=lT)31!^y9l3~I*d?{u<0WM_oI2s04YAsj~d86k5m*hwJ_Ls*W$MclKE}yiPG+bZ^Da&f|0LZ)+3tu1>z@IC;a#CY|E-bDTWwJGsHh zpnJ?4I61?~FPyx+N4&nRlXIOs;bi{%;`P0pT;k+;CyVxs*AH;=Ehld}S-V%f{xK(4 zJ9*H_-<{0YJ5JZw$q`Pz;N&JJPdb^ZPn^D(lg%KXKzJYF2*M47tbOA&ot>QPw7!7+Q}PE)_EXa|D==qoy;{d z=DnO;3d!qr(8-je;`ODR?BL`hPA-6CePxf67ag3B#zU}14PM&o#m=JGY)XA1kj&^d6lkYis+R0QCMSXPL6T%X(v}W`M#4! zoV*MfOhTO$AumEHgh2=+om}qZF(=bM8n18ckPMlCkHq=+sSQCo^>+)Oz15kG(i}KkRdht1rgYOelSn0D{4IGVtS4Jv;jvxeEj-K zYzP9x9`q*#pXSH2jCc-Mt+NdkBAzl#72XnN2tTcaeqj8bkGZ(FMfCsuX{|Vq=9c=? z%&l>-A#a?6f!+9qdNCbssn5o|E_q29XZ_UYxFFL&^(jfSjj0v&__;4PF=X!Xslo%+sA8o-R567d>-U$3jn{^fKBE zo?AIPH|OW>46nFd;K7$(k?Ap+-jq2P_oMLQJp&%TXTtmUEO;KLe_~#%fk-z}JQMS1 zKN9^%UY}vmdVHXEd`@kVV|RGtrp^OXHSuW;!c?<@{-%q&9yfxahF-#%;wel0GRBtp z)U4*7N(b#>(M^4M@^%3&6c`&4l)!!%yVtT8pRs=8mxK4>b^mR1OZam5S(vqAe0LrR zCx%n7Ob<__h*O^tt1uqV?qIT^Mk(VRphrrrLFV_7|3$tyc8mF`&9fW;G-1TyKMIY!=wl zW`hlG4%qGHf~{|U*asJcO>tq^As2=1atYWwmx7ISIoMTSHEqHT;rHQ`0`all311J? zu;yMf2ed)v85D%42l{^?ooYCO#n=|nNnxy+Pb(6}Mg|;5WbCMTLgl%Xgn!-vws4x5 z=kRH<#9UtuhMB^}<585g@KLf3eofwnFBDpCbA2iJub|gC=5Yp3=&hDM#`$Y3o}#Ii z9(QoLyc^J0E(sm{fv_JL2OFC`u;=6Xeq~CAv@+q*3<~JKkuhuO8I!TwEXOJtJ1AbM z8S8CN;dx|ih@}T6JYIOyn!w_tC#*zz!?I+6>}LpUTZX|NW+ZHAM#JuA3~Y6{?}@Mp zx@1~}Z-w85wCG_@V}p-z>loW-=(UkC-+qj9VQix1NE>6TL>GXuG(Np};X-=F;JfP} z{N`PY?uM7$s+Qi;cm-aCkF?h;bu^ehpHFQW#%lVn)QDr{EIoOUO*8ai!q^=1m@6kn zTF!eww#+{4p2J0kIy7We{o1%Mya&_5$3l8aYl-;yXaYQ>eFraxK2*~&9U=T#a_+!% z@Z)(4e1F~wf2ViAM`}*^Rm}}wtaahyGfzeINXK_lA#f&T`iuPs~90FCPq_=R@EpeHeVbKLr2qoMma8%p*7f zr3sg5@WTH%Joi5dZ~sr(+STLZW+_F@F`i9lpSsaJn<3DMroR=^nT8$_P&j%ep;uH3 zx=3}Pzf>POPYwO%hSro!wK*u4CypHr%2khJc`bD*x%OF~+MmpM9&|yekHzJDfftNGqG?@K*772jNRF%2H>Fyu43qP{!5<<8sHbPlIu8 zt4fAGSh)6KL+?3^J#XsaTZsf7y%*855xpMKBNDwO(UVe|Fin^iOFAs+vD_kG)C^%| zEOWva!MmABUfW`*2l13qKF(GlR!m%5Pz)Kg&Hm=hFg+u}T4Lu^8(R(E5$B zVL@}e)7)kkyjam!JZV=;txCrF`t&2n^@HF&kUkW7_OHQn;!4XIKA2{a<=8A^^?h37 zGWKo|xV7``Ih-lWfbwC3upyR4va}A*)r@nli+6%rVBD^#PdjDq?}K29p}rOl8ROco zVk~?K#?O~w%zY)s=~sEHG3x&gGzB(7yI_mA6}k#Lq2I6vIuRe@tNTqr{YmE7(Wixg zoB^JTuW>A-sY3cgW$beFntR??A}2p+Yp7|>HH&=OI&;hI!9+uQ4#vB|iem<@EuS@N zD$^U&?^vdX+rnPaplBYJjks1@qE|5Q$XYDxEPe0sjr!TN#cZcPgbCkVSz*C>ET{~> zT$w}KJ94k118U?lHq56@7h{zJdiP;$jOp%5xe*~}93h)&8@j8P)2FvL{P0r3cBd@t z#aO2Ahh00rlZQ;{kiDlolGdo(bVZrd3P1S!Ved)r>!dZ7+6>Hdnamf#*a*uRKp2~9 zX{&}??X`xcXj^EPc7R4|Cup&Dh30E_XvrZI-}?-f4yR!~aupUfU15#50G7jRU|qc(7U;iY4rtaA zSR?q<6bfng%&nIP^qj!h&jEd8GnUIz8;h}D#E%4zB?;b)=zEHEy7?J*hG$eEY!Wub z(oB{XVM{EnWN8z&4LK1GPTiyC8nxP}@kVVpYRXZIjv98b;6bxBPaMAz3^ug%DhvKO^n|{`xBC$Ek&b%Dpxt!BI|W^-GtjR(51p)wr~_Yy z?$=L}Ce4|&(b4r~;9{+7Vy|1y@y)GPi}a9p!#I2lOV(r{pWt?T<#Gr5ACo$zrMR#n>^?>|@OL=`)rk%YJywJ%Luh zWn7Lz@cvGpz)bO7L~BOwi`RzNp+Z2rbRNmVV1%JBTCP0+554E$B{{J7xGQk0PrqNx zW3su&)is7&`a|lH9@FXdogU%oWuG4StH{DSS5?$sSc7T&w>k@FIN|IkoRNjI zwQvR;&W^(wggCoU2Grj8jk-U4D10=00xg6Y;mmMW_)Pe0_+0px@cD4|fB6>84`0O@ zFA84^7vubvhRed`;fio&xGG#7u0dULUAR7^pK)H-$Y!V!-|M?EuRvoQMTT1O)vb?t zHXgRr=VH3m-YS6bg2jJHlUb$Jg>;FvHM>hTFd8Z^!e) zG&$g#|1P_|PMoGE+H;(TgwH{HoF{eSn0}{NPcSyxPxv#f0dG;4O-mg6*^oMNT)R!Y zz;o;Lxaypthjf(fgGZNpd(xZfwGU}|#b^1V7vf#q=l4NbgN`qcemRd|p+C>#Sm+0o zK0@g~l)gpjca%O!>93T&OzG#8K2Yf&mA+HySCu|j>5rAZTIt8NYFIU_hUFeCHL%pg zQVUBREOoKe!_q)~f19ZCu0@z!-n9-}hi$O5#qzJ*&3Dl1+8BNr?#FZfRd_Hwgs1vQ zcr-j7o(R7QPlcz$GiX7a4bNdV%J1>z>r*4XR;6v9x^ZlP9pc{pE&+Wd@Txu+3^epB z!T91}q#2SgPMg}$yDG2gH>MVzu|pxfQuFsg__RJ2C0SZ6a;rA5RivlXQi!jE-PT6f zdToMz7)vkK5O=|L?L*kR?L*H3zczfb&SIPMu!&^J#@geL_|o$W^egN_**5cGe`;W} zYGLQf?<=3eBc>Xj`TZfilk*uk8?~<<#|qf??p#Y*&Z)+=uUmR+U~FoTH%}Zp641jk zzXNTeUhu3;FWFzAtrYie{0Lv~SO505vu7jGtC3fFFe;$uq7q7qDtNoue^FCfCTx|k zMbc4vFuF+31$!;nUoi`_s!?Z@N3ln=eHlA1c8k1nyu|Ov*>cag|y_gd34;oV~~JfpHxi>;?E3o`Ckjd(ao~IKP0+wr+660US_+pp$Vb2{mC(%VJjJGIxmeK*=jNXe4ysFYRbP#V!UJ@4ev z=k52!{R}@} zoGVr_oGwo_^!7>?`t>oi%{pAXY{sM4={F0?Adv4?NDMbawSN2pI*%P zyiA2H=LPhorpp&!fe5}$ITHxCy%9Zy{AF9zb2x7x>3B;`ebxp)_r}BWatf?FX?xFf zo$x)NJt5Qe@#!&?v4MuZZ5f+kGNZ)ge%X#^`<+%(TvN_Jh8XWsMfA(GF|_(6U+mMb z2xCnGdeUKRTyQLJyuT2hRl605Y!vB@MUteEI<4m1B#eOwbX2NohUGbwhpn72>gv_9q6k zeI=dfZ^V6JEWN3WXB5Q_-td+1*Srqbjo2E(~!_l***uTUl_?~lMM z-Z6O6I~jau8lYZ%JRF28mmm9NJ(BfD)*D%0WId7fL)HaZ2V~ukeJ*U=riRm+1+SW0a%cs>eV^e(k4`ytn;mjTASn(c_*FHs)Lxs;f9YD=#k zc%0FP^fqcOtqi@^^Vw#dt`xrN><8ypmSYS1P4ST4znIJH;C{oIF?u528NE9j+>x<8 z#`$D8*9_;O;T$!I5h%`+!#Q-~BaZ8A|9uRKJ-eKdjPDwk_{`X5E9FL|{(If7?tklv z=3Igtt73oj-`Dy$X2tQUxPN()j99V$w+at19(R}6m-1A7|2#aEx-eSZ1*?JpA2 zqLNoHzfT|Pyr=X0RVF1`eS5=Ic=p()O+qhaS=IKqCtQX1j&E2F%)T+-(rT4?RPkvK z%vf`u9`$%_*9Q+9di5jS6wJvT(=UR@&73@O{1Vy$^mEJ9CE@LR4r&8Ty~}X+k+z66 z!?*8k)R+_3@gH49UPsoFSx;t7nRR8>mRVnp$NT>1y8iQ?{y!QY)Ym+Dl#p|4^7uPj zS}igb`ro*3Ob@g)zQI*xIn174_WE)RwQ|Urf0_14%ef+O8NCAVTvQa^j&$v`c5vL( zK->GPker~gsr@FU&``bsCAV(9IGv6p<#i@-N52TG{jCU;0bYFslkpeI!=lW*4 zg_hQEj4c(rJI3CW8TOd+7W7(i_CM0w?Oa^BB8biP*kXDPJ`|sa-^CZv%gB+O`S1z3 z0KLKgZj0%xX^49KiICo8umjZbj-nrRn@_7y?)R|x;o;Go_xAhraX~u7rx$+4#u?75 z!`RlOxhQ*h@8(G#|6KP@_pqFK1TTTdJ_YtCupf|p2)q|OZnpN=vSYoUW8ECr{>T&v z=`oZ!uL*kRj$`|R)79hSEQaqvLyRTTTN00MDDvct8GI66^VgYls2S}G>BE~j6gACI z55;XnJ7jh=7t3zUmBsnqITw60lrGHUU6UznRx6G-LXRIk!t;EpV$86;q2B}MRmZ0% zdZusTcQ%}Dg8Q85FETe!C)*Oz?-NtHW}?+K=w zRCv~SR12f{Jz8R)K?Z|B*dMSFJwDq|%iD#Xo_*-$*^ippVf4}-$DKKi-k$U5>A8eE zbQL{3H_*Fd(6f^gy*e3C{?tbMz6IW>ws@O5<4w8`qvrkbCJn)8`ACeajKL`RMD*s) z#1r~Fp3b?r!wb;9UxK#%3f$>6ctYPndww(S_;$48_n>yM5AFB;sLLHe+x-$+f;Zj@;h`?|@H#@QmljtQ(yBD>dQ;G2_=)=o9~0S}Mg+8{}7=GyXBZ zZzIlSz@w;waqxDAox{_2FIyqmDJ&uBacUn_$-?Q3O8My94$0dYA<$S4eMp zOuN@h5p*)N73aRcKuc(nv@YmHj%V2&Pa{2-F+LFE3FY&~arPIKFB;d6mt)?Um6)q$ z71~R&rV0Cs*-y+qV)hTSZ}=+OPV4c;^GK4yD70aUFlAUNyK9xh)uPF5?O?{s~6@ zF5ex;m*YxsE>)&ZW9iKc|1h3*4d1a_F&35&DDlZJESFgDv9a(xPcr5!a(-hn88hInJWN#3Iv2bk_X z4E1y*%c}p;uF`X&WqbvHODj?upNh`D7Nlt9dKttXqh;iFYnL z^w$d7g$?3gkE2Hatflu%o;Sw?1=Rb z2ZckiB#-TIWQRHi)G*Ma0D3fs^WE}j(m}f}o8_!be4-cnYoWO?2U-cY*N*qo%5sLP z0a&*leej#n|GooljF0e*{2brNLl`$ZhWgtnjF+85PtHa3yZ?lm@NejE575`10{!gi zP$SHO-qx0=-?T&BrVHvdJ)y%i0QH)o&|MmZG48R@S(=2gwOOby%tl?|WqfZJqPKM^ zdRkZFySo-WtQ*j~x&`0g9q3ivi?-gU=uJI7Xl^K4}aIUf(N=UDT5jrbZ=M(d&`T4RGP zeHU?EW1n8v7<eg_xzE0U2a0J{%3O`iBLik= z?12cc*a+_tlpXZ$$*q^6mVLu-ZRqWp+YAqS)Qr>JgHe!SmR=m0LtoSY=$VaaCi$G> zusCA>>Av~HDD{6bt;2UP>eUEkFfQePRL`do-dtXTOG!(j1(q`q^W3gSxoxAm@wxT4 z^tZvY+$bK!xqc@4tE!>|<(3;Ux7#LD75VVqoleRT6^x2S<)dm*9jQ6agU0T{h;tNi z%a(o@L%;KkuL$OtsCt}tZX3C~)ydKyDc5(zJ4|12-1lIgvn7%C!yIJvR7G0Zr%yrB zm!WNT&3#Q?vGf4Rd-;WT3>psa`P-9 zNRJ35-hw}Rvi@iUrv^%w?IwHJ%U$1Ymh%Vj7*_{PbH}kyf~Ky{G0rx1U)II8ZN7MI zD$^GCnp^BQZQZwfv7vW2vetfX&1rgZ7A1)PPQ`DL>7$IMu0yrBw(xi0o}@sG9!cq~lruQy5A%ndOP5deE78_yp`}Ms{yK$ztzAC-ittwjXhya+ z^lr+$KJ?cWh+{>}x*Bn;yq$-;&oiYK4K-#e2J~vnt27+nCwhM7ws!>caQ>toW;Elx z*QDP^oPnINo6$V?Oh0Wo3m|iR&R=E>G$(e3oEMhI6JR#jzeb$xn%kU<=--O5B%hvW znR`Ec8;80*-Gq)YXX;~$7tB!9eVE54v*UhCXfR?}ol`=qHChbm(7)zIW)CXET;9ShixJA0YY&qW_>hqG7ieqYu=w z`v~ak4MGQTBtU$^r0VIS?K$2f_R0V0gwH0Kw7RfqF-$@uZ$X>y~5Q=g`KbMgw&ksMT;0?Okd%P`80S6Vz{@h68mRenp+@H?)JP z=|Eiv_KZ{Cff^6gd7#z<^&Y7CkP5wz?0;krB>NyaM$S=kj+1kwoMYu2E$4VSN6a~9 z&QbHKXj8MNihWh=tzv%_d#u=J#a=7+Td}uV|eV>lHoG`li-8_14*=#Xc?e zYO!C7JzLaaABLJB_1H(EK1f}5YCck*oh31K+Nst42wLmxQDUDGdzIL)#GWPgEwOir z{Y&hrVILEFnHHi&zZkVhj@onFo+I}hyXWXV$L~3U&t4sl;&U9IBl+x^;%Gj{^Esl= zF@28eb6lSz`|JVY=sw5yIl|8|eva~UoS!589EIR$Kgaty;?FUEj{0-l{{Z?8IEu`1 zWR4_rESaOp98YEokz>m2UE;VhN0vQKeTDig?JQ_%L0b#z=%qw&19hQkbHSb{+Fekm zlC~Edji&ttEih<JXMemOGedIW%#p(|t0WiBIynNCMO;S7G8!77qcI-Lg>zv(4DCBEV`UkK zzUgtWW#TeHmWk*qo`|*>7tZ}T8NFOwrphu6mL}7n-^ztEl0E@z4=&SXd0s{cIY!7) z!a0~hcn`EA_oCnLBg|I(2{h67p)LCvw1Xd)8w?&VmIN7_`AiviwVdB^8#`Skj7B z2JJFp9mfn}opCGrmN=e9tFBn*F|*iqWkHWqRw3#r~!@Spt3WC7_werLO4u+0Ztlv3QEKr91&5mspY#2skc~GoU z9zvfh?NVr&66=AE$H>S8ER(Q2ie(CxsaPHpTb0NErYAaGY*wDa*a^oMXJG{88T3g! zYxCugYcjXt&B}*{7Mdhz2fZbh4%Cv!hZ+;>F+bx8$!Y0v9hDd8bG?Ha$QIN(*29j0 zeq&kdDduxV9mWO*Y4XJR9tvif+fbiE1<`vT9376q@{jceZld1Dwgvm;skg`3qnOvr z!A;bqxQ2ad>`}W}GtRpn>~waa)xdRE@Tlg-S9q5j7kJR$g?0_MuW5F&5|yvwy~ zk43v&&F*5ft9qPE5nC2^L_O`Uy79L6+kGz0rHEepnBQEP&z|*Tes}(nuGl%#2xIr( zgq$;oTcnd2C0JkVif{UWBJox377h$Yg%iT}k~Y`@wc;~s49#B7;==l9E?MDNiJ}Y^D_4AtXe(KoI>%_5Mma{f--#vqOT`R(~lW_># z=Not%Zu)J^WY?-5;L~S5Q_uG4{hzUg{$$q%nu`&iWtOw*^7@rSJ-C&juW6>L=5v;G zp4ohVwMhlNnh(Rl)#G&q?cjQG>;XF%SCi=>p>Jv08E~OrYPKS|oOI92hG3jI>8|c1OaI%vOD}u32Hg!k&~xuS{Eza+ZDiKn zSZ`yUjrBFw)mTsa!8C*R&Y5sON*i9A<53#B-?hYZ+Wqc6K4`bNt2Ewl_H^d`gxP|- zhF1vsR>wu}{FG>0>9GTj9Be_a6%NPaxItRzB9v1)<r=IdWd%H1{@sg<1{g2jRJ&Km%`wPw(|i)6SoTHw|409@|H3>#+S& zOj-+U0kGE2QjxW5)_{3+SX2GP+!@ZomjE}_yCqtQmi2L|!~Ay*qJ&2EHKj`wYZJ|? z5g$WY`w)(pZ(jG1PkYbsA$Qzg;@PDyV6Iz=XY4m<2he{lk9>p7;KOt7jP_M6oB|#z zPwNbyFg2l5dN8boYr`#m2{>0dV}ngC?15Kf3*Mz|rdFevzh>!8oheHCoOg%0w?@mZ ztSg_M^_SxDo*C{4>8*_Eb|jUp6UVyPvgj8en_}t9o3V60XO(1ZWstLa9Lr;Kx^$B* zeN=Ppb?=PZ!(4x*vCzQ#AUuQThU*p^&X|RlR%ovmWFh}jxu zOPH--wtx!+%`uBNeTXx!v_8G>Ggi;P=t_kC!B=>5@ef)qTE9hK$?C8iqSbdr^ukxh zxK|Tcn4AhZ1Gwa6Ud%FKm(tk12FA$H~j2)7>&Y5OB zW;iKlW*9$joDm3}%HRchX zKuhPRC@nTZBBbKlSTgpdpIXM96CM*}rCZN9UN7%~Q}OpPu`N0O9>TlwdFyyxOp^0w z2|>+a?i2H-w2$|C!@Ggsw82PlUVV6O!Gf<8p6CSU8B-_fSNzZM&;;ih(dO|N{Ll9` z!EuZ5$9^{0yeZE09232U|5=|+q~|q6?HmhACdKi<;O{CHjO!?lM+SdCVZn$DId+Ki z8p`WKjfKnjpKoM>qj19?`vGCouXuXlv7h><6z3NSGtFSZGer)`>xPOE{=Ub8Q7n}n z13ma-pCf88ilY*QzjIhH`l&cxBmAAk0-bM_KMHgFeTM}laq;-6%;*Iz$wO}k`a<#c z;>~l%(~Ug6SIGMm@81`piC^8%vs8&mp)g_ zOQ$#raq!sxdj@qp zv$qi6RyOuef}`C*Y9*e+~=mMpZuK zIiV5o1SEaR@^}g%ADkli6fAh^Tt46v5Cwk>3(B$5@%fS?3jR12XgR6;$;(1Vc|0Y4 zF#lr6zpU^+ULVMoE+6m-<%IY5`a||nyu9!MGFm@IaePFO|3EAlMO3_^@Ij(+wODa{ zFpz#Q7M!o*C~U#+#{#W&#j6M(0=)@5SBm3fh4e$Q;JTKHuLF-C9}MteSRfU@NBD49 zE1)K+cy-|;FlLN)r{Xn)$A0kVDqd6gNN*(M7R74`9|cPY9KYhVg^%_|L;j|C9pMjp z4?@ykZbnERe_i1Z;oAT`JH_h>e;C&I_}VI7U-%er4CHvl8wei@Z4frflBD}s>T%B|IfKO;Dyb-oR`Kx#{ z;f=kUyFG=FO+6!;vF~7o!cM#rIG}Id@ z-cfivkNNdiyp!@|JjWN<+Tmcz5hq$~D;dvbIaGXraN0;<9t*`;4z`rBO|%FUA0+&wcM>wC;w%SS zdf#Bd{)#8>_kQ6gP_Kv9qT(zETgn(J^n?^2D*Tvt405gFEC*Z4*ekA^;=_d>@s2>w zR(yo;uf4A!Cn^4baE{}mo}lADTLApWf00DltUWILPLZ`2#pb%AT&j2hR__L1wu=NRtWbZv_@!y&=#Q`LVJV`2pthRBXmLN ziqH+A2f}>_J-x{((FcyOAK`a|J5%AFB0Plf8Up)FFCb9UkTSiK^cl$C^c#2^LK%d6 zo$TUdU&vty^eZ?EvJ>QUkPQD5)9VxQSJig(S)#dK$Nj9w`b78?@pCRPoeqD*nJ$sX z|CjkQZ(d(sTV7XQQ(ik>PhQK*2-gvk(x5K~Aqzq-gn|eq5NaUQLui7~5}_kPAA~^& zLlMRyOhDlEdK7Y$fi-oJxS#YEyf@A9EVaRN(+M@SA+Y!yiJC~F_b%_>O6rPef7LG< zm(ER5_e=EZwL*Q2+Knu=yP!@o4aF>W0Bd*Ibhh?`z^)PzhQfY`=uyZ=-$k z8+zo~zT)p4XeHf1>z(a3AHUCe?3J^Kv7E?{$HWc3q$b1hLzWLeC#C#5yX*hMcN zwqneB>|^5*G(kN!ANIodX0)zY0y4#6DQg)!=55EZGnN@vo{8<#xST?1v+x)fqDAtH zm(p+Ta`?*o$;FO)t=+Mlk(LruuRuHKS1*;%7CiUO_~QS=+j+ogRa}36?tSRJgCa$m zfb=F(EEE+LQB)K`mR^)5EQ*S=kxCKnZ0v=cjin#)6Sg9;&i0Ort~m``JTiXJT+-`q2 z*)7#s->ckBk`#JK>n7{nM#tG7sE$_Mpbv%C7Mty3`@+^O_)b|KU!n$}Cz3B$sK2B4 z!u?zBQn>+bptbcTSv)FQuOTKR9^6_yalGWHuh%}vYWIYVN5pQ#S@+hD)PJh_h&0g` z4(Uc{9jqD5BgG-SpXJ)V?khJevo{pyzc6$0hU3(Xuc_21dK${Bg8G*#Y87Zbfa(af z2xf9+ByiXzIJJHbTK_@{`?Ye`(|mZ>B8RB(V9=7)k}?1y!Zll zhg+u+$8|!We2$IO=_v=RH-LIhKQtr|+F@#^=$^#>m{tT>&A``34$WTJc1z>)(D<0qVQ@-}gO72+AKo}oE_kCL-!SawD zcNV*3JqYaIHiBp)8{D9ydf~3>feC*P^|pP~)Aq9(GAN7BFvVzsem+(+EnbV2+gE7C z@(%shDA(87D$dTL+0Nojtcmx>R>!Did8xIpwfgm~>VRMW{!zS&^BI)WL7G?JqP2}j zw4Q?uCh0m<9JOJVi=#B}Ln4zJSD?Ar3U^YLy4T&t%=WgeW0B5}ZKVzx)HhZOvBMe1 zW_7uVMlHx~vMM)7DO_wRB0c}4NX-2)nuD&)XuoM|XVCuCMyc4|b>VEqNX=ry*$Q}D z(5iX5L!%dN&vvY}P>xq>B?Z3JH;Wamr<`I!%k?n{`AckqF3y+TE3TD%K&wu|ItIBm zQL7Z+I4|YbG5cCC3A2g5YTt~U@$(N`izF9tUm&;nUWQRCZeF+XG`4r#AF|jr)XMhs zNk`0TJ9pB|!f7)nO>f(!T{~A$Fm2|{NplM3&YLh{(%iXilLTzyq&br&7tESnsIRl; z6&8%2Fl*k-LI86xciJhF%5XKQq^s@8$bT8$N)m5gP%wMWB>lHI=s9!V^y!6@=FFHj zb6nx1iF}?lw{VBw-S(lDj1~m`xAc{&{}s^^xjk744_U4NCe5EPX?9`Z)NzD1W8BH( zCy_HQq}O}R()!Q_ z!~Hr*g9xu}xL;R!Hy_WntSd++Mn6e`L^$&|NgwE^F@J>DHyoa=d0&J#FubYwnz&7b zH#EGqtxrw;ola-TES9-k=F05(f}ZO$dwQ|>luSRFy=3@?+e0$@C_Qn*co=g=W`9hK zBa8xeQ(v&Va-KdNe$vNLGH{%(LWk*Gn8)k6(6g&2k_*bLspgW2@(q6jEN3aPVVbEG zXuNk=_T_MQplo~@t`gwKm=*tRHS!rKTKI3~)@4-p)X0onhszESpB=4P-DHh}W@!X` zt_$PUw$c?EVpdJ`iSYrNOAfITfO=xN{n(JWbt7Iiq$4+8ZqNX?D(B)KWYhq8mna| zPcPT`K5wh*mSwhj4y{+@3AA2R+I*>3%=`>+p+$|hw4ESV=ep*ZyOZ5#nQef(TRtdN zN|d$ycKQO{{WU(IR%5TFx=f2^>^D{^I!UoU-$w5EtfqC0w%SdgE#bPiT2KqQZZ10| zN&e6ZX`$p0c~oGzLk{^|Ay*f=BKwgqlqkRPqC>xXv~qWb@^OLEaj!7KA;w{LP~GMP z#HNK-DW+>a0LQAK_dXh79j(=cW0Vh5MdJj;eu3rs`EHabk#8ehE7_E_8Wpkr8%FqT z-Ha@(k%~3EiyRnZv0kXy{r2u|yPkuNddJLrhrPyc@fP(M^7iw+MVSH8P5efmKFr?| zOW!5_1|PqiYtfL1p7z(###7Wi|9VgVO*Th?{iaOLr|j3q@3&+yLrR6AEY3!82V|{D z>(Gy@3Fxn}ep>&~qH6`QNa3?-r<{0Xz4d_z_x<)%@m9$rMR;wTi=Rx;|0^QAf#FBk zzJbvf0q8d|`~bDljDCiwwWcKNb&v}`>saC}=l^yy`-F>n_zl%~s z{Kv`{v8Qel`vdg$wfY8frls-)cYT#FxIIGog8QlJlkl@mn^mN!joh_YKe>-aBS))m zny$WW+XNp=%89b!ZZTp|Xd?l9b|K&;!$t~RytZJrkyl$ z>tSmggH&$&E6&5TzQ8DeaZ-We&j@L@N^-8Hqo#cOwY1Lav-g)<#-CI4b-6Z(h|v*l zx2*P3ul7;yBI6gwfH4a1?Nld6s;}QE%fYGE&MB*gYTamZh<#n3(I4wzro}U*h&P7` zb?6=WRAQ36-P}9RIm@0O|4b;q!fUCi^@-!APh-Zd#Q^mp<7UV@Y5t_LrlG%rcDAOF zPf^r$mJ0V5rn*rE{-0=+*|GlDQ7PHa{&q4YMmE3Qdx>87H+$c{{C=Sf{tvM&5z|xE zcxtHqW}Sv{@UMtEz(ImxHf z{SnSMLAR0UoS()W5#Gu0!MVZ0mq&Ov!w2OC311T7Jq;h28z{Vn@+VaW)O6|WeunqY z^%s7u+-KvFi}n?sxIE6>gZw<$@ZPz8a({e8|8T?iki@Z8+#~#G!+S|Plh!-4aJ3sf z`&h$!=6VVr7=?>Gh4zMv@euAL!+Yd<$bIjK{xrimH>ufrgikk|GnG1}5aD9CqR+WY z?bB!R(eC!_Qw--!ruG%1_^`j8lh&|+hxjZpoRgf&vxxrLhI8gqtFY(g>zjfS9nOhr zuQ3Z(yXdnoGrVE0q3}P+eaQdc3oqyLcnJ4K!|O<%U-!R6^lvx3cCNPYdm>!JG11}d zs`hfTaJ6GUd$r*;bXr1V!6@7ZgqL@DJcRqG;hcrlO;Hj3Ck*GTtk#(#{At5EN2~qu z2>+|$oUzr|BEnxVykf4R@Z%!Q+(m@fHJtO(l8=e-Muu-lHwgbrmOqljnBBzim(rJn-!Atdf0_xe=<;~TpVo$R zE?Y7Y5&hi_=ghWqn=<;6?3mrnaL#kGQ(3 zWb`E|GJA;O&!x`^9}&?%NO)zJ#{>Pt4d);qwf?Rp)$^-UvU%aL(du zw?4v)4CkD_`j-e_YWU4Mm!bL-;pZBDQ+ku|CnEep!*9^JfAOmbzu53q(g~n?72%f~ ze!cVsXm>xtuQdER=@L*qiSTO-Uzx5HzB0mB8onZ3A$)m+uQD9n1kUXf;eRk3T?X0% z%j$!qd}iNa_yy?&!jF*qP@nG-Ud83{P#^9${QUHMxj!hP|A67=rRNFn8R3r_er|fM z@Qx9_&hT^6bA-2y@V^*-c6zq(T_XGi!_U%9IGR;O_y)t5NspJ#KScOO!_SnyMe+Lx zf5Y&l=~Cg9F7*BWMCHRfhA&B%2>(HyR>+5Ug;#ZXJmkZNhMyq~V%oo#`+$FH_+n{# zRQ=EBPZj+y4PTTl68=d<|7+pZTpkZ}elWZ!Et2~!5q+okYSMAHP|w{m`qM?fyy2%y zXN4prBKj5eUR`?P10LvCH~ci|xzM^rM8A&V?}W-plY&=_oyGMR;Gs(F>yT zjqtqmq!Z;fNE_&RdqjVd;q}w{dLER~UnKgh)JQ%mt*2*E zM1O|fYesmWUubyUw630`BKoHqUPpTKoZBbDsjoF%t+bY&QzD#pRnu{&lJ*rMoc3JP zRo5wAor;d|3k|Q9R@3v?EdFOJ{+AkFHLa>=7r77lbGhDYxjY`?f3@N0lhMjTM1O_h z=$jGWitrl^FQ1mzGmY?D4M$In`j=(-a=+Vfbl6DVDZ=kJTDf86k&lAqMRpN#M)4M&%bv;;@^(}r(NwhF%_!s)+jIdt)8{G8?Ia^)xE zm0AuxJvvV!_aQ$S-_(xqke_cFj&2{_K^M{ghvDe`(Kt4vf0gKeU^qH~R1YKi9|^DH z@_3;Cx#8#$(mruS|69Y+F(f`8;XfMwX#yXu8R3b>wRIeNiNwz%obhfQht49E>d8{MujJ>I%%keMx1^^@^&|?nv*GA< zl6+}|GjFV`d*wC@zdgeH82(!Fn(&nozK`LXl1;)dh;Zi5b)`d>{&Pu$4>9~zX+%~% ziSUCBe?ki;ZKT~);m&Zf?7aIOT!uaAd<$u6WHvAds9hF={M*m^a zKi%*rq<>3%CZb;?yuLIL20YL?)9|&)TDku|qJOsG==1XB9^9XAIJ&+xo{ZdIWH@@j zG(L>*OAJRxn94E2FE{+5Fl=*M<<;2lH@+r+kXge zARVOOkDQUuhxZI0lQ7@f9N`}rj@~()lgZ;Jd}#Pl(ov^9v^+lhW5W+m4i|n&g#X)c z^xJ9vlE+W@%5-B_&0`k&`!E!pBLxzXRG1p-O#)*!oN2h4IMhi72(?quP^y<@zn@- z<;Z_-Yt?#Dgr|nrPHGEZ9^vH;=cZR_#fk7rhI0$7_A(>9is4m~D#F`Dcs0ZQ`R9re zUej=Y{`uoY#r0%lEyMkJ<|l-Q_EN|2SKX_sXR9K-zTvM(u2SbKB03EWf61}V^kPw- z&Q6BkCS8y^HyGiK4M$I;*4gs-3A-46qq|Y~0^wPG9@)h3i=@R{{el+tYUru{aX5HG z$<~6OEWECsU+NrSQ^PNm^t9Gji}G}u8Sc-!UKG&*ZzMh18sfX_GCJRgPIJT0avB1< zu@RkJ4fp3B?~mw!?_?4#I{lx~`A&3N81Bzc4v*-xG~A!h+#JyX-`V!mbRHq2^P}jr zGTfi@>>bf*Z8)+x%9n@^cw;xo@LE^-{P{_A+8B<6ldy=+Zif4FhE?SgI6!7kb-y_|P8V_qdA>cg>M<1jl z`c=6C-qUdOT54Pn;k^t;g`Mh!`n%wM55u`5M)Jo?@_28vFFh0Tu8UBoWM(&44_+Z14!`Hq^ zgzsng6YdG&JtBO6!yh-nnN|^gfZ=Q1TDfl=;X@2Zzpds~5q_ZI=)TpuZ-gIYIC^ol zo=}ue@4<$nGgl{GB7CUfYjl=CHiqtVRt@T$L#@FNUg=~g<8+avr) z!>`bp1=Y6*A7S_<=bGgj#Wp0vLE%T7f2AQv9zLoi2W}A%DbUr6jPNt$vrR2cBXnj=X zR&5|5M?6vXlVq04TqLuuyw-E|d`RYLnT;~YCw;1CbyY(Al>e*hx+)TWYRV4QD#AKu z*uHuW&G0c1Hc$35WGK=q7~=cevsELC^bd zh4*QFQTFR(R?9#;VDITYC&oY=hTmKA8}0|oZ(n`K&wM@4mw8?o`MrvF*$E%FkLZ1a z479iC`GXAp2&443gI_Ee=1qEkSf;eQXM{bauy$MT^N;xSk)1G0*8#$stZ(yWE|Iwv zcQTu0c9h5c|E9YsWys^PdXmS->q#D;swa7Trk;cs+~N*5eE&52_N~gf=}WppNoJ4? zaS7OQdS5CN=W+Ok`#O(l+^^>cGT+J6+#voT!}-|mdh$M4=9j(`<}q?NU8ecHCEG;Y zrbx>|xj9$nS{dSUm!4~7ppm}3{kMj~;`oo(J7rOtMxpR0WoWa+`=v78sXHGC|54_p z`!%=O>f_p51LT0M&#+S?Ecwp63x2z0@4?@p*?aIaQ}49<(ruhHzN$^-#u}*8p4;ge zrwzVATL}J!=zE}3nlF^56?ntUpP^ZTMntBL0IccH5V(xZD~Aq3%$!Czwij!Fm=0!?1cHSzLUOyQJ28q5{}#2 zoAPd(%8uKB1-E+&=X^Qw3t@(L!la(X-@-fJ#>;Q~ExZSRE9C||;ZBp=Q)Mofxl;!0 zO+7n5=&{dbkLwTLNN3yE{P(@}Bu)5-CheoFJS~lrry)J1%c?Yv+mHs_k{_ok+!(tf zi^p?%)_cI`_dBwa9x&2F8Kj#_riZpndP2T;(09@^O3y%NitNw<3v?FB9_I^va!hBX zzC-7MNAh%@&gg&zI&YSuLq8tV*{1L42WauS<`*&pWDb)#Nd^oVVcK)}c6#(}t?U6` zAK|f|7!U3P{g`gKH++}@3u6F_SN1KAPoMz?-9C~3zltZrr(?gv^?j-gbV=`p;$aK* z-gmVx@5^PU3|`U`{uY1xOjukNxcO9hJoruZt@1xwPwGseo~3EOAgnC9-wOXFntT4; z`z_M*QW<#AN<9hdj#A$~&^zC@$pATk?T=%h)i~Wzr8eM(*M` zQkUmG?CS&Hmu7U2)3}kbBW-Y`p7#jnJMmmAdujZ0VYI=r+?K|1OFIm1p;`0ol2~88 zQ{G4DN!gSBFm}X_UJlBI_BKUsLYbc`dng;)OZ;t(zQt*$%tO1W_D-HgbJI!oL(gzmm$_H{6r!CAOXwTGz3h!#HAk#%=PniMP`=NS{&E6TK&5^xy{V9!8 zC+I`Va$6dY-G=lBH(|a*KT5qOo@Xk2(iX4~|C@!A5A>tK?W>vFfCaaoW^UobA71~2;~GPJK3^$dLUb=j-GpU01ueU{8Jna5?; z%g~4HzD4y|CiZuT-s5kj{obMPfe(Z-3-6)MQs=`r>MQSjgI7>5!ng3wH_9Ee%LktR zZhCf>!T%n5GG}Jqu$Sz^WkMeW{~-O7GB>e(mat_qx64olFX;JC8Om*|p5;FD_Lv{> zt-A20*|!dQ_K~4Krob3a?0%T==)8whw!Oeq%Y99 zH*?4Hd6~~;zLOdDk*CjdoDBE^J&D5yf6`t-W+x6{=Y6NV6n2fC8}+3J9;oL*syiV*%n1Wc zuc+ zeyJ;onR=3@LDHsyq;t4GytRIv^h3JG--&&Uz$4MX`mh6*I>#1C00e#lCVmmF1* zWWkV&yFfB_H%YE=o!!mzxAe`lx@mPr=XfjKb<-o$LESIM{#>eb9i>> ztao(>?0focN!ba<1-CjOO^}nF8=J=DkSR~})+^U%nRi2~A3EEwoFV6gj1}=j+7*|t zq#qY~+dYn*qsqcQm5vOw;CPKBKWHa_wOc~0q!nA%j|p*HGC}Lc)3b0sBQFm@8dbAo z?ZdueH&<_xgu&*7odCi*#C?YH?#(ke z6{S&}_MC52+&3xSA4*Py8?UG(-z41YT;A>wrDimeY~k6GUtDSTgWhJhgKm;M^5&F% zRPtwE-4J$o!YVweLvJ~f*2Mbl^ls%Ew(k?9n{Zu4vQFc2>^Xoxq`KpCs;2gvijtkv ze@Le{E8l#5#*JU>ss12=w3t zw5PnE-MKbIyVN7KgFVLdzZYuHoqBe?%IgM|*e%H&$-T*HyA5rvaRKf<&H<^7Sd8+RvYxPs2DleHXZhR;&*|92;PvfqaTE->d4vkb-xo?cS z#zq-0;HEKJKW%=YTK!_R`=ydIT_)Movn9`Zu4G`(7Z12bvbihmZZPfzyU8@Ya}U97 zb{`mbfx#i3m*h5gOl+_lyEaOmotpKwmY=z&fcw2`>x@JzyBDjQPIdIq$&o#DYGsg4 zx^UMOcU+CpDV(u7x$}r>HYIax7Qg;k2|L}Wh3o)jd6aUMeH%)wYQjkk&|MV*Ck*S$ z&b{X^E9c0qYD(c^rH)(5Hz-y$6^~w7nB#J@RBH(DHc5@NOm|ObW%UIqJP721Zh zwn{XgPvdf&>wr>um22}_-p)xGB#$O?MEjz-d0b;Z6CGOGPwo5 zqiRb}t1o?XmFH@nCU;2U(HkQ9TV#FK8LFG~SAS4X^=8URogVrj!$BPS>(;}{$~p4J z-&x3AofY&(o1kx`m#HJ(jO)5;F-QXuw;L1Ww6QHqFV1+xpd7N*xI9CW)=#RSy2<{E zbh^DKP63ZwoGeY2skQ%F-b}j;EpkVE`iFEv4oy;Z71kVS-gq{dl1@#h*}Z_=2{<#I zWp@H{AK(?~pVQrPgGTsRexB4z2PxM(%I6D`Da&)3jp{e(@#u}Wh=q6#0&lOToaN@g zBFTEg8kpAFu9F_4R#HLuACh~L$C8@qu4&tJvuYJ>`8l12c}utOAm@bN zW0TFQk%V(`a#eDS;>1`a^b5qko$i_3oQ3pL(sFn2uW5n=3>1Bh+u~M<4mq_bS(2QU zw3H91!a0zM$9}l@Jm(V#_w1CDF4*o$xp@pqj6IE0R`>3YQZG^Mc>ir*x33$L8J6z)! z`rRNTn3zJIJ|BWG$mb)A}?AN@Y76Gq%i`8S=Ef;Z8Z)yUP+ zxjt$!{aDXjZ*k4_iVL-KS#nNtX|gTSob{Xm8C~vNWsC}!pd|fx zg!_r8Gw@>WR`s)!Z)`l0=Om13k$*>K-sb?h?q@Gy=ECT-h5cIE?4@gO!YPv?`*A*nGcBBVp`59SOI6zR9Pl)MbM+IE|mQ) z$&ooE|EcliQe<~PxxkjFG>9y9~7R-AE%ry@zCwILXYHJ*>^4 zrGj?U-~B_kFrAV7Grg%)UsJcZuh}!#Mk9yTavfrpkDIR{zGnCu!Fe&x2KyhUiv5rC z%Zx<$_0P3g>|_6hNivg;hZD}cVOyErqv2A{ zULVPUOZ|ui&IDJwR~%`zI1=rhm5O%O%XQ4wD>$nNMOq{0UZMDEx?!OD4fE^O>N`e<%|56!!%c1y2!i#i$kK@d?uVZ z!L^@BbMgZGqvX@93>XcZBpRR>n#3;QG?x6?Nm-p9m|Lryg6}gHWbDRyBwSTjzr&eE z=%1HftXT#pfS^<r3u?fs~1t`|m? z!{Us&%J(18~e3&0aFeBMw zGmOezK%GkzpmCN92CWY~ylgsfJV#7LW3fsl?q2cFF_atCNl?Cm*pNsJhIl zd|bSdqNe~`l^hb^)b*D%5@wa=P4{=Z3u24udcY^6v{SOyLAi2v1;39brzl0#Xj;)r z>GxTAzn-Gi02h^V=%2(zQDO4lj&6=jQC$M_Vj7{rW`Jq|NHQ8t$uIUS^7hH@I6J9{55NA$d1fe`n>JNKz-wv!xIubt=3}K+ zyP9UP<2Lgx%Gw%o^@aNJ`r;1jQf~G0>%ac~!G1Z;5JEFaIaP_RM!H4m#T6lc+M*R}w7WoeRIbHL zA2xT+bEblL)RshX3&n|ew9txR&!lRRcYnA$H)~V0Gg=wvyrFbvdTG{T7{Q}0p}bH( z(qcr&9f#EN8d_oTzmPMhX?2Q_u20~^+a@SXz1{XdSQFlMyiX4!3bcCBURjY2E74D9 zF=mxxKgF2u>lD8_T6bY|h9*%kByiEjd9vhI(c?mTyX78Gi>xBs-)#m|TRRH7tDSa~ z&wjbyMV_-vkyg!DwTj5;YI61vtnBu9T({Muka{kL#hji6l{-QKeUH^s`V_ zQ`uAD)HdlEq7qxAoWl(xYd^=_pt1GnTp}Js?!~>F?4<)8@IcMZS%m?lx*ge5_JQ>TlEB{63vtY^j;< z?rG>7w z=PqTf6!y)+YNZ{TgOk204Q4?rt#itxam#3J>c#_Z0asuGb#hA6(Zg z#CNq`_HBxmD=5^=?JQT&9jntFoH4?FlTH?)KLh*0TBCe7!ynNq)w@}49h*Bi>z(H4 z-0CZne9H-QkR!7i&_?T%ha`I`z7<5PshcSMnCDB!^Q_#XMV|i6irZchrHV=pF{_hS zRG-mKw&U!iv|mGG1hJ*K51~}@<TD~E7>Qi2{nY~ymA+{E^(=@`OxL;8ex zIXNBk2u?Kd^>I6!3&nj^Cii9S==u~*R-nv(nuu-UwqAOT@_AuArtJS%Ixl0y!H`UX+MIIlaU6;3#OR~3X-AZ@8 zor9(&?<2lCJgKWNh#6tfYodjNaK1@?%<^HFJHpPmf?i~&zOX&ue#~-!eKT|>fWDHT zWdqw63HoM;=KfFwgA%k;5$<*A8vU;(v!hYd8uhWw_5GRRwePT75XUCoGl*k$ zS@C{2=dKB{UV>%|Y@3rcS#EuwaC)3l>8H5q^rvbCF0N0|VxY`(_iB_>l;tSWJ)9l~ z@2D}#7b?J`Wsl_u!uT?lUkSMw`iOk|p@=*HYqD|d!{~HJ`Ma(}-_D*sJ0Y|<&%6A{ zpPha7L}-J4os_viL2gcYUl+nI(-%cP)oa|GCEj*YGAFC;hiM;nj@o@CrL**!^Q=Pd zJ?YTApw%f{3)7*X)r4`g3+AI@R|@Y3+08Y&;iYTAv>CIf7ql&CWm;^de)rHhle!m! z#TTf^f1oq|ZBx(A8B#tz3E&9@j&Eb!>Hb!0Em}7rqwWs1-E*w0jcaa768D z>!o;a*?BQ4vnR#m(P6^?z7|S2w(e z>!IhM2q#~|y-6)2yr$v)-11Hl&TOCc^)C8fHNrJ{*I#G-q(4rh4)TR^VHIDiZu&`H zHp1%~-q9pY-i>fgLG;%_Kdk{qICCROf$OL8itq-8x0gP9jaVYQq2cXp^nG`PYl%#M zZS_+*MmX#2I)$U3$}z&3OEL0b?7?U%q^q&vt!=09oCx2=@MgB-uqeWt81C{)oLJyoKTQZEpGFpEX*1Mr}!^wM;*m zV`QeuoFsFe%yOBPGIz^7AoHjU?s1EsXJt0Y{8Q#znTn#B{zYRL8RVrK>)A@CjZ7Dr zJ~DgD43s(JVlVgnh#D(-kScDCwWdaS(tg`pOT;$7-80`~jUBBAtgpK#v-P)@jPF6x zyYV-*cD^6yK9>!2x>ga9uK_*EJ(N*z>wYi)eDAAvNuA?+2S=^PrW+qKtfMO|%-0pH8m6T*5Ott3~XicrVRnzNa`!lhAfnU7$wczK!dYQSPd^!!JPl zxlc3O0q)u?j4AHU%r@0^%4nWoH5MPcIodq7p2iih?ctVX*q+9r!1}w28I^wX?QHY+D|7 z!r#x0$|z272W5T>-Km-FWJg;fl$|t_hY#YryMyCm>+R}gE@!$UGTS`2?GW#$gSZtu z8<%^!ZJF&rcYYT0$+|D1P3CL1V=o_2>1QramjJQ{}kcAfAjie?u+mSM&Iiz zyDh?f|1c$)B08%gyqUS5oJg!uuOOAQ>S1s0bfuxYvg^ zG{O%sd>_+?wr_+FHGJ=6Z_(K!!VfciuVgRb9U^>$;a-1Qs|Y{FaIZgYrwAWwc;BS2 z=+utzNrv}H`UtNW;nNJ?Guc!4JHojKVP}`l*7Ab>dTXCnIy1;my{TSZq~%n#;ylhc zkos0=_#Vj~a{r~=2mB<%dnLVuf0faf<`%nOAaOml&nJoMh`ySu+G`#Ubczh`A*o5N za7Xl)7~WlTe1*B($G?T>pJjMA?IdayETVso@SMxzf&PVtch!!D`t*ptx@fuYB6&^m z*NlD}(U%k9os-VOACKtEiTb}h9_U|XxYujC++3_=7|0shPO-F3BNYN?=`$_(pGq%EL`;euQ7bLWH;fLMBzRt zyn@T)A>79dZ2LJl0S{`7Y%Qz(;*rYMfe88TO=)n zADqRrm*Tn6@a9Q#;fKk6sGqM1uPFK2fQNYg-SB2fGr1oV(SOJArb$!bV>9}FME^a* znhBwq5;GXXV`ac@p zAZZ}HLFAq;wUX8b>+1Qnnk0bizdu7QF5Y9g%oO)1MCsC^Exg-ns zK!r`jgokixw^dw)q=KI7aj%IVda1yUMr0eKFIJNkQ8#4W$_=b_#a^S_wIY)x5$0SH~O2Z zLIWP+Kg{s0l8w=tW<(#ooK@X7`uYAVxIfD9uO*kNv&)hDQHFnIlFm0~@sUQX*#(Aw zDV>UvV~OH3PIxt;0T1-)kE>}X>!aIg2WXM~?+ zxYv8xDZP ztzMEb#79rs@BPB73k`S(_aVb?cel&k(-D2CTWG*TJlh)X^|&^S=yx!DnOi10)vxg5x^qRpi{WRwGlf@; z=ywxdM`*wU{XGm{%65^R2Bn;n?Zmlng> zM;g9B+6^V!5YazMczvM(4|KRcqrN*;Kk)&%4|sv$UT^I^5kA52leKcBJ?IFZX83%~ zv2}lXgwHgb`?PglIKsJaqQ36hJW)97T_N0)4WFl*rq%8uoO?>@o8+7HFhuxb!{=&6 zL8m+;e3{{MG>_NXU4)-&_z9AQ(`+%qFEV_#?z)s-!wBadiu$@0cb4$}5q^c?Gu=$# z@a+(P^u5;Ct(MG_Iz{*j!@d6AHW7Y<;oP+?sk;d09*X*sNE#`;j_@#Uxzq5&O;2x? z$o;*Bd%e542L4;t?E?tU2IoYk(cbNYwK{fMky-KBcS@-EhBOabiXv{r_(n?)8p78qt48cq5&R8K`i<1O4|6_xl^G zBm7^6`+bZ%Bb@sz8fE(!H%It4hWmYq0&)?_u&a8h( z=hOdKw*6(fdq)QSG^O3~4c$@S>bXka16{DjpOnNxSm^INKMHrK+)b1T^dC_?;_w#l z;Ct!t(E0QGQel=AkD%-7*SLk|Tcv1{jyUWcxj_%tRHYRiaUskGpL+dVF^*1F($3xC z&^l6X(CM>UetDm#cXZ64UkaQuz|Oa8^)2|N{DQxxiep)RMrQf`Tf!PFKfg6B{DiQG zZ>U42t4ML?{oPXOB2Vy_YB8d=OsEfWy^QM{w0g>2zz&u@PII6azMrh`ak+&u`M=Zy z;!sX;zD=~BDHZKB?* z-0Ra6zeB&yTFqzVA3beNSpEvcStiI3QYkyg?4w7GiFYcjl#1p^dQ}Fk=>~Xyfem;rvdA$72lUbV4 zzzrB_y-aZ@Ur9T8N1S;lt-Oc4AGe7Q;f!+Y!&{eYA!?ToS`p6K-p*u#Vy@qXe` zN3{7)-o2ymgbAG~Z+klATc8uLYG0M4L;j;Pw2sPv_S#q9<9ZRwWmXwD^{KSmP{;lk z@d)Mi|D(*wryEs%cPgE0W#YVkS@}#}!+SUB8}YLCe~-^!{CzFF=GSHW`<=db(Kq~I zr#+RfFQxO5H2>N>|Fv#$H$w4!_XuezHt)xmbp_x=K&V z01SPV;MB*z=o#{gy14uwJAQ|5>9i4MsEf4QxIL6EuedFAQMmsLzvx?pSBG$UFI~R~ zcboEnZ`3c+uub9D{Kn_au6n|MLw=E;&4;XxTNBJ1j2zg#sSfw%Q#C(NS8YfeCQR~<8T5_TxnBVvnTdoe#$Upk-$cXmmaV?Qh#19r>pCx`DG7Dcl47)%mA$! zu%3WS;IUetn5VUxleE5bvSvLCwXU^PYjzj9i?srHxmFFY(#j;Ome*eh()*>+5`fGwu1I_pOcg7Tao{vXk~&duYFyJ!$r< z(S0*O`|e1~KciZfDz<~PZ@{h%_Wwv2`)?J!{c7!0u?nimomM!B6MKNXHB~sx6L!8e zF_o4vWFN3iPdLrIlWd)p@?J{$ep<5_X(^tf+oG{&WuYIJd@KjJ87`^*4%Jx$By~%) zzInaY)bEtf%}VPVuATJab_A@J@cB1LE)KEvIVcWu zoU0}$2WVYtkZYf?g2tC2wzi7xB!_edw!X=D1%a)GYjK3_YDz+|i&h|4YWLy>?F819 zR-*>V_0v7gF4lf`Nmwn#*Bskd#P*G?oKv1Y7uXLZ_MdAP5ZMRpT~zbfSH%5Sj@80h zvLV$+Nzs1#i)V~fNlZ};X6ox~t+me4`fOpuK~7N|PBSjDBr7Y=MOIjxuNMcoLAB@> z)j>)QKJtR;BiLxXWV71CM+*IG#rTJev%pd6X!jazRSmRDj^+^Be9$Y|R+0`KGQE=2 zfnHVvXfOR#qI;=K1D_c!nIm{jXk{~!ZM9jmq%;n)+I6<-)-_hQZWm|!T)WFv#l2$Q zH8pueT!j)`qBL|-Xq+Yg@9P?;$JK;Xp6m#+i}U`%R3Hlr!lkbWK9*X17ywAX;O_TNXMvW|4d*G+rmon$x zX`fRp9~a0^(tvAI0t;gk4K!iMxJNv zWGUKFdl;e`Iy4z3$wS{VD$n)x3t0%{GmtSsz6DtyIh>Fs6vr(QG4sz*$0Bc;f@p8>RQ}*H%%G`*6Up4$ELxxuP9SL2C9}0 zRSjLFmP4EQUXn@8)hfEF#jv|eUSBR=iUc3!>-#p=@%e(b-6vIOjEc(Y8-KoE9Ms8| z_;K!U?h{v#aAKQSA7J}-*ycJUo3J%ZdTGoo1CDPl2+YEzd!4DWO8J3@TH#aCX>N|?rSE6OKFT$3t*=VJL8l8WNzyu z`?n8yRZ%6UYG9=@NF`6n@ME8ly+ybuwUg1NA8)dwj}!oXi0?-zLI0y1`7tK-qf+Eq zIJr%{&(kUMi!=hSl2l93cS&vV<0(ebo&z(Irbc;AoF7Re2|_xq*C=~gR_3jfido+5 zX?ifb>3c6r>3&KF9BQPv?gDY$MH&s9VPmDU6?5hrI@`#yhx#$vAG>|zba&hTBvyon%o?am9#mOZk>zBKkyeSTJt!VlSEC*J zO(YWVL41%-!^e*fc~a00nlCI?)tw_dJA91R{CJHKHT5@)sG0ToF();exgR^w%>HH+ zkKX*q&8L2;)%bWGNH0Xm(G!syzU;2EQ8+!%YPB!M$;=5~)66E!tKgn~UI-7R7p-Sw zIG<}_h8X6E@hmYvLmX=3wyDVg@f2EWn1?)}+@VD{U&{j@f(Q8#LVk>Z%p`I0 zX+@HhH(Gv^1i5)an3`}#pAuyLhDJx~JLN?wQ8L#keqcAKhkL{|Q15hTf;1hzH=BGs zsqy%P1dWZ|^^Q~@F(OWkTlgVS$!N`w*!-A{aU1Y@vRWPra-TA8C0tvbYAHT|81CrJ37qCM^cG+~h2=ZFFcG7$6%fMTaW|M~Y9J zY;&Y*#HFqiUk&3hX5q|T;9JaJsIOr*$9SAk8Dp||G}cr#uASAkuqMQe2Y$z_hq^vG znx~;jlAH?tGtwEvvY*M}kUJAKf_%ZG_DJUn@t-@DX58H9kog3C78hCS2PWT&Mk}Ge zW-hhqSInl6Xd=8qlPM%lD=kNmh#>U)BnQFi5A1ts_Jzba?i(lVhI(|g7+*7AXU5K)omo5cc4qF( z-I=|!0>~O5tAMNno}AU`i>*B|gJ&ghdDa80v>JfKCDNGiV$c7%>zEPwpEMI>4*IJ4 z0!HQZmwrzFjirH^eHf*OzJXQ=Z$eI;nLcxUX8Su?&&p{4P6}{}fD;COcF(+)89sA- zX8FwXSy5z7aiERE{7B4?>>1lL@12pLV}ThcJtcL@uR-}WLROu8zxcRA!zetMm6}D? z-!(~)pdruSaVI25g;PR5*trI5Efe&G;%7gbzg?;H)=Wle^d#8EEGsl#3F}pRCTRI& zEFNYFVg1!}PFhyroyM zzjm~?i5d4;rWmn$wr7IWA1>#c{4ut>OtKr>>n6jGt!mOLt9O(RCzL?@CC?w>Tj?3% zGM!Z;kqP9>iH=iJ*!D?~=OqSNua>oe`bqQl9hy&=HFIuZ^OGh_9XF?-5McVO3FD?u znl^Lttb)0PbLLGb?9jYZr%vr$)wbI?-44hc7EGHpe%|EYNd0F#8QBxZ6-=L|s21!y zXUf5{log>e;!eWKj=X&+m4t#p)8g?7L?cbnQDZ?K@B}z8^E%F{d>P?< zXC%Tom1-;1IyADWHm7glU9LTVO2eo2+!!xrbQ;k88jA?`+(Oq+@H62 zI>H+m{++aKNnT>554XC)r9CBTcU$#*C<>P~x-`NA{bq)LtGSm}&?EXS4EN_Px~=r| zYm0ts!~J=S8zTBzP*UB_JJ9e?G*j2AZ$y7T!#~!#g5>g6ditFIJJ4`{&SOGE|6t+e z#l=6;6FksA%auGhx zaDV>e`=~xoG~A#6_iK-(6*T5}K~M0IZ>Jglyn9~H$0EGQ@aNofdj288ml*!6 z#xKrY7vbsvME@E6G>eJwvkhM-J%1|q2-hG=?w`<4=kp@`Lc^K4Y4j4|V&8KAn0|7< z(xDi@xd(<@ZEZTbalSmpElf|*ZF6JQ+}1Ss&I#%*RAyV zu#fVAdQi#MMZ`blKIAj?sd9vee4rjzR^NN8p07vrUp3sHzxm@zPhV26vo{;=&)?h; z(SJjD6^-f0SMWgp9mD;(oHY^tPs9EBo5La;eo@6%ceG*@;qap>wtk>KD#GD+RWyoG zR=R(w(u8ooG~Az$IW}_twc-A}OTP$*k5`eLF7>o=gwsz{(HL99YWH14{|Cd**IJX# zzY5Rtc{q4ojgC*(^U0OinJuJ?el*;lV|X#bw;ArwJ#3Ef{}}GiNxUE7KN;@NReTZQ z^xt)D46fcY!V|;&xsA%#<>QkY?$3GDk8sXE*44~M_M z&P+q?E5fT9et{&fH6F;vb8R)l{rR3{5zaZ&y0&McJ*@~Q-gPy@K2~%lMmXnH(Z*X( zcq&em>NI1jc|q#x%OE$dQ{Nh0r<$RsZf}`^GKb0_E3Oltnir?i1DI-Voa)qummfdc z?l?8v?bd1PpmmPZc$ zgN*QRagSV3@PE-=I+w3-kyZIt&$7M^_%WZBQ)ItbrmVE^ZMDo(N;~$qWI`VNTlSx1 z%JN6}yQ(4K5BmYKbB}Bz*+-5U0}i5A^MO8R%T5jwQ%h z68__QFDvXIXZc&)Q>MhdoS4!x_vFjFzTU$(qG&U zH)}qCgcHQ)%{T;!8qR;fj_6*e_kVca+wsop;-5dL!{?OGCsOsW>e^$PM-cwgnwR+e3VB70Hd%g=SJaR%Rf6AY z{!Tuz*2kSiO;r1d(e5tD**q+oDrtwD4Kbu%T$82jM8}8_^h%94wq^D^6*4m%=sfNw z5BDxui>{!$71vSrO<1S+NF~P#<=3kJtSD@AjA-!xdv{ZDy<+tN>^o_`_O?bY$hE9- z!xVF3GEn=vXuiV!RJO;#e(ws^vfDI!d02C#$CURWOhVk|F3du?*5-1Avd(7J*gkNq z4`BPuHBu*zi?aCdOAqyzm;|cSRq6|^`y;EX8?qXAnyr07twFM&I2VKTj_23prRUkq zN}2h>H5kK^FH6-@nXh2KOzY~bbwN+@&suWB**LNW4qYq^4Okmte#+d-PU!m zeeBAw^jM?hg5p>)3MQ0M8v8QS!u~tWub7R=kwU*#>v=P?5UzII8zF{U+|ut8mwQ>E zpzRsI?Cl~cR7G|qf$vs{Jf}R{pnUpR@n=>@Y*x9kS?hb)WPYF-xX3E;jl0!8k+Fd% z!ad=fdA>N~Atg*f-~R&I?ISzHG$ zf*eih8p|p?Zf;Z^qr4u@%K8+WKl)s|*yhy4<`!E;1$|mI9BCTta3y9J*qK$_kkL5F z<}~=bz%lE`c89Ga;F4O++7{?W$7&)dyCq>Y{UN3JD{&=e;*=Tv)y=k!g`W-X#NyIM zrUtBaa(Nc^Jok-!K@pC~8VKK4J7#^LqZO0KDjD6Fw{ zD{Rl%N)@(eT~~1?a&fGi?B3G;NVwmih3lH7@Qq@flsDMQ3vr_0T%&x?!AcQs*@NV!0_-i67py?y?+v%CIHxKqr{H;kAA61r zPp+uCL~b0SQAX(R^S!f z9alJ2`xC`r8QU!LX3m{9W#*)b&8N*QY{zDUwl>(SXx?6G!nM0$_O@*b2F;mv;-q$? z{PXZ>r%XCF|7qGBK1`ca5&GM^ZwdD@&{-EZ3TNi!As@Gf%2dp#xMs?Dl67qck*AJF_hQR#5X zelL~4!81=BF|Ifn!zaz1H@z^c332%6IsSc{g=|AM?n6~zvUYI_1!aOS#h$$96#Tf)V4UJAG?jaQM_|lPQzZemeNdTnw9Id__)$WzOPUu-aO( z70BPb;yPg2Si*y~i;_O94n;|&s#9~URc)8+k;5H*+gl@p3e{}?Y&QKHBZJ=hzej}A z%ObDRUH{vH>#6;=wQxFinHl#m&kbj($7}r~tT$o5UWR$77>>pHO*(uy^7jgH_#WU2}#bdl?#p9{2#iOHECU1(b*4pduYx7P*-q}?= z%J&4jXl~fTJ39+A2jEU%US7*KYCGiL>U7pFYXjg=%V6(emxELv!yStM2)V7`KnI9qoXK zQ6dI3toGK zGbbKeIEM@|T2U`pZ1ri_L3#a>w>*O@rndu1#Zk3DzNarjg zoV6+>u#VIJ>mr;{kM85rI+jK+5#G%3qij9?iU@CMxYvJl4!GXoU#$%H{OcR>sBJQ= z_hJw0sj(d%2M$jQ>%D=${nEFvMu_{ct{eQao;gc&Pm^I?7hbtqY=reX+P|)dC>{+T zY3p(Dn3;|=^1X@XpX3|%PRcL1Bzfn#WE05^wiDh7N#wt!>x8E}%7ysD(dH5zn35fr2lO<(1&1B|IwEhAAt>kJieVw8;h0sfclrXoVHIe`1JyPEX>JBZ|H^}b=+FLA= zOeMSb#OmPUdDSRud8CILL2l7xm@?Q^`88e^Tvm(vW_F#yS{pyXWnaa$T$Z}tw)V*P zamDM7Q;TQ6i*5Ce6owKED|m+%&vxgzaoN1UhL>Tm6!G3-7HWgL?S!z}xw)FRNV=?G z&|I5)44FAVw;4$eGJ}VXn?G>YyqSdokC?Lcbou_hWJud}6eT~G5s`c+{Ih;b^b2tm z;*R;hqflz#{R_F-&Ia7=Z5l9e*31(p%_*ETG4$C(#}AxZ-0v0~rI)I08t^ z3a=R9Z4951nWYbRj!rrMiDM?4E?ptp&zkwgg;>TuDM->zwt;uAO2)`^IUV`Ya)ED;mvaB zal9(RpESIoZd6tIM)+S0=jKrDmqhsUhS%5a_R^~#;TsI+o=(-?2;XdYRo%WSIe-X% z$M7n;@mQ-F5&nVUm2;Jaw~6pi46mqrk~MCQ@GlJKmPPTC2>;gb^11TDzg4CLeyE|l z{{EBxM+1~C5&o0mKj>ys&7UH?Lc)h{)2+hSML6>hx7Tlzv0{~?%Y2j{9wbM zNzoVkN`xO~_)~WO>7x-o!tf{4C*}Ue2tUT~HR&4ROCucp_L_mEtA(E!;S&wNFTGFr zel6 z{5jq58`B$wZ;tTAhObIj311uG%M8CNy-N5k5q`emzfXTJ{K5#o*zhaUD}|pL;g=bH zMT$P-(Gh;7;mgzI!rMjoO2aQtFBe`X!f!DAvh*_HTOZ7)>ko!snxaQ}ON8HQ__^u1 z!Z$_u9fqHio+JF32*2C#v(vMMuZeK<*oz~jX9>S8!kKexWRju>dPRhz$KJU!byutQ z+9Di1_NqC$Jze}f!k;jFiSAXGd{Bh{+3*GF0^$859DVi5;gqu&9U>fk^=i54DZ(p6 z_{)Z$oTB&oLow^Xe_k_uemYLc&m4&|-;oL9K(p5>T2>&U4o~K4Q<i11p5f11$Fj*4)~ueKLc7B9Ye%;;Y z3GM6;5l;Da*WKu}vvmFgaLTW{?r5joH2QNsy_8>f_iaME z=@#LXUw8Na*n1CfEo!arduDDrhy@S@)D5Bn(m^ST0)l`P5jG-41S!%x2#7R65y1|K z6;V{giUqL(qA2#>P`CXYu{XqazTZE|?0d0ctLHiI^M2p+UC*6s-C6UWHJMB%$s{W) z$;Lma+2)_1a>!4Gx#6Y&$^ z9SpAew|n$>{5W_$gRB1S9&Ly>fLAbhO@}`gKL-Bmz1indz1%%|G=3EPJ%bTA~1i5~*L*5LYnbdMg4)n1ry@VX9P7q0^!Y4Caue;|GU{6vG-clg?PE%*@z zZ{YCzW3^K%8N8vx?~CsPPYmA3;rGV(f`4&O_IdT)?H=6|s~z;T!S&tk9<7PjfZt(o zeRsP@cgJ^wFE)4+hu;;eopqMM^*!z$-4WjbKE&Y79DaMO_F89y>pR^&S{<(jZ)otN z9DZx8cHjO6*Y~@7v?^W&j+sw>{#5&;dvr^z_T%><|0|y#m;P7Vx{tEGL=kP`G zBIH*XyuHI0#tXq`7`%hSFNiMyKRz3NuM-?TFP;ZpGK=R5KQ%hX;fDtNP43&#;dA4; z$p3OzHvXL)J|k8?W^)$L6@FSY+2Q{T_&bEt+2PaU>Bzrh@GcIY8czj(FdI&<6CLjN zp*S{+=L%<3F&>Bf%7DL5_+1@7HXaLpvB6Jr_?UPM_*n+;=J3(+Xz(Eh@9ywXtOtM= z&fvKY_xq(b47lE74~Gwq2P3~Hb7jK&%@sZdn@-{T2l-El=gAKD`)HIlcu$A-b^Dxt z6~dAHDGon5KACV{HFz(FpUA2hw4V*$+u`lw_TbA5-pAoBSq*{yj=}pn-0$Pr-{7Y@ z-0ydJqQUz)-0v58q`^;fc-6Qn;a4?ye~0`12jhS%-3Bq7MHo z;NO!D8d)0eR;^)vh`|Rs+|OfQYw$r1*D6D-V3G~L*I}1Ib-173esnhcUc(&j=aX+m*_Y-M;Zxi^4D`T3{&(&> z+~Iz|zY+!?;c!3y{HqX-Wpt8dHRSNN=G6Ly8O^+!9<;Nu+rLG%H5V}p-(xS#L6x4|bk+|S?sGQ?By zndopo@A?UYPjdJcRvE;;Ap2asCOh2EPcD_ky9r-_btNj}=;6>FD-$QP&vdxokLF#2 zPjR@P54^+RQyuQ-50`t&w2=g6WVbGKg;2M9&!(ZPj|SVw>-+=XFJ@_f1Ydb z84lMfD(E*1KGWfT9`#y-&vLk*kG*|${(J8=+u?rSz$#)?H0(ddmJzaQy8 z$Kif{@?8c$$Kif{@&bd;b-3R*FxTMo9PalGtZMLc9q#8Re~ofna!~MMFy$!z5;eJ2CPgZ5)v&i9oKJzAn zU+8eZpWxL7zsTW!Kf$vNezC)sN6U#%4})LgaLp7zk7V#m9q#uHEMxG?9PZ~ue@bGc z>2|rp{XT%34Zhgne*W!s24CWEKVN!+!IwJx3RWN_o(8|d;eOx1CI(;Ta6gZC4})Ln za6j+*gPXJQyvpHzKf${Vezn6di!S577aIH;hx_@!BMg46!~Oj2_6EPs;TN+~AoZ%j zmpfc54)Qbj^$rj7ibJ_jK3w5&KM#D3!EbQ*LRKTBe-vZWn}7cHN}rzGH&^)GSdYvg{DXpgOTxd! z;eK9sF@vvixSt39RR~A&w>sR<8-FGney`OI_w(DoyD@uT;p?y$nZ$h`2=Z+S=Qf9H z9VzxGF!=2b_xtxPH2573_xtyaH~5_n_xtztGWcB%_xty?F!-d3-k=0>$!lR$n)Ok@F7?O@qID){SF@-4F}xYiz|KW^|x9exV4Yp{ql_+t+5i7gPLSJ~(4wcg== zf53OLcsJpjmDIO|GQaym63nk z$@_f+uQBo#Uu@(zIr+BC)}y^(c1(BX98rM*e9h-!f{6d<`RS@q>)~7AJo!c7wbtgFoYN zt&>LknMP1CeBR6YXB|Eu8Gt{=;Lka{PojQFSA#$A@E(c!PfZQ}g2PWtPQ=eK_=^s2 zpR~s>ZSa>I-a2WGKO?JWTmQ1dKZ!q~o!{T!uQ>eO_+9Yh4F0OaUyol0FP44Y^{+Wx z>!@OPu_AxmVg2h4uZ{(v>=nmA+Ba|XPk06XGU!U^ zD(GtH8t7W+I%qj`J+uP40lE=d3Ec$U4BZN?hHisyhqT`zJB2dqDB;WF_ddJ_|6b@m z=zfTuIB5|ld?AwPLFggqVdxR)QRp#fJ+uMZ2t5vMf}Vh$gf>G@K~F=^LeD|ZLoYxt zLN7tDLa#xuLvKM_p?9G?=mY3O=p*Q3=riaa&_AKip>5C?(3j9x(AUs6(6`VJ(2vkh z&<^Mq=vRpSN9Yamn&X67(MePUDhd^YibEx!l29pV52!4(C$tx|H?$8_4k`~-fcAy< zhblppp(@Y;(1FlFP!3cTssanSKlJE%R>0XhNd2z7!wLtUYhpj@a2bTZTvItA(l z^@jRD{o-@!j9&!Z3T=jTybbyTs!}|T>Oh5I135Pnx-`hF!+#zU{$ao>m+;4j;~xk0 zf)t))t`5BJbKhU^n>hal^m&jgiIU<#s1DQ|QkbXWkNg|F?l&#SEyh>4H-+O(_%A_u zkmSDE4PL&Kzn^3e4}4Sn4p0whh{5K;UkqId-Bkd8ljDyIV83&`S7{%t{!__^MCQDLzj_-lrWR4Zy zXV4Cd?GZ;uK%F6XAFi7We+9G(dN|-(ph))+UJ1Sql*$i<*S*g)*z&;NiN7K6FXK!0 zw{U#mo^ezUIu%k_I(J^+7vt;rk#H>8_o32zd0D0N5%3+t@i6>xknlwT6R)_Y??T59 zb8aK_6!d(M`y8JtCE6D{6lw-_h7_mt++uU?<^t!wHRqCjvezlRjvN=+iseKIOW_FX zZRB*lu=xSI4`2EC#c;eM9G5TW!)}4!5$X$#ha`JGzV7i9{#(%3P@Rk@v!mi6yyrV|KfnB*Q9aZ7;wFVU*L=H>*Ia^ zzJ1m-?mYr7`4mgj-ag|=?_}Rc`h1JRwJNXc;5US0l`|bHf4>v3OvO0b0}^%szTzU8 z`r$q6d>g2DI5$B)q_T5mz{GFa;N!6cY%BCFq`X&pzc{K1DO{aXSd+r>V*Fd6e#QN@ zDqjzCyajp(O0Oxhe-_^lUU)Zy4GNg9y&&*Q@o#}1F~@JgZ-X+G;%I-U4y3Rot8-R% z66ekf=e3@X+VBg@Uao2PF%5m{3{=OCP({suPdY3i8w4PQ< z&CGFo!>4#JgQeGu1Jj;$Gw>Hc+N&--SJ0QN&i|F{rO2h%77RbdI?KpaY?5(D&#y z$NbDbYviFlp}nDU(4m~G{`Bt8wdI_Z9nP_?S%{xrzY>0T;XVxh3iLX3&ZXFm;lB+P zcCW3Rv-kReV;k4f2kv~n8pnSXW-Da0)>RLD(Vwk*)sOQt=@_1cKLeT#&4tc`7C?)j zi=oS)9ORbZAH11yEc~mX>!20TO6V47HFQUyyYX#W{8f6~i+q~C&%)bu`GjM|Q}?oA z+O+z^T)!{Ix_Tzb>%O~tSGAF80kwzDfTlwiLo1b+CricQ zQ>+ZU<{Q=~zi7T8t%2y?MA{N6Su`YLn0+yh85D7Y8nVOSzg z#(HrI7LU`glAMlZ3aNL_{%>s463u1s#ij`mJ$Z`WY2 zdp|b5o3IOh3R~i5u|Iwho8>pKbKaV4OTJ9LO@2(aW1am+QWRV7;@F3m!lt}DcIf+J zyM7S%?p3jouZ~@P9c=OIW53@JoBzhl6KKNRf@aJ|IEpz8Z8B|{5z&rW6kVBl(T&*} zy_w#<`IZ1pXtX8hdtr`>>akxNN*|mUT)`daWBN0J0h-0Mx$5aSZN45ImJAOJoHC$ zSGY^XjK<3)(K6PEy|{s~Y(>Of5zC52+^Sf5J8{QFACk{0fY`5j=*=!EBHBi zhSX3v4Kp3{b75|Hy4B7~Kxa>kWzR0|?nD}Labw&Vn7EbBDoWf#@gqEi?*3(5ERl_| z(8-CcoW-4$Nbe=CX(Y|LxKWYD62wh+*3IIkCYsYFu79GDJaMa{F+7>L5{a~b;`$_- zCm`;bL>4jPE{tVEF7EQ=XROTic2-jU?@ex9;qmgBT8!pO;{NE?kXi>}ReWnmiw2on zH~918GP2i~(cF>5Q&fVZ_48#?W zyX1$ac|5}EWTc@j3)ebYmZWt}!8$~vsFJ?Jjf;!>z*#Qqz1K-pGd0M_GE-+hkC%sZ zs+>r>DXe{x6VhaAqL~=NUPxrKBkr77vwp-?&B!ia+~Q=PAopyt54l?Jv?w*=rR)9q zz8P7-NOE*6n=f&9#ImLn_h_tn0^+VnHkI=4;LJqd4(XRmg16%B>|9rCBve-L}=Am8fV?{Sn{F$ zax6$Rwk4UfBaLE;W7W!#PUj}q@vaow{YjIIX4&Zcm&wrep6iv-$ceC1qi;j{*14e%t*Zr}RBA;e;Qfi$wEaK^i_!3^BFQDeR}8o?EQ zjkBo~_f%Xaq&jKKQ!C}#M)v#DFQai6$)4gY_{7~6%QjNnW3jCK#SKd;5Ch%Ie+e~v zO?ZQh?83xd5X-_%+|pP&adDr-e}wC&Bs13gS}FbZ&Dh{&#%45X$Euknq_1mDXDwo1hHapHg_c4Tldj7zdB$0WWASepV7`=igoko?2JaG z#Iu`bMkC1z?~-U6aTNMpTsD~)^72E`4*~rl-aDB{-cn4~xE6`#zUj&e8I5j=n-Ixv zPTbu1(EO6CxoX0SXJj2Fu55f!DC^}D&DasPEZHyAGPfm~&q?TtB0c zogB`zz+(I7xHMMqSLV0M?&SIXx5>B2w((ukyf)kZQah^&ZBJQd>i(C-<+!Wf*Y&R97_1lw0q)_L<-2d+vrrBda<)IFfCZ-q-f1V&>@l z-1i%)s3&@ccH%3v438!ZNvTaI>mgmcB`O+fqMzalNzsr7Y0aj&WRki+(JVJ{&nB9e zCvIEv1aDK}9YmXQ-g=Mr$!Pps=g*2{ktOa2XDcS|k4V;h;!cibmn-h>xGpg$3inG~ zDb$XdE30$OGqS1>wM+)!AF<+WT;RjQ2@q z!3*6p(fl-VS0$P;EUryPW3PL`9nU(zX?sPj^KP`L)0VWpDH>DP6@wyKiHe&ZYd(~? zDT!vFiCdCruA8_k6V0|0cSoXeUvcdsjhu@+Ig(9+xRH^p2gHqwWUnD^p|e;M_h}^C zO{K`-cu69AWudpmvXmG1Y&<^PZ)u`gk-}a~8iu!CoSwz_kap!WYc}}v<1!l0mSl%W z798T{MzRSM_q(%p6*o1mOvDy%Fsla>~ugFBLu3E+gAzot+U+3w8RtagRhZYlUu2ehKktl##WWuv^?Xu(;P_ z&F~fXVrf2;ugj$^3z84Bf|Pc zvNRUgH`W-rxYo%9fg6xq9^T=jku248c6oGmz#ew9>4i1U93Sp6FTS&>mn)yq+zp-G z5Nq~`xToVgL&&3(J3}fo$Y|{Vojp2JZ=>hV%E(?w*u995Ith|HJ|hb) zaq}YC#EHAY%}Nk=bF6tU;x@;cNvXWtB9V?^!Um6z&1?c=G$4HFQz1h`C!o#U+^$S25(gXOpFHWjVEL-R*TCs<}1G zUSSr?$X;Ar{aBV1;@*w>6M{nb8XDF79YU`E&COSl)S6f`fy6x?Yc7(wE%DRj8s+-Q zXoX%TZN%$+%Zf!3t)hoOi^FBxB5G&M4R5V^Ms_w8IDZ`d@g4NYm0qdFNUeeGuGi)D zLG{r7jLpc>SYfksHK|+z?r8Ki(mB;bL)42e)+kdJHGf;uE0Zrb`1mP3Wc{JIYJI55 zAy@5@XzsaW?@fN94CuVtuUer)XuFKoq7c_8BdZ&6E#0~g;;Lt4p`#~l$=5uqEt8$3 z&R^y%w#9wvZ2!fzkMHIk=ssV>MH8(&AoSa$!FrFX1tfc4p%=IrNaF5`uM9CdDx+C0 z!j{AXiMv8l|68_WLi;!?P;pD0-Kx0j+zftkPb6A%K-?RNR!I=IJ$Y@t=lW$HC0&#& z4rAW1?+t2>zoa%LTAe{$(M<6To;xFRUf{-MWP`4=+uW=Uabx1WHhS!=%ntI5%G8vg zJKV9}pBa+b7*gXaa=6x)(3Pb!vVa%2$;}-XcV2S%29J%&$WC3@)<`pl#4U^OUGKSm z8Cj1BJKovxiDRGJP&-`Z=8y;*6>HX%xZ7i`GaznFqLmuNy_pON@4a3|vnqtuh&8WC z+?BCrl8IXzpBVDmlH>*6klscc^pkJ$RLU3M&?fg9QMIu&UtCw6nP|qkxao=J*o#|` zXq6XnZ8Nfm6gN7O1-7_bo$b6re+)fO)exidan<}3&R+yLEE73!(x3rt$B!SQ-53YU z*E)(R=9hyyGZJvz&;g@I4<32^nDL_qOzb#p^q`y}e*GsGkp0`OH0#Y441{%u2j6j~_5Kr`7;@Au2oF*|73t!*69-S3IBERgZi9ym9zS^Wz`;4S zY5|QOJS4q|TaanO)KRC8(L%9)Rju7zlz$5+pTDM9tuaG}Oc>01$!@L5U9SuG+Rc(; zgB z9X=~4e+278QU~sWYbR|&`=OwG6IR`2{_1-Lac>8C-7fOYqGrgq-cAk1$5s1(swJZ@ z*wy^!{=>IyuoJyl-(E)k9Nr_ymqNab!xy;y+1eU;wNhjQc?td*LB0&~`#5|yR+4=E zuFr;Fp5qL5mBJ?l`Lf9G@9;C-I;g8m_?5wnV5d0~|B@iT7xFm{ALZ=b7aRF%;6>4k zPr_dj$=_HH-Z1w*xN9yDR501mh2l| zEO71zjz8f(zZhKc(E3dplT^B#feq|jY*e*d^cCpC@5C1LA#6OKj9zB1f{&w5+@1_t zslEjB{*H}LWftu+W`J)(_w*Yx<^PB;Pt+6Cmwp_*g|pBG{^N6hd<=U!_l*a{HNHlx z&iO&~UWfA@#_;Z@^1ja@k1Ql#EoR){0c=NK#NP93Y&MItmwm&yPuwrQjJdpz#T#i0 z?}&4fYDwFqT{0pWm0XIh`$oQ(pCQ5RAMSOMqYaA!lS#_ji(kbbZY}x9+|$bf7_sX@?8XwiE0TjrX_bVr+)-=)F5<4ok|L*)BFngP6W3k# zl8@CAtbYF>j2kGuRk&ubTW@p@t-;H&>(%}aFEB#!B|8ihWzX&U?4R8?zC2#f3JFz{ zIh2@!u3{qDB!dWp@k>I0z{@YE`c;zyGbKf;%Q{}4Ohuh1v#h~l^yhJddTqTbJT z()R$~*D=T`_0A#X7Lz*r@DvSrilwC7Rx;Dg0x$M!vKcBpm z9K-t}%M{N3XFv!ym+`G%@;&(D&o2+n5Uf0Bx|xIsYQ( zKjLm*6Z2n?D~?<_V%CtDwdCFQ0x`%pR8^iska=#TJoM#EAA>pXSyo6G;Bd$j28AZv_ zcz|*l8ij2>jO{%4JK?&@Te3M8AS6v>THPPQxPo~7&a=KA?sOUNy55TatVYz+CqJ0#A+Bv(sP0f6HcX4Q?p6*utHGKrztH^d6vd!br?bxZ2 zOG8raNE1r_1OFFVtl8&J8_oI8Ws^;bYL9n5%04a4^K7Hir*fke1(kOdD=n`fHT~Eg( zcA9SrO-6gE_-xobosU?EjekG(Jb~xOux2!I>R7kOOHOVyuz6pd={s!nz>$*%4emQg z+ro@LZOEk211An6x$5S3a?VV%XFf70MW&?8)RdVPa$vs5*!0Yl3DZ(`Dr~{~PaZsQ zBE4_jzY+DH%Dv`UsWwA%gkvO_N~bq|SI!Hs8_f?K>+VxfRaRS}CF>0;z5#1*pC0%| z^av(&_4rAw$fA3=vaPTw*y7u2bmuJ3w?GS`IfBCL^L^HrTKB9SjS5_|Xma3MC?b95Y~IWAEim z3I5dJPYb@1!3oyR*^O7zODW=opH+12*>I6+PX~uIG+X^BNr_SZYjwv{3mlvL;?D1s zS%jFJ^4EtxwHmwR;nv}f&YR|s6uu9q;D304oic!z+35)i#K+%J7Znm-{%gLAaudq` z28tZNnmE?e+ENg&#jZI~^wB#7@xxi&pMBM6QSTg%*CYIQhtJCClTc$clFmcVirmXiNAHs*V#wL2S_ci z!yGDk=Tnb6UpI1G?VR0sJg7r=$|gZ>=Z7p9y>NS7_$T4!XBX|}w%c$+I1;qsq!DZ2 zA4BERodzF>lGD5b9ojeGj?D{j$0q&V@lMGv#5~?#AJW`#aR|HAxe)c0b$iyiBYkSK zNBX8_kK7x{9`j!Q&OvzaonF-6jj4M{|He&rdTYD6iJt#M%D?n!eE)OtXm|U4z7C@N zdp%n+N8jQi?A*~1e@}z!dtHPte*^s3;OfH`;Y(g0|CfaOz?T&;mf8tz*{~|lhEzq0>wM(QAQ@FanLesr{+o67>=Mi5Ar+et0>f4DFcb!v= z^?c&>R6A``IHWnvv>{{qP8d9JQs05&r;eRCrti=J6NdJkFmympqa0edZ0j+x@4x}0 z2MrrEVB+Au^&8a58PC=pgK`@79Wi*|zyTwIH<8{(UkTS882xvIIy5@6zH1X_HM`M- ziQ^{?WS2!+n+7edn)z@1`lIx#ht~2*x{c|t*Ta4?L3%9zJUXTNRsND;rge_GOUzThux&$QVa5fsAY3n7~5B&Ha5} zr9)n8Gb$1$LT&mfd(VPguZxWPYxH?p{n<$cVrHh*8ZH zFZcb3ti0tkDa-yQJj4JSnNq(K@VDL#)n)=qNFz;J)oRs^1hE*ZMt$!ZQ7-gP;BeoaYzV)p5x)F^)JG^XLtjqJPXVYaJ#ZHAN}&6oa$% z;y=zR_}xB=7I+lR|-H0W3&8eHQkm9R7F3*N)v(nD5?PDL|~RkXn?JG>uS zO|)PJuj262;?uzIH@LLRm7@M}fADtMe3&cz0M;-#7W}+`=kVP+(Ba3R%|Uc+~9{h+^-9{ z_T~KN$`xLhaUG_>N2iD9Z9qEIc6eDdf|P56*KznBF*Rs(sKM(xyiBZGwuHge9;q9Z zMw`woif~_rul7q_w?{Nqod$2pcF!XF&BM^4&b{*KK?TJk2Mgg@a7Kx zh>;0wYz?mOO>?y3e4V0m4X*D^b4CR|#b0i4zVzUq;mAMcxrDc1RDdcqdLYQRC;rDc z{1wJ5kT>|T4u6%g2i5~McuR-BhV~gdEfc=*mT2+$G)8X)`A&r2%Hf+C$H2zO;H@40 z6nbndrVW0a!=GkF8mxRx_`=&ThC>S|+Up&k{#^;bjl=yqkJSy{*5Q7g$L0n<-r;_o z$F2r%=WxHyqsC@Je#jNx-t8P7MI!>30|EOk%!jb$54)^Ok z)-ZTShx>INrRPucr^o&Ji5l$+@#!YKGo!rM;SUM%??XCta=2fAaHhdKJKV2Vc!j~c zIQ(j74{*1^Pjt9n_i&5ByE@#jlQ=#bpIqT5F&cX*{<{Gm!1JEuaKCQgMuT^AxL-H$ zMuT^ExL-GLzQJ=H?$-@G_}Tn)$Q3>fedf9Ny@UJ^?%TuRe!aie20z*1e!agM2Jh+c za~PwfX=(6N96pE9Xl4<-;P0#OdpUeIBj0@A4c^<~v!Yq}&lhuaO_*WrGh z!HW%ks>A(yf|Cs1&*6SO!9fN;&EbAM!Hx#+@9=4)5%rM z|Ni;x^A2?QnT+=H-8A?hhfih?Q09~xe6Yj)I)=*)KE&aEy~240Kf~dEy~4o;AL?+w zUg2>DALeksUSV~E4|ljyQCSNPR)+2m*h%_(X^M z^$>pw}(P4Pe5;eNfv#e8H!y|;db!~MG6dj)xo z%gl6mCw6gVR>m_~`B@J4>mhz@aFyF>%t%#z{G7p6&ZjZMMD_Q520zE)?fC{UC&A!z z9q!jjJkQ|s9PZac9A|LVQ`7i@wZZRi@bet**DGvm@c9n+>kKw9`1uZR8MVZ(Z14pR z_v;20Gx!A#KZf0M*>7V@e!q47LWiezP{q*q$QB9MPbS1Y@@Ky z!aj;M)}V4wd8h)kFH{lQ585B91XYHrKnFkvLI*)rp=!{<&>_&F&|y$bs1|fMR2!-T z)rIOo^`VAPBd9TS1au_S1ZoB~hmL|;KrNwGP;2Nos14K>Iv#2Vb%0KQIzpYHF3^e4 zNl-VaJCqBZ4E2OgfqFrGp;MuL&}mSA=yYfxGzc0DodFGnhC#!j5zt6z6f_zd1C52o zLF1tb&_rkwG#NS*ngUIQra@;x)1kAW8PH5<7Bm~01DylSh2}x$LgzvAq4S{y&;`&! zXc2TFbW#4=Aswr-1`)KaG;K0SYhs8}-mW#0V)lG0U(g>ynD*K+9NTq2ET7)nhM$Hv zmFucMz58`OY#7pc&O!Dt>*LsCyY`5}P2-aKj}cZunb*LMC7$o# zzXyE)eGGjDeGX}jlmF9eu@qc~<*(L{`I2}Ht+w;pDK-t$HCnW$ip8Z%K7;tn#!zjl zhpV!~0oP!ehldZ(6(5VcRdQy*+kkeBEoepKlHIeh7K zo%Au@Y5czg$Wdq(ElFo6Nofkb)7MIfzER$(-6%w@MelwL&nrEacl~C08MWwx3*qRS z=AGKN3P-J4?+)DRQ$YGoxz9ql(wliFYdKw`9-nu21sUliy;J|H6f&#nr52=R;d=GZ zyi3;rQQzA;wd-{cwdTE34?~Fh72Zh~B19Sx?>+?4U2CA*DoE8rs-A-0&O5cGb-h~9 z-l_K|M14N*q&EOTrmuhF|#L+qso@=o&w zB_mC(cj|2kQ6I}Y^*4p6m+9Rj0a1V2yLSU3jfHoIuJcz(SL&VG4+=-E2JfUP5+ePO zcbcK7)Vz|~pdhIqt31Dw_w`|+|C*DiFr@?bPVH48YNdK7&AE_^(U*HSfHsT}S-yLx z)~pbce@r zk|$8wK7n>>7Yb>jkrD3>33Wd6Hc-nDek-f3>8 zLafB-L_tztMc1o`;+?cDLbNx7cha{CDS<_*chW=)k^b2`>9~bRv+Z5a@T7gw)q7Vl zJn2!?=yF^ujy!41}mZ;GO!8LeyjQPQ7X&>Pvek{iqOWMZJ^eTS$dCbx#LG z8fWiHhWPbl48yycA%4={%4sgX((P)#_613OVBJAIU+>h57NWkhchVE-`h|R93etBW zmrJwh-I@?X>DIkd|3)FISL2;}*h18=_O1=@R!9fhA>OICCq#Wb@1!RXBJF^8(x3>D z-o!iUqJ&5T<(;&Ts>L3N@`zz!{~w3vkfzr=Y17Dw( zx=KA?@1(U6B3+Gl(jN(t*2ud(!ri5>^G>>N$w=evU7c|EUW`3?S0}vZv@Tb_S=Xqy z?42|ULZnCVPP#E6(ujE{i)SIy;d&>%z7T2i{qOZqDspKbdUsF=Sv_{|t_$Hv_v)R- za}lZ-TX-bt$}M7mV()NdBjGT*63E=2us@1(;JBF%+&()tLI zuE#r#QwgcZoEGmiLM24wQr?XVsoal|Ht!mQR8HF;(o*Ug=_b9CK2znR53RX^bVInO zMxeZtR!i4Qm*t)Gj6$Sc^iCr&LedgkGM3Irh%`msNsA{$x;yWrXAv?mPTix;JfiUh z@6xdojhJ|MOt`8yW0&4Z52A3S{qRmBh(emjsngh@5RDjmCvA?9G(U8Tqz@G$EvR>z zhb*KW=I|8_1_xagn8BErw_XYrxI_3icbIOEvh zsUFM4$K^fr#0FPepA|iDQMBIRB^_SQjSGL76GxYnW_C4cD1ST0#pvI)gE~O!v++rB zeKy@+w#G-H7rCXgl0dQiCU8eYFLq}f3+!<8ExMoheYi`j!#&>+y-M80Uw1_G7F;U* z7Sams2xsdou52`i+lf0o+A`eF)2V{aCnr*h=(^*0;t?>C*)!7Y5XG+|x}o~ieVL!%fsFK4Aiqz3(4)mEw&`y<_>bhcMTat|{{)MbQ4YF5cfU{L?eI8yf>C-c z-fVxXxkkSizGFa`XIT{Nb5a>?KA=VN?N1B4-%MQeD}M=9EAOjwSq4nE(Ukj~4QJ8f z{=SB0adkfSU|P&w3rMfIm_bVxXx$S#pY6owcv%^k-@#N1U5dm}cN!||?ts5EdvbcM zEHCaB()nMjS-Pr~52H%&?D_Fb^t|;~e|$S>uHWE+yRq}FO=+Q=yqjO1yGgVtUrjM5 zftSON8IRLVilSqrz(u1Co;bV$m~e!bp{{6-KPljQ5KbwFs|OcF{S8hIM!u=r_ob7;%Q(D=n;)G4 zC-?}%4BOV6SAoAz*&{yJ_!?hqXu zWR;7Q7P?L;p!Aj85!^?lbk#Yf=6!*08MSmXIfX0t=$(t#n^wLMFD*~qa1Z53^?XwK zNSjVPp-~rp(>7CX*$iOs9F&}tl}AGz@F?||HUW7K|J0!Z(3 zJi-TQ4ofmpKBd_0a4=>2c{=%&?;6!n++yQ5IF#$o31xmF)%hBKU;95?IlI8`_8Qsx zpkgi!7~)pguk2Gaov2&1DEhmXd8wYKQ1{)Ny&^XcT~w=q9{WbSZsqs&>h9Ov#w7a#V`>k(YzNXz)D^ z{*J--GWe$k-`n6*z|;E*--DGgFN(4E3i6cy+50$rk=t+iH-jr)d$5AX#+Jy{WQKJxnnc}A#atDUxIbZ&eu_yGpr*Wg1EkdW$^tCexkuE8T?d(S2lQeb6?^6u}04X z!XFXj8EKnc#o>NG=P3q1z~J)?exSiGHTXdWZ*T4^ymB-&9?5;L3-USKH^&{SwTj4PM>g9~t}*gEuna3(sM%q<)0+ zO^~la_=h@N`Wfty41SowYnpI`SHrHfC-P-z(kQ98QrNbT(U4sRAW1D{~<1_nRd z;0+DFBQIOth1X?|E!l~lhrH?$Mt{3oe0c~5e0C!vf2+Y88~nSxZ1}?KMRlD0=)FN+ zc7C*yHJ^h1KjVf*UU(z+3ZsP@JsRX&A%CR9y}jmB25(~Umkr+3;9Cu@_E96oEcB)O zBrh9J;Z4}TjIU|*X^>}>d$!tHO(JRd=t3C$D1-lO@S_bLf9&(K!f9df_wus$72X`X zSiYiBsUY7GdD#~*!dpapqqiiV26;y4XSZ^AIaX>w zFJ$o625)Zg;|%^%UN(H;Em@V}H~iy}SN>ttzq`fzhH$`Vw=wcV4Bpn@&*Wvp7k(To zV(h>l8^Xtupu5HAhj74WA8+I@F?c(JZ_3MtFT4#aB>aGXWsvVfIPD$&UGyFPqj_0* z;cZz{;Y<7*gFKcDvpYE4?>GK{kr&>9@qkb9Zx8ZVJj_19;oc7QeuH;3_+tj|Wbmg9 z-r3;yWqmb|6 z@E2H@gYshVlMP2NBB_I$GHoCk06f)(d;1(_x8&B8T<@`A8hcU1|OZ54PW>O#(-Dh z*A4QE2!EKv{eJRI4L;o9tqnfH;6w7V;R_$d_|gjePC@<>!XN4I<_#{SP&c(kV$X|v0c!z5YmHwu|Cm8%XgHJU0DuYil z_;GpJ`wE}RSlLYcdxHFO?mOAx($ZrGZ16J;{)EA&82ovIPc`__dD;64pUya^u6rZM zujIbd9Io*T>VJcuW$5+XE|J!1B^=>e73=B=4HbXehwqlD)-fc{9S}I$Kl={ zw~oQjF?hASY&gQ_V%Mm8^vEE858)`^%%vt){aqz5D=&N=IxKy!S_b+1ke}!9?tI(m zr@xog$DQl&Khwvdjoih?G^>-#+Ll&8t|DY@O8;GZ($to;9^o{A8bOVrBOuw3Hieo& zvLQVhY5^SswS)@VkhTMB4|RZKL)r=I40VCJLMK78APU2VD=XfNp?pgl>XvhHimwg;qniL3coRLU%)JpnIYF^YZ4*`QIar$5~oK zoGlf0F2z$>9gDa7D~eBTP*dNYPciu^o?_{>(=VeAHFPfNYpJkvDW1yeSp32QbS1}@ z?D^f{DJD7bdiST7V2XTwjUlN2*OC%uNiFKVIP)B<_ibh!x59jBtRy!y9ua?wE7T8i zXUDTxe{p+Um_Hx6@`P6mJ!|P?Z`_}AFA-14Yo*Q7@oTt0=N`Y>k7*vm)3%Bxysxnj z`qkX4jJxB`|N0*5>9juQ>Uecrn14@@dk6cwXX0n#!hGq+6kczvQoh6$=1c!5xiRth z_b-UGy0|d^ zlOT6cTrGYFSD61*Xa8K%{{NX99l~2l9lSPP8yDt38sz@T8m%wHFT{oU?*+O2u%3Ji zSC}t-rqaC=_M89273NFNE4cyjp!jQCVZPcCR0HTg{hB1H-!sJLV!lsz$9Knt`AdV` zde-^ez-pU?`D%YFKIdbVc4vHNT$mpb?-;qpu|^zp#oYKjR=L~}7v^ikMRJ$2-skVQ z!u$@ie7V1$e*16H4)Wb$T-AaccMspl&7jJkjpJRB^2y)D)Ou80Sd%WgA-*9l%vbM1 za&u_s-5%c_7v}FkPPJ3x_ygRo|I(5xobFhdZ^ISlSL@>INR3hb9k&K~J`j}IG+ktu zI16M9t^c!WwcboyL+SPc+M0K<)hmYau8*&e3-hHv(VJO^Mg3!}h+LSz0y(|mE$j{Y1Pcch{QoY5Q-`&P-^Uf^ zr}jN16YrkkI(-$jPH>U<72Kb5+roVgWwnNPafSJFkxK|y?|xIb<{Nf`c`1G=F3i`M ziqfJja}hqn73RybvOaRkUkYFON_ncK%T1TZ|EKVGDf^FcpT>M=j^k^S%ey>18io$x zOuq8-sG%;S6@EQ6`<=YK$7%n(!B_ngYVhw_WxI4-o;tc3=~N#JyH-5YiIl!msb@#< zt)5Ej=Dheq>Z1SN(90$N)5DjBPC1`7oa2Y$UH`8M=Qp#X4l7sh@_#$$)pB^1xAS!T zG`okWe);=H8o5)hJcf7p39c|-BhZqY%)9*&SD1e?xk_Ks=26MS(!P2Ai*{@z9f(32 zMNYdpzBw+;9}?uQB@f;g-xn9=YYb4~H6b5 zS~p)nzjRr&Jim^A2#v7518H5qE&9%VD>KZwNb7yABBQ#$9kVxjFtVF|17>0ku)FWT z|K8JoKmR+@R%>6YWEIw@sh8*T%@FFmmGR2BF#oh5x0KrOp7@@)F#mxd_aSxUbMbR= zVg8#zZf|PPH*kgdvSm@ezJPl4S6pGf>}HD~m(J}NKnbkP@jHyA9mE&x7}`U<={ZfN z?RY6Yvi}s-{G|s7-`RWGcL&U`T73nk@%5#*MC;g zp-+F`6kZL#+LvH$MQc^R23KHl$2t8bUc+chGe+ni!s-Rt8qJy0vX<-0`STZ7Ld)NV zH%u0KOG5+Zck=w~!BTUsk5nh&x8+2%vV+iQ2idJL@#u#|=%d)jFW|nTiQ6jn+H2%5 zczPc+aj0&q(VY7BJmNMFYqsmrV>I^Xf0jC&U**~KfsmU+7%vg;Z^~k2wFL`>=g`gU z&29n|ffa;Zr7QQQOQRm=FP~!_*$x9=+VN))$E$KNFh*M?i^k6IO=fz_74@;~Zx(Ub zb{!>(rsLC)wD5<`^%5J3d%rGmw7MGio=%-$>7ExJJ9L>$iFx z$KBA{RYX@bokbL9g;g%VU;oL8M4PrfLK!Z`f$U647=D`hLaG^@&l$g2IL2KJPD1e0 zEFJXr1~2OH>1b@RCs~mVU-$8|O^LCQ_YbNW$&_`2`Zx*XmHhq+9~@K{+~BEFQ4DZIL3ht?lW{Q?OJk& z!Fi$lx;eY)vkhLs;ojbPfWh~5xVM|u4lC(<;RW*Rgd^9P=i1NVnnQ z?#Cw%I5>`4Kr%_~4^4s=K-WOhNmk@0ssGJEn5KdY+l;^A4%R!uuW=hChur)P{2x#` z^qDeiZ4mUD4agwUYwA7D2{V=ae5F&uQ`Ep7D|LF4H5Fq@bx#INGjCF-IzizyrY6`Osb}(M&pm_lN~?5Uru+*7md*x~ z&&QI~R!M12s;IP_N=m83jNyGv{C^Es|3=SuDDwe!M`@k!*A(#oV@R_Dxuy=KqyZ_} z{J+4Z-z0sBQujhgw{(W2{JXmN9g!MxpIpW%|E|A0gq6-Tl&>9S^{)KvRR6EKR?na6 zZVUQvabDMGrd#T25_SyMCh8FG(ubNPb*G2Ylg>$(e>$HNh0?<~eWi5gBl$M%j`TIu z`4&*&Ul$Qy+HmRb z4c{z@HqT^cVJp7Wq;D0@Yv#LtYq!%zJDt60sqROWz{2HVMo2?jcF~hwCjI{xZPFWP zmucr={T45!E%q4KJ{(1}Hqv%j=5hUNj^CC8Y1w>D{5gMAQzmGQqaAbs+JF|cLC>U3 zdJY!V_mCz$*Tuvyk571|dbBNRDrm3suNLwJqM0!T*%^JH4TLqtTZumOC2idie&xnI z(q=(Vzv{2i);J71mL}@D@JDpa-p|mS$hR9m`K<=~QGt%~-#O+7j$^X_L z;Qf}gPt|mKn_BqZsgw@*8+bmgq%mY$5XL*4@SmkMAxjeN?$tfKH%T1L^QFIWq|f^+ zXmc;8UEYLtJ85zvX|Gw+D=+ga-KwoI@q5}}+;b`E{|M=<-_Ps3Z~rwf^6Ig)`JbRI z9puvU@htM#xe#gc2KOEF09w;)7zuoy)&bXb;JkLOyya(q#hdfIzvg^uqQmJsjNmzW zhqXxa`lLgD@KKw*%w3Oo|3k``{WZIZRae3}i$C-oI<(h_=M0a2wWmG1E1x@w{PK7f zf04LnfAz?nWjUnXNc!nl7l++BslPMs*X`eRzK~z%f7ks#^aZ6CM5-4f7ZHA#M?Blj zS9!tUMReO}nwx*}rNN6ie1e;2Qu>>0I0VOUBwA|Tw!up|d>|v@%mFia8Ha0!N2X61 zT+c`Sip~Q2C4=wf@GfXGnC4}0)qa|vt#!d4Hn?x+w?xlQPuAf3I{9YkMd;ZXd_RXb zM1M#r|2F$vDj>9tTYl<6@HcD9PqIQ&vAHpw|?_NgI9C-9`QcN zuQGUbhZjTBPHkau3OB!hp@Wa2e;T}o!+&P2AM`=w#`OKxa`?AsJ^3~oytczXXE#Dd zxD2j*#*Q*hC)(fO^&P%dLzB@AgEw^eYth^I*BHF9!=GoB9#)hw_>m6Z96gKwfx(+P zd_C*7@Lmkw+~Moo`ny%AHPYug+Tm+ld#stkk8${FXII+O;4K|~qq7H9?URPn+TqtY zJI+fD-p1kDTa((?;Kw`sB4=m$s=?bke7>`%+-~p_9DcU5o1Ct4M8;RS>*Vmsv@-oy zD8;)tT)iI3i;?f@@S%L=sj&>+&EW&Ed%z};+Ag6W){T;}xeixfE{YENg8Slg--}Pi zk6j;+`N7%xWIZ1!@YH^;j+>V%8z{|B{a^n-PY3nqwTG|#6BGL8FEVA7G@rvr+m(#6 zF_IX)9Obdb#c!%$=BjYx#&eK1BAkVi_Yr4H~DfYa*TgU<8>c=mIvpr(9WrU51 z76j~Dms5ldh}5T5UC~6$%ZVjWf>lxvAo;7#2QvN?@7tx=oXNs|g zQ##hyu)yfXNM-|0W9H&<;eKnPdjeN2zA-%CT*eWvXAJQX_St-k^)Y^oGU&VyVhpzh zE2{L0&xospYnHgNRK>Pk+=gdS$rwl?JfJTnjXiCeYfi~RLPz9){} z{ldG5;|z90rC8@)d*NzDlmm$UL2-57kavgkEZ#NdogKw)o=Ro zCcPWXGkZ5W9ve@J&y1(XGjQj`^MX@7p>&w+R<;(`FFHHVW1mOY1g?DiN#Od%8po5| z?087PK5{xxVJ%~obF!T48>vnaI@D?i95$&z}Gx_3Qs3RBhR zKiOEqn?YFBg2Xj$_KMzgv3NzmD#zaiZbF>ylzV9OOu)Lt+X6Q*)($BDqA#Hxm(Tc- z{?JHTGkkh76(aX5LtDJWaL?<%PC5EI7tl7==hFP9A){F(-&Lj`Guxk-aEte^pG+Tt zw)A7i`Qzf@e2bM}uhQRHK>y$p^!62LPpzO|M;rM}`Up7><3CDUzXk16;&&x|ys>Aq z%g8qR2-i`w_M*OfpY}X`9{nDDi1G17o`3HeT6eVje>jMK((%N%K@@2g&P(U|^TRlI zJo!eylE;#NjzsHDI(&aJt$^|LPZ#@hUlB+9>whM3TuX`Fp9M_)wm_jJ{X6@0;a!ZO=DGr$sN6-bh(|zT+GG zb$;ERg#1&~<711t6P3x2-1)04UfSV)p5#V@@8xiVz?;5hQI$KQhkXS2|oR z5;PtLzt-V?o?9D(U+-|OBZo%O;5Rz_HfA`W@iDlsOjq;$LQ`gN$&tfxek3$aPsM|h z?)pLh_&l3#_c;7|zJ^isiow@9e0j7S|4D;W0rS&3m6R`oQ>pP=iesG~gFoYNt#Ify zNNIdtak$@SW3-Wf&*6()f2xPUeWkvTFF4~K23L7Tx8~-p)iU^R4qrf9k^YgvRlmp* z_k8{{2G>4Bd_!<(__$dbPtE^EkBH-IVsOnn*9uZIkNh{OFj>`H@c9>1*n_+UA$ zTpGUCC-5sLQ=b_5)(+Qb1R5)YOW%=>+a6@_T!%|1juwuuV;a8JneeMjqZKvy=?>Ql zu-J?ke2BySJhv?dAMS9icuFoZ_!x(4bdUPe;1eCL^$gKp8T?F#Yb{Ox6;I!!@^#_cL8m|pYX<%v8~uHRUku$E z&OMF)7W4`9eZUT?5l6M5=8)oeBEGhx9E(2(O4CKUl!sYjZX@&*bdlsBEhiVSVBAL>`!=f#etW31S)*P%nCXDz`JaXMDLS`l7YQ~b7&umk8&3G2qOmXi~& za5=39+I=i4LGlI(g|Pp)$!=S zUx2T2b_@Q40dwCR%B8S(1GZlie|#ijq^m?LRZbWW`=Sx<^rPvIP{DFH8QpbJRJ)K|M%GHNX!7kH(-_6oTX{1jR{ zy=fu!4R2qTWW$_3cNFc%$+S7A&`zC3czVNnpZ;Cy9WLZPdRKZ=%Xv?$=*g_6_j5aQ zuI_a2Y#qC!Xz!Oz^ckObZ|+Tct#4!D{1J0zKA}hb6}|9p=(&GKZ~s?xc17Z%=o`wS zqwsskR6j1=DNi)Vk;|&r8Fk_oZHTzgeDF&-&VcEAM~ypvTqn|BUyp zcBkHp-i6-7!v&7C?+OYp%Jv!g@6o@#D=1aFN7XJ?Jk%PNPDs2oLE4|CI()(B++Du- zJ2Jh(b4lCq7P=DMN1C4xxqlJ(Q^=hn={a;x+70ckqI1f%BDL*xPAz)rqr^)KrM*+c z&%&wSAzt;H`iA0FH%TK|23|Exnp-WeeN$3d_0`17UQl`?@zNGarzKvREa~&aON*zz zxOnxzWvL-v+63uZ#Os-*uM#gUmFBsK*ZY<&*iG>NxAOiI|07E1e-_I-(dsIFm5*gT zQysn+Wny@!vC+ts3GYO=YR0 z{P#okuIk*>*IW|GD^K2vLP=#s>EX-Brk!~8fpy>1u1`7%T`Nt5^cmu%#W;bqlH4@D zC!z%$m*8067}ZOuFCCGtQHw$PPw~=vN+&E{nqcX-#Y?N*oo}pt?g<TKkaa^kR>T^if zEnXUKjSGlZ??GcL;+0o4-Xvb-x{oPGn@V|m>$L5XZP)mg?kZOw|M}UEAM<6gw`&>u z|GCr)%A09>M*30RUp+02Er{2Mg2pSvtIxA3@EZB}lDbUS>uVs=9CXPkPijm-yhai< zJ|bSDBHD%L-);9vudVCVzth-;c#UYh9(aw;XzWV3MyxchCSIdy8uJsckv{3W#jD?_ zQGW4?tHz(itDmE>4e{6Gz76%Mw7c3o0~RmMuk_{ORmb-zP|s-WL+8|! zeJxzC@07+Ug==I|yXNS=_Pg*$Xpbl_%Q9KA`c`Q_oigyY4`lgch|4~lAH;WTXy|>Z z-cg^pAo6I4kJ3`QeceME{o)~RdWRb86s{4@7pHq(`R_%ZzY%y-z9+4y{m*b;Yulr@ zZI-p}o%%1qzkfy5_{9(ll}JA1?n{yOYV>pu@kR;GP@G7})l2b(q_t0@K*Rre}ZwzlmU!ytZx{V=MYurD~TIw1D{BS;5@nYO4#6v&|y`Qqz*+GCo?Jxg$`wy)Z_sjpF5-A7}yKkz#==rh4!EuHr(V|?J9O%Icz!5f~l3a@>zBfS;P)RXTfum-={Kr zqb0WM*mXRA8ZDY5qGbhgFIqAq;&wKm`U&roPmeC8EO)k85GNXr_Q;SCy zQ8l?fxquehCeAU0-uxY`^nUO=kgTtaJxV>a`NQZ1UdsFcYJn09DUm<(0Q_oFtN-H= zMonAO0{XyX>yX>Z=XnQa0xd?9b`N_z{lD?pl=J>&v%FvOdcHU#qNp{RnfEUytp*d9 zrHrh%!20+^;z9gdkv zZOh;09g)7zo$JJ+7v^KlcpPb7fq4g=JoXRX(G#SHZ26wQjQcn6_y_BwXai|^&~ir3 zx?;c2^J%ZvJGl0aA%t_Qx8(k+D)~B(`5?z)5B`+Dw)NxK_MAXG$qVW4S<++Qu{`Sp zY=$25=-s6ET=K~k`yn6Vlgf8R&i+p4eU>|lXB&<@>7ZW+?s4Qm&woUkKSiFI*wG)q zm*;)`-?i5B56`1@u-^0PaiqV?$P@Zqd^`3;cVlNnYyAt-;&vQejISuc>#5pu|qm;ydIhBJ}{_X;0^p z_NnDsmx#!gt zts?cv#5Iq!53G>tG(W&xye3(b}-ZxN4vU8VXJqn+#u9ud$kbPU@_SuBA?eF760(=~W2XUR^jk2HaGNq`s3-MY zFW&EgqH@BEt7rHlDaO^KqGg!TSmV6~{bLK(mOA}{w3o=v;$y-Im* z@w3Ouo$G!5Yv+>mr`=f7--x}5>ok9~Kkwy;3Rq#3EUf(RI-kdv|0>(Fm6R zN`v&p4NeB+_c7MId>IVh%Hi+RV&!XN@U{+r+pYJnFJ*e)_6~oIRu^yF;9VU4qMP^9 z(BQcamrXIVH4RQC=JzCa>3p3HPNwI#f%X&c(cq^$ob}Z3ix_-}!`ITAVgWutBwRNfW41Tx6Pp8L7 z={ESi4nH;Ok6+W^dVlK4_2K_OgRd9NDpe=*zmLJ6aCjHKACx|WQyKB=;JyOC@RnkZv-!kuXe**g82$n=f6l( z#)2Vj19>2H7*rqX23fo}$J!b-#iqkw7><|VFNd`Kqqcv03Hlh?4z~I zO@Nj`R{lngw?bC-Q;svuvvOtO8$+qg1yn%Cg!3~$Atl4PDzrSi!}o?xhlWGrp=r<+ zkj1a#_h%eii>+=ws*ys0bSum4~WBr$a-bG0&@)gq26L$#q6&`juD=t5`-bS-of zvCi&xYUnoTVdxhq z`hVDa6Y#jI@(=h-ODRwmWmgcc1tAGtvUG<6X_7*0)5a!k!KPd$GdIc5WG2o`(uI8! z6c9u}G^f^05;fGCTA?4Y;+F1R7c^8Mbk+;h%dW=UF~@B1EZJGu9s9=`*PBAj=*HyW?^LAKpjMeEJp$lDEdr6rOJ1B9!;8O5UeGtBqVVbiUI(My$)H)u zdm_eoWc_DD&$ok~(-{4QnEL&w|6mMV&q})9j=}vBiTh14{G0)P(zmRl@E$34m!cPg#TqN;*K8D_BIK6Wx{yc`B1)zt%sTI}k zI<)(>61K8O3{IbuIDI-spZAjbJn^OIb_daJvV`6}5kt$NpoN~Qi_+((IUin}M0-BS zf2(5(pZTY>DQpMSp9)L+Ali4>hw+}|N&GuuQM}2b%tvDAzfjVDT?yXN>|GSxoxz(- z3EGDPc0vB1?BXm?p!xus4ItR+P&P#VhrVwX)ou>-{2_*~i@=x07+OCoX{A}%xHjqA zX;Ezsqs?vhj+WQk++OaTbax53|Bk`;DB!z)Oq+SMd2Nhdeo*S=P>gJLL4I=#A3h`b zK(p*o`u7{@cOEK1&o-DjiQ-cV^em5Q_X64O)R=ak;O$;I`I!>!UQ$Be=zDchI2FLh zGh+C(EBHh+#8gimrVd_SqP;aSbe=EiY>UCC2z>U8;a@-a_eu$T8e@3(xRlv9fv+i3 ziXqA7J}GVP@fdyjfYhgDF*uI`=S&Q}=SX@N#^ClC$L(j6UN1q{fie782Y!4xMs~Xa z=M6D9ohNZx5`)ugvR~h7e!T4G#Nc!SaPngKv>tq-H?+p-)9W$(-$U~M%ouvkmGr#7 z1Z?_lBDJgZSLyQx;BbIixFH)ZL#;qv0r$A;GN#3eDbSB%Fmk{HxCu3~qOxO;MSm?WvQLs+{&f9%Aj2FS) zUdnYihF6bDUL6!c7uEkWr|U-k4a%stPeR*kV`x4bIDaFC=EEe-568&<7~s8!q)F)! z#kF)$)Iqp>2e9d^4#n4PkubZ2>`#f|#Uq>-H%z7{^-0Eb2pI{;pH_aR1ni+0UR(lR z{1&tlTs5xd4dBlttaneszq$au69V-9AO^2bNxZ)DQnY@Y3>neHUlgw>zPlp^?%B$R zMd$|M{wmjvowvFuhMx~}evVG2?~zj5Na!TmJgPAgJTbX@_SJq^6oI8)gL%@7>|TRv0bJ`h9u zpE&KgiK{>x!B>aEeK#b$7z6)wz&=C5r*F?j;dcgb-wk|;%A=iTsvU~!|Nn0P_q4!s zFJ92op6$!^ba|+*wd2CrZeee(d(&inC`5| zjbzhKW6u&Vl}fIYr|!Jx^{i?UX`!1Lqp9J1?l?AcajP@4?Z`sPnc30p%v_wC+S4-? z=k(N`(bW9<8N=yb+-7rwx&E9p6Q#O4ota)fpUbcChI9Gi0zerWE;=(?-46Gtd2`sz z=9bm1&eXmwNXJxXdb=~VWkFA8KG&a54mpc6gI=MhBa_PK3c0?bvno>QKpU#emlU9C$BJ*zX>bZ&j2XLQbt`sSYT)YYgr3&K~o zFKb=Cx=WKhV_92w&tmYxTc69X>ltmFQ9omL{oKa6AXnC@idmB0!3?B7CID1(iz@TM zTT;W=%;jlk<`F~6G*)!CG{8Vwkq8TE^?FD8m6?be!&X)|S@!br_6lrGo2V+sjM0w@$b{ zaVnDAlb0KZy~2zEwz7R$cT*!9$rdvGSugEmvPJBM+7d1Hp|#OmCXHQV9H*tptmo7f zig46yN{xwtQ&-Ds9Q;cpYMnz4!FfWhQ|la~k`6tzrm1ndNUUW^mh0;)ctzIOxMdJ0 zt^s#&sbT({E!RY9cm%Cj%vmD1W2I%Xi$?nTygY-XoZi7)YMq-M8R|tskcrbAo2k=C zxV3f*xsjp-SFU#LmZQ9%Q@)v%ZYY__&PcJvZOtv6PO=x>F|@naOC?7Np0liFk<;%L z+miXgjcpq;#hO|t?-fV#WC6weh{sY3deC|E4z!PVphJEKIx0HQg6=>^{m!qaP(-f^ zF^;usEYYr%rfT%oukneF$QRbpJhyjNK{09-^atO9@)mFpTwrMyC+9Ju!EXmT)< zZW-`W>&lf~oXZa-i(TMCw!d6iVo=#Kd4qlM>V*)Sn>h{LaNT%N0PYB%>hNIvL1`Gp zBEcN(6_({Xk{f$H);MQg)13M9o93vi#s+>hx4vQSoJinCD=bJ>0tP!*$oAjd$vklEz9MF!qN+1LthnyZq7Fib>h!(P6)k##01qASAhb>z|` zgC6VfiUYYcYsVlU-POtZF84}Dx4_{LhjL9d_spBCaawUx`W zmceA9z?ugK*_ymJ>?MoI-a&6I-g?T=ce(ERIkT9zVK|S=Oco8eeaXSW;sE;F{sBJ? zqmQJEX3$yZ;B=`YV<|M$pUZFb3xRdTd~PsUPQm5Ay|6sXE9X67jJh&f?MTo{ zR)|4Vm9&nfsuaU;GOq+ZjUutwh6lZDe~~OBqWy}2*tJ8NPz}veIpYzw+Y(YM(*O#& z5&Ewnscc)re3qzeoI`0LQY+VLLdb3rG?%L$$mg9Qs=>@5E6gUuiJ3rK<`T`ZFb0*D zv(20NtkqtpI%PJvQ+>s1ZNS7N zz^cL6hE;WS+g_@z9+qFU9r)@s4s6GPsoB$odt)Ctv*V8RRJP58iMR6uaBciIUnd(> z$GBj_SF}(8TPPN0y&H?3ISDAHb^`VuQK)j-G37c`wuOj7b_*nnFyia;89rN!*-JSH zvlW~pZWk&8mLp=TX=Nt?jntN@=nN9j#13LBLdec9m)~$O^xb z?J#Ua%T*FuP_~Y2!Ymym!_4C%fT*$X9 z*qThqf~$5qgeIVvLmsmZyBZL=@mgjc-opOsbi^oMMOxG zDqR*Fj4eRcDan)3aS48FQ&t*Lrm!KJ3TsHVk!&igVF*XokW*uE3TIG360N+FK}|Wn z8*NmgW`-0hR5GK1DryJ{Do4J8lBJX@lMUz=BkKsPrie7Oo7jBK!j%TI|ZSOb(;75F`xSVdbs z7h%S$uyATm_VB3_5=Ii=61Wh%{)|$pKpW<;pk{$;D%Gy3QWv7KyzaujI8|txyMHQ) zui|0yU}$8p$X3fKEE_E=RaFg?1$jtH_|*hx2X+#&q2vbNCI;yE~-ZUhoK*(5rmphz+cQB_Vd-!Hr& z%$=wLCV-NagPCSU0xb3)$Fyo@nib7-$CQPcVOFq72+2R3ZC2JKgeuxuW+hEqsFIy! zRubrkD(GahFo6-${c#3nBUAe?Ph*5C=lP~~v8Sy|H^s%U4Kl?bHvW}%AVY_oESiguP+Nq~%{ zPq;in#H^%K9aEQdvRRl~P!f#MA3NKuZ0A_5*;!^KYEkQyRmsjWE5%gM$!1}brbsC% z^1(jw&SBYjpqG6NW-H9Z#P@n+yyPfFFsG!hdhmY^%jmV76 z@VRF7Ml?PQby5NnXG*1o3A))wNX*P60o6>2Eo9@W;)(#SA~}d_1%)-PR$+me3Dr!o za5W3rxC(s?U@G*GM5lRLrl7{rC@ApJ(Dc29oi0F7NQpQ;N`0&Z9;-D{XxHFohK2{- z8h3yGcocsK&-XhB6dV0~|9Kk>>o(ETNYTaQ9L6*!SZWwLr(2^bF&}`Pn(}mI3Y$M3975J=8_0j4NZrlCUQz&gf;u4R)n>>ykW4F6gR<>q$>nnuBZuCp|OcG zv|_AF6*s{XDy7OA3@KOC1Z(P4r8t)$&;$)MN|dzmEm6<}DTajO8|hR7!yLMc^xIUuXyjn9 zBo-Q&p(k8d#{H};uTiyn zMrqX8Ael&U>c$4fKxiCLOk_BR{BCaC92z?$6Di%9jt8M}KrxX~92&n`j}wxK6wx+* zgvJ5IL`KpzZj~Mv91|hXHEv|8grTEd6A?9-Q!=O9zoAVoK)% zL%_VJiyKW>P}&5p?SrWW^ccWpN}50w4nhgb0j{9530&)AIlyH~nm{EETv+A6mMv=n z1`ZVgEm_V47#s~&A8S_vVTBi7m9)})lrYG%GNdX7lXZDV>KT?%^}?qb)kT6TSEIB& zb8*oi%(O&2j~?B+hGp24)!U5$lutaymkL%Wa2^{NF~MkP+xq3Pj7-L`8DNpPz;U*9DE%N|rML z0-dtJV#}C-REsJIDTi5PITIkY8d=C7Gq#Kg$kZg>dzRuD*W5rU6Bt^IDq`alRl)?q zRa5PIiPh>zG%Kh|JFawBFELY-!-0KB$W^g`-a4-Z%XS!@mN{wrx6`?K3**GLP^*ehL}t+pH*V*_BKpa}{1g|Ts? zS|BDWf|zO{7(y8v022kD8@BHm6ECU+VWPnasj_~M4^>t-o9A5c=}UJ1NPlrn)SUWg7b9=DR^On}6*a=>ECn1E!9Wq4Mi zlnD$imH`w~!UPf+D^?RQR!~(;@n9{+&C|C8<(7FNXExZ&MtYfCEu!}}MU;`bcvVtV zCZ_x%)0f8pYmWw_vFyd9N?Ks45Tmy?@l@4|^Y?o36jem0@TUq@vR_lC61G+Ygr=1M z{H%NVDhr=&FpVzpz6=*Og-;c__{JT+Rj4o)nUPtY{2dw5)%Y7QXjTv}Q&OM^H+rH{ zQH`N*^ouLRW0%aWxB@+zNrkI|T(+=+S~1Rigep;~sFGQsEII_SstT$wDT!H`?o}Rq zKhM+8*sG`9Y5oTfzj9Y+Jk}qXP0+>j^O4ylvP4baYs&W&#I$WaZmdw#ZAWH{wj;Cb zw#n}~!6$tcViB3T-H1&6GJya{swH@@KwBI$#ZqB04UxKwGSEsE(U25w7Te-+i!G)h zQp2&u1mu!MG$e(a!S#5|VhU+!)M!j0ja763ngr#x!!e?%bIE;fGCVgvhcJ*6R=j;( zCzQ(DkaLH!4k5&As5!-8I&Ymns4m|*MD1y8gd}vEX35G*Y5YsUl9NyY3eDDRynMPs z_-m}<%V?b9%Lt6@d>e-#J-X#!j&3iQ6HT|>j*WwE#ZK34*y(X}NUtdQrzK2q3^U>R z#=X{KZ4XxO+$nm40GhBKj)>4jo>^6W|8*RO8Adf5pq9TMh4|rc0Q%)u63bw5pYcG> zcaY(BG;yMkO;yzux#&u_C=fMUBSw^HDO|y%EtVykGGB}Qx7t)yNzoKrDaiVgf(;g~ zh!z>CaDU6+h=?ZeO=-AaTVAm!h5%Hnh4cvriknK(h6q5T=(kYTL`+GkcE(hxVo9l1 zN>WpbmJ;f!Vl+iT1w|F!6%-|mD=21am2lKCZo}4}3W_RRD=131R#42uwS)rOn3gT5 z@GM)fqz))ynm9(W-$pLJgu*JmM4W&{3sm0^i@@G6b)sA)WfU$$1Vwydfh;`Kvj~WU zS7jy3z%2_-Q>siTN{K|N@+A~D@i5}lG>k_aw-pv~`3i@){Ad-_?Mn>lz1XINM1hi- z)H27;2-VF&w|c#_9P(CCl+#ohRN+XK%FFFa(6m6~1ku-9*^f}T6I-FG!USCg;X3f+ ztticiZAVqp+$mcn*qUHL*=nk#3X2C@(Fh7ntAe636@o3PzOkfyY-%cKLE~SRQ&kit z+$&^gg~bS>MxYE~6&KWamVsCi^OeL(>OgEJ)fPjgc>B|vo8BaU`9|IrC@K_l%}`GTGGNNZRX3 z<%`Ke!5f+}z@~PnDl=BK%&(uyrlRbO#)kUF#u@W)*U*=m*W1)PKiS|l%=4Ohr?Rm& zI+{#-eFZd`8c61yI=n-F{hV3ONVbsa&w6PGFHL4OP9otfbZSPE?qH@+tgUxy8A9|L zrw-w|Q&(F9kva!0blmo3-CgY;Z(F{Y!uVQedMKrqg`v64t*tBCy1GOy=b%6(v^9#R zY6^IJdX8_kLJ@fb z8{vPUct|RjEfgKji~|nkx?0QTF-)P4q>2u{DS&sK59U(ILDcKR5So%1G0ErHU@qJ5 zOj$E$EnC{+wzrDQ^#sr@g1;z>ztMmeEO6bCY&JRMrQQ1a`nhg{s67azMp4UeqcD)m z6BvPPxny6mgDJO|8S>n8vY13|MO3n1U>ZsZG^i?Q`e|u__~O?LWwBwORAQLA9qLW- zTZIDWt4s{S^Z{5*N1HCEX3i{^zWe~b&Y2~(mqZ{hNi;R1xlFni@ATimw@_mNqkH49 zr>;A5gP9aQL9uuwn_33eWBJ5OjpV)HeQTj8s&yp?n~PXd=^ZJ0NMx>`!|%GOR0DIn zoAdp2qT5DsODDQ*61OdwH7pR8tF%H_iSX7AaIQ;i10~xQ>W0%4sM}Z(j)N9L zI0nnaq$sA6Eda+#kxNVt0ol5S8UMQEFKcaH(dsT+ymDEK zTkkfx6k57%MonCok?ZQG>)m6iorLy z%`1*r+TPV|5~9#2@d8;8F&WMtS0Pd=Y^M?YG8h$xABJ27lN#KHEeO5doi!Hdz#lzQ zVh8-VcdC$6q*w8rBL2s!lxnf!mKw*}QpGJ)A{R@iDu!HHhPIr;kPVdb7m>cN zQiZM1UC?J6^I2zV%RvWCb&hw69=^AbEPBo%YG~J*&y0Fq80Ss%M|-WAyq!Q}b~~kQ zLy-~=#pG4Q0D3}=i!GFnm^#7etWHD_PR#)>Z?1NYI1`Aqu+oV;-QR0BiB6P*(=S&>gM z&*me2=`-0sl&o2w%xAIlvu1dtFi=Zl%V0)2Q%Lp>dJCLcbLTfviLeMtauQKp%>f8! z*SLyX#^^%~5J9{dIxfWoA`rn;C#GOAeS|NX*U%7^(!uO)frH`|f<|<{95Qmqks_Q^ zWjXmFdWLhuM8%2!@8bxC;?=QYaw%4fjSbaG7gHA$UDK=zNq}U%jgoa(t9c=e7rDf^ zd_r;uhxMIg!Aj#RKD2gID^^Ivw3I)+?sm5w)m<~Sd%$x#mv^-v zpPi(gBNBGIXI_1tO}(NkECZf?N}7T%Fi@sq~M{H=d^b%cM!Hv7{MQ< zz^V>GS>Q}{CM2AxGn_8ZbMS3zn!q3M28TKLv{y`K1`9K$0^4QYdg|KaP(%BDa+bsa zVx!4?hI(peAX#vFJumANMuxq7%1dL7u6Lu8_11fNXE4*7Pv$pLg&cB|MRfj7VIxq- zN~DAa-V`^^a9Xg;l}V${NP!9otPwDAGAqKq9Z*^QhQ)31lYX?Pd4-3VF2z5bTrI75 z1PQd#T4b`%D~ob>&XM7aFF8yx35oG(Nx90hY`MF-dBw8!Wk+A^uf=YzF7QdgQlYwql9Th=P4w2-)p zK2wfWBYtjVlM|CC$daquTd^)g9PQ$d^RT6p6Edr-dj+V$?3l>sbs4_cJk}-Q!g}$s z+!=_UnZI(W+7*GbPF~C(GGeY+`lAJzPF(WUJstQBvne%xm1#2gHWEVXhA4v-(^`hO zsrsrEY?Y}cpqEC9gDD`^CZbwsJBL8ctyT{tOkEim5ww=6!JD9!YPV&8 zEpA`h=G3{1v6TVzb6VX@u6LvlOaDkPuaQ|R%kzb9Ug^kWHjS!6Z^T-(E0soqGqa@2 z7Ax>|G#>I&#aw=4pgxenZn3`124G-TS8@>TC}6Yz3=*(DyJ)13DUs5c#jh@kz+D(S zAA)z9RJq>lxXb(c3Lf79fa`pwA72mGEI}+R)}^Pxvw|~xYk;a^7NZPVCK^>>&aqXf z8=sU%dzIjB)1@h?thO${9+Y6gU7WanOsVl<^L+`R+E)){;-p8u+D8?rb0L{NFRx3H zc6s@HHb#d44mz%U{~6(;ko zuC%IIK{EuLJBvUI=)rVXv6OG6i9#cyN05#$08dOagxF2*2%{>R4Aqs5C^1FIJ|23BN*t{2&Bb(0g;GeBUs5Um|_J zPHe@PfSH(;uxs1OE8~sA-fuAoq>R+Z#O{bn3Xc*(5=9iEG@S|FD4z&n=^T|9q$MB= zaEQlXx@y2tR$X8F=(1I%CM%??Z8=cgY&eEWzatG zoH|}E!T~E0*NPB|`u)f*i#se`SmE!sXyg@$fyIN#{wRA5pe|z&61xtUfFi#SiY!X= zRml<>tcq;b>Qw@h+A&5{RBgRe6xvye;&KH!K)+WcIZ1{K@u<5}O;lPz;;z(2Il&5= zsG4mCR!ZiihE^FV}*b5XyJsq=)d&I7!hayV%9bO1E zS&WDLEN@by$Ow3QbC`)yfr$`b|3V9viIu`X!ic2UD~AzT!LD9uGj=TnSa4BsFleN4 zrAm=))(LPo=+`4q;#pK}3(0dGGmEa^qIi#nsK-BHS z)A|_A*3rvJY~ndNYI4DvUV+fehJ$yCfnm35*NQJ^=BVqEjRs_dnHoN!t)%LYxJ)fk zfNNPS4=n0!7U`%GQv=2Q996o{EG~8@0!E>TXGMmX+h4&;0bdiTHbn2bMocaxYeRztch90S*EIj^))df6sLz-1fSl2oW9Jg<fvi3~edJ!&UyHQ;xy{yGJ~l%fy8HvgOO%mgUP9FJIAt zIdivbbtmS|yBBxSnoNzD)T*t)ss@f)tZp>mS6@G`o}XrkQ=>RFh*SMMo>xChoMd97 zxNBhO3rRO!T7IdiUDLQ0&Dco|;iP&>s-a0iA$b@JYiyV`ch16o_?BN#%2q;1L^9ag7dS>Tc=o{^!BW7tE z6bM{5nnPJDV5J^LcMyRtr7b%u2G4TlobZ~$?IKW%_c^pU1I5uSM+H#;Frq~|AF@sn zApLs<%TJI%WMrn=#WefK;Go4j*aET6=Lzl+T^JQqjH-}PAt&KzltY81I1~!b{T6IV zYhF}ZLx+Y2$jUFnss;00l{8tf!Ykkvt(A`a(z41uc z>(niJ9z_>_@ws`guf=A;URQlUt}G@&MnFMc3Co&;ius;}7ur^iPfDrYQBX#om{ zBV_2?7Ec35lq)WrjL3NpU<+048r_m9yo}&$V^kQnt>JX*rgMa{FA;-s0VlI zrV1$SF=0@LXqAzgBelZJ0@sb2rS7y`2nVxI5aLoPrC^lGlkhFag$_sJY_qLWobC1u z*;2%8%1uPj5EG(v8lEo3!;jsFHKhZUlAxI*%#oSzhDf_j4tX_+W@x$*@RqhNU%7~H z-I35jAR5YnAe72NX)FUJ)U1IynM!~z1F48yqp@0MME2-~rO>n;PIHXdo*QMPP@@XP z0Fkl1AL7U9i2*WLg`1=rfec^-B4w>=-K^C z=nfw|L?{lL<0$xLjZjlm(+KSq74v zG@(1;tO$TYT7C>#=kG<5k-WO6&R4C(23Q2wEM`BAm?Plfxs}+A^#?9w@brKXA1W9e z0s2%;@x#>000KTJS~@ckjfWP%Px&m>7@vI(w4za|L+wb3m#7i&gL-w)mx*wPJ_f!Z zV+xr3;HqH|8v^CSKuHOh%4jS?H6%zoJ)%v!K!~jp3~x3}nw1;@+{%xJE!P)1ydl~r zDV5f3rq><~dDyh7QsiF6N_PC=rAvIvB%EsXn+r`&YrVuGj8yMxA(APZFzu0=eMe;l ze0>!xq4>eQu-r*m@VP`tF7NY`1N!WCHM0;kjnkW>T=OiJ`d0Z2waMBK&uzF>Aj zl|@NyO#%+W-e_=P0p@^D8stqsjATM={OD}=Q)LOIRqVtagP12l3&JmGdX zn9hu1ZHLO?G3f?%(FhF@b?L8q2mu`rJs_6L%VMowHaC>X@)Qd*xd3CRvmbCPv*N9n zDqwncKcxKXyR-(P^-5?sV6=RzJjdyuEho;RDmkETGIU16tvu+k95N&O8(|%iec>gV`XR$tj-6$HKazH%Flr5@-X1VK*&^Ok}Fh?P#XU+KrHIu3Hq?a-}M!YkY zPlDC?acf9waa2SHAle5bb%{*;>CitEa4KXoULMg=UFvSI#^IGMiZ6U@l1y7aK1FPP^EeV<$9( zTdH5{?}(G(0Jfpg8+u*cBN+6!UUoE-$DRZiPl>x(JZ>kha6xZ+W;IRq^ADO;F9z}0 zKvajP@`c*E^iaE}KJ3Yjfl;=CQ-cj=n3|(o35=(pL^@YMN;W6&{pkcJ1$X!?12s0PkZcx+L3Sk7bH3x_d za)W7lRlpc`D}lqXt2p#?ozK1W+|0y$;vFv5B&0TNlQQleuKb7EvH3I73w&nA= ze1}&kB>QQ`Oe3Wb3Mov^CPKcpjd^HK&vz)D#|2iVf-5|2a%DNL z1O^m;kS|G4xXVdD6C+c9%=A)4P10?AObGFi%ha(5i7y+;OXXfnzZb=Xc;pPvBe)xM zqako6it%tmrAm+)p-;k?_=9-@l1!lI?6s+xSwWlArA#TQEfz)+IKcQje}h(!bMaLHc}Bx_%edhNQZEaXn%dGfXEW#&gIyU@2+U;Ub$i!D=zU; zspPt)NxW974$rV~x0mg8#Wyl&JF`hI%@%QWno?s`oaR`!*ht|G4h}WWVtotnF`B+y zPXS-Rz~^W-4i|Ghc;nPSPhlYGG&!IN&l(nc@Oklc1|Jdedg>ddJ9&%<(oSQ~I*yt= z;o%n#-?kBHp_>`mR4+SrB#TEk($j}>>5)P2KsIxEqcd}P#+iAIb!W;B3g@h*;aGUmTM^p3bXAGx%k-ECcnMn`*^yi$J zsQYmM?(TGEV!V;dzfCN*L@BWbK-NgpSH z4GSV=G*KKPTq%g9E0P?YBhaH{@*RM zMJU%9X*wx{RO>*cw8OA06|mBV8YmS8Q zlRn@g*ov;8Gs3}_$$jEr>3cj@yIJF}vG%N0*Gm=$hx+{Q7?!GDNUtmO;4#~DZhfI= zbk2hv_;%v#`nh-#vf6%GQijJ_ ztFB6QHm^L5&^k3dG6N5}OvP6HkKo>xzc80Z{H1D&oTn=#O7kprC;YoN124s^sAtVf)> zX`r*J#kiU3OmD|G4Hoot%8!83^E8DX{aE@c@e$B2{SnYrEy@bUR@pYdvM{<@mlS$d z8&=rtTj5yhN>lW!wybPzHt1;<+*sYdtabV7F3pD-%i6ko7Dreq0LeO4aZuap7y-ay z`FB)PBk82VzlUxx%4k`c=MOhZofkv-y5Zrr4K$MMrgt;Ttd^A{BlNaWGpn7pbqZ^d zHC2=$N@Fe2dSaM5T)xnTcJUqA;ekx5fQLBv)>EernDDCtaxq1Kw@f$#SWcj~P1s$~ zG$&l0H$@quCT?q^8c}WNB}T%jkFTFksfh$@cW(qW?Tuzq9u{7~jaF}zo=LF0NZl~) zvDGhy#M?ip`tU}`E z#W+Fwnm8z{w-FRhk18mrQ+FYa5%oY%xMmpjmX+%DlaN4Pgh)NH#-!!WG^h>(v%ag`V|HYrM7iBJ2KQO-^3M!Bx(e8N5}>nCHSLD!8$xQFieXpYpRmPgkY0q zK`4^aIy;#pEttb8S2jX#Y~kuBU*<$&s~g5DS_}4eWW7O}d$h}9bHNCok>Eu`VDTy% zyuroA>>s8whx^EPfeAX%uO37vorZHNsETlU1V4?1IgWsl8wEj5h-%Ln7q+mjtDV!yi z8g*uhDSG9z)tmroP7yZD7SoSr-5@v3Jg1HxP>>I@SQ*OrtVCOoHemZscP55HSR&*7 z33p0n6T;s7f&@m~K&QL4Yw-(EH?O(c)N1inbYpmJYbu|?)H`WefLVMtR!L2C-5f8p zCbm!cL5LDusEI*Z+$nm|Y9A>fG%CncEHFu^k^ny0Y!XmX(T1C=#}+LRe!L$@B~$oT z2b$>1&|0fZrC!}EAk9d4w2Z7vuWE6V>O0?#xB?8{nW^4I7vgYy(5lzu8Rpdz3xtgiA2~>V(^h;v5??`av zDHawlAData7_j-=%?M3OR)jPauMjc`k#-2gz>PqX3?Qa-xLq}M%niMxt)r#$=%!Yf zXiIK*Bj6Y+uQK6RM6Y86jiF+VrC>C+M!^uF2ejr$OcX%DgtI8*Q^NdBiX5aygv;b_ z`BqgRt^yftzoqz1mSB*h8V52FjU!bwE~u74mRMYlZ_X?nL?zI;HZ9O8O)FNU+$K%t z1ZdH$unXCD76vA&z<|pm*uGh&BsuXqM0zT~7|JJg!*=4~29$db=9p9wjOx^&bgS_s zlRp&dIzVb*BH%$yifJ4Px5wDwTSt&L*I0HD#(2VzL zb+obp%}~*z@4Hcs$CyH~MA=|-T!0a3Wui#r^424uiP(n%9H&kUjg8Q#R7EN#5F}wr z((1%nQYX~0FIj9E>E++TQ8fszX)UUc6csWw~(D%=G7`+QjiAuI8yz>P*mJZ5WI2~rXB(-1jT zq_s4p6tQ-R1Y(`awey5qQsr!fwe{fZW}_BByP!Y76{zUwG$Moz7LfrgkHR=j*dQ|u z67ZL|i1KtTm6aDGhf+6`JPv~uahb{TaSOlUxDa1?QA6=57jJY1P?5+>$YQ*vnEa$m zyr~(FCDLpi&oB$6>3w|pRUUS`(jyGU&JzqP8^LDLp^0H}Fuy@PC}H%I7%TB8X^6i` zfgaTX5WyrG^Gkpc6?q5C_&G)7UOrrcET<2T(DY(@!Kz9bn1{s6fkVl{ zOhqzin$PC4IST6(CMZVmIYCSXS@pCWtYkg33nkm5Hj#}Ol%9a`6`EvBx-?}?(^`c= zq$RK>k_l@B_rZMd9;^rFAs~_uYnaa(@S4}T3@?Fgn9U$K$PHqH)Fiuuvso%VG>7A7 z81pz5ZDJa>kEY+PbaEC(s!u8{K@gqXys%g|$A2zJD}mAgZq8b%{g55IhJ1Kx8rV!T z<}`Fc6+@Z@xxC2r>0-*0>J;VnV5fMmHa(!lrt`Ni^H(YJt65|rSlC~{7iFv!8aI!J z^RPic><6Gai-Xv%QF}n%LlG~JPm>ni zRI*S!M0Kk8aypvi8~F}66c171$tH9*^>{HjUf7K%xY&@;GT%Wd<-kK%Yo$S8iB?!f zqo0Hes?jVKTKF}+wVu*=xaRR!O>v<33OUvg=n}7@NeypYlA+I_;kHFg5sR5)o=Q73 z;PmFRrg^LhFH@e)njqaKcD!@^fj#vHo;b~^5B)!0CLzQ zQ!7oFOPvTQTIzT1AiosZa7H_oY*FiBNkyb@Ow3^I~a4w`9%5%BN;S3c)k_aD! zB}u!h<9@XhK?oH%B6dlbA}SP6wGeVg77vDuX5g(J(Wh9J8dUql$TkEXY)7^v zmd->t7mbUtoJ|F~_(C>z8j5}h%&!-*UmPteycCp&Sz}{LF?KW_0@FI#k*KIrJfVxg z9`)337@p~*kQvQTL-B$!NH zgIrycoJ0v}%iIA-R4iDck}o%1LvdX8VOZt%@_4U2R$J>t=w=DGuyLr@c&0a^lHY_> z4awd*DKvp`xSnjwo;lN_^Kb=jqFnw_d73d1Tp({tJc_C9Q=pk-CPe^LJQP}MdqlB) zL2^NEUXXXga`n?xuTmr{EJQQvE=E6W3n+ExzkH9-g7zbzu3QWXi!tCNU8#(np}E|Z zsmT%QXGNA$K!~H2Pn>j`^9@wapEM#~m}P|Zm??6WB3MvbbsXI(smy9tTQ?{(EXfl} zY8Qs$iz#Di?{<=T=SxKNh!Qvr)Igt(nw@nNIxC!%Bm}Od1dfsx97(*e2hkYT9()T? z0L}a{fR{?27Fw?jRC+FIQ5IP*SMKHQfV|a&n!NJK21ycBY2@t6_FXhzG-RwlDI^QZ zdZ|uD4Oh;;3Xm$zjA*U1!P<9`lYd29)A?WL2qiqPOCqfL)B+ z9MW~F@GjjsxmIVltZc_-wtc5T(V$njtbmbQ4N^nhInnH9Gg9B>NaJhOnlA0nNRee| z!OgZ&O0mg_=a(j+Is+p371^83W&6%Xl41!Zw?H`_BW$52D+XIii*0=EyXvTn=70^+ zz6-;zlB9}@mupwyz8U?L8Ut3OYo#O<=8}!Ax+_hiG&He_sBXmip#U(<9gw)@8>wkq zkkV@eEK$w)qj*tSf)y)=hHR2Ftw*j#o1Sn}k~d=B!S@Ax6A@e%#gRr89W((QuKY3AOtniUiGS_MN6yk92{G@U=~?oyq(V#=*3RrPU~q>Bot&B?MzS zDYWO4o(@OYOT*o%iJ3M&1xjBn6uYU3N4kQH4k=L-3@BoGHkws!*q}}zCb1Fn(QWfc z+GyB+4_$X?22w1m@potOhQcPlG%kZv zXL1IVCMi_=V&-L*`ryL`b+J)hY*H8NA!AWmfBnI}SD%e&iY=*1&;!rWbxHq44yF|8 z*+=@#($jvJ@Xpa5nl^oBS(k z+ixKD!XTVDITmC!Y6PK@mPjJyTheeFwRFV$aET8M;zUUi8%tEKv@b`2p04B7@QlNq@lXONV+xnNVf(X z>DJ;R5)3Amx#QCvvp^0nXZ-<#aNk7pA4G3N9{)5ZNI_Nu6$2rBt-&CL-pvn>f4wQ zG+H1*$zaB2exRJ*=7a~lw*=GyY;WQ-27)Oxv#U0HP~>W)2I4Gj z^On+YmNMTTF{Q@16I10_bH{TIV&q{zHQ%t7t;AQGhp{UFOU-y39it$;zc`1DZ}iNu z)3$uE)5o7n@7?GuZfkDo#C9aw6NW8I_yG1u7AyBjEc&FKU9=%5cFB)H#Llbrmy-CB*LA5D6vWkvl_fN2y zdogzR1G@OpKVqzBlD?;_v;HXV_xdwqH=oH^`Ug z*kia>N9GB}UjMJU)92*Lj9qpTV?VE9?1#5AcGFvca|>f1`a5IEM#f&fl(DDx1YR#Q z_T$|W?9~)w3!Y}|62LoccgBA48e<19WbA-B2{r|F9xpI<=mOF1qpvbncMoHGejveq z`fuH4@apIviksj+X?l5aRXx)Phsrse=)Yl22t)G>vVbZ zs&g+*JEa$HWo-S=MTw8%{PcDBO-rydB#*woy(qtXFJq%SB-jf>jD5K-!TK5!>>u5z zyQ9e8&?#Wt`&V(MzY8DI*U7UOoAXm~zwLU)iu*D4nY$QE{f@DpO;4~L=P`Eey^LKq zGr?Z&VC>{;A*(&WSIFR=wUE&>qV9FTZ+R1AzdH(Y2JJ_^%b4>5W4-TZ?7M3i+XZ|v zkMDsGC!@Xh4?>Q>Sse#MFOF$ecZ}WlCB|+Aj#oh6%;TE}nRkq>{S;$=e1);&?-uFx zxZeH!30AXrg1x_iu_^l`*loj%{RKFx;||dMsa*uzQ(jZ4pb7NO-&-TgZK%8M9V_aDZ7eL#W@*Cg0C z4ok4ZAnyU_^s6kv7Q+Uv1PyNo-4~ zU?&}zV7)sqmaR>&13wF1ek8$`9h6`nX-cqtK>N*81dNX!!`P|YGj?N|v8`Z3^%uYv zM;N>Qe#TlM>wUf{%Ducu)7D|g;}zK9`ysc70B^}%j7Ft6yboC2aPS$20cM2FAYh z24i;}3>+V3tm75f+8vBN2Y;~qMc6v(-3(fOkM{Zj|NnsFGhcx%LKi;<9e--i1e+-B zp4$5%$_0H*0(O;wJvcTTs(SP}?ONr|a@DaaX{W+p1YlD7gLl$jaa@KnbWFgx|7O@K=={d~1iK#X((#b=OSgdk&qF6Kguk2s z+Ao}(U|&GFTV$V^f!r=`1urj!EyHI%1^@gl@btf5V5}KD{|D0PSPgn#`x9d`p$C(o zmuuix&P6m`%@YFDd^v!9&y1F zgoS;aJSD-VJ_SGcF_HdtDW6kMLi^u={11n3e;&I2d&d3%y?q0E@)~^1C!llYal!YX zXAL@KDQG+#^87jak-vMko&IgIQ+wG_=2Yq+uV*mLF(`Cc4KU+ zLoI}ld<$bUKm#2sVG9@h9D0a;W~U3_&*0;qY-jA#z^fU*7aiT^`IZVy!CwO^hJ($=BO%z5Z{&^NiObXx~M4?KY95gQRd{F$+bPe#o1 zC&&_h;#~OHq0`YHz%Jf6#Fp)?-_tPY>eSVYeFgsJkMAP>15OtJpI^ZSx*&r;z`p1> z9dKXIpu*Y}cMg2cJ&0*|V_FwjE-t zA2PP+Da4hF&}Z%gyURc)VB^<&jQ#dy=+*1+p}o+5@c(OXirSZ<|LT8UT>rIBoWBWv zJ@_}^idgIh*yfi|XCRAl1@z)1@a@;Yk&d^}-tVu1Ea79fy;-EGzo%my^zWV39pngk zdeE)=--i65<3GfCo?(Yn@`1hKi$2HL8FxXyaGi(lt^#d;0}gb&{bR`QeZXZu_>HeH z)(Kf21$!@byoI*tI8Vj~ufX2d$o`qq5BUT7ZnSv;XuqwIQV@sDV(c3W#5Mih0Dde4 z--E}+pyAlxiqt9aLs7>j#0@_MFIoZfJJ5kQA&0%d+ee#3>1UB<9_PR(tbvcD<9g`J z^PpKBdxL*xZVSF$4*B7yj;3AJ6>RPujA_BYN1=ne!9J!MHqtBg@&=Uo8h*PS2Yy4w zUxXaiKt8wL19@UBd(j63yuHy+uY`Wog2sC%G1hkq{O=y{t1kLy=s*YLb0_re*B4hoxTJ--XZ-ucA`qwLWgDzZ&c=|KY zhX)y(Jzvz@{+}2RNt&nki?q-E3iAvHiR*iR$=DY#wxZ)JKND#OAP$`J3dS0U&$56W zbsV!8aTsF3MR}e330w|70=t2|9t~L6UC-Fwu-Wf!qstt)9p*9uc1*bkVVrRwe8-1p zVVnV9yfgS~9xXqIF4tiU0N4{yW*+Q|j;)?%tQK+!9-sL-<{uK!rLQu!_%*~9(2Yya zM_&gyJ`Mhz3)y`K^8UHB7pmXZp`q+|tiGnJ^Ugz`i{IPQ7u2JVrQ_;5K<7s>ZvkCg zdJ|(mS%)|R{C^v~zQc$G?r8&j_;q#c3!E;4AE1;c@51;P{N4t*w0f}pQOFp5yLn{S zLeAj9H0bnO;LSd;m31FOfAkMs>WO<9dt!fm|8-nnbTwl`Q!zhIZ2&LQe+J*7Z?}NPrN4xIY!4ekU$aLG>Q8|VzXaYw{&PV8?&weMfUmj=xUBmc z+Q4rkt}ll#-2?nPW-@pMuKnN!#wLRgFQfhIA?Iz7_v0U8e1sUY9&vi&dyM@OGJX^= zPr&`__?>Yu>>PYN4s!q4iSSbk6Kpus=R3XbS#axkE4o@yOpr z+R5+%kAsfc;NM+T2JrGC^b+PdXf#I4t0ERUZ$PJj-sOJn@EwMhBJ(dfG%yUT!24t>b6 z(CKpvkjak`i>!ej9?96BUWblPh3|h6_6|FH2K>ET(z9tD zKb3LGThODwVch%;*hA-1k-x_|;ynLZ%&S6Ax(`n;bg*62-QMVO)XV6|mR6aenPi_(Sy5jc*_Z!M!@} zI$vEORtIn2MSD%)^`9qUehd1rA8h&x$gB^x%+U7V&x9_(KNqBa>)Q6!Z~p?_I|lx_ ziLqI;5H~>gdtL!QVPE^feow$P9XIF1`9qN3!_fVE(9g|Aj636EkxqXvhs-YeoxYE{ z`aNtKA9!GQ;9P^>Q~1$w#VW+M;Qw9c!Y@N-E<6n}KWJ0OLhyAB`oCj-iaBFkUo#zj z7ic^W_jLRObdP|Rqi+!R^!E#OfOU(wpN&|l_A#CJs%)21E<@ja>1@oUc<8%eGtd1R z^j-wsLT)d>PVW!+_%b+7I(6y7L?GUU3po<3}ow{Ie9j|?3k_I1!C&13J-hII z;1y)>r1U*k$o8q$b;zURP{?{Ve8DqPCL&e;&BE?)ohk0$o-WRpqdhv#d>H!qS;VEV z>k-s{9JuwuPVRtw%%c-Lz8QA0J^Ir6r$o-L{$rL9*IPl4x4IB>43N|7(Bl^jo1hZY z*NJj#FkkX%*cu(rLJ!V>-hL6kEczH1^n4xiya;8~@%ml3hhET81TWQb32@&EK7Lvn z@dj+;9q|0Q??Q*rUzo?Qz{?jGnkk?ic$&utq(6M~a~L1MUY?Y;P!B(KKlnq(S+Lu; zix|T%f*zj&J>3L3KnKr+K0fnz$b1HLW+z_M`tvsIpN{G9>ruykeTXH#FLLI=7cV*3 zF7@aA7<=SFv~>;UgTSMkVehx#{87N&et*~?Y~_96_nU_c_*X*5{|)~B1NQUg#h5#R z?$VK+FV6X&AP&4wT$_LY4%xS?MGOPE-MbonCv@`0H!)UUDoTA~d+6IAagQ=z{RR4i zZy^SQjbF6{GJxMX8NXK!)extisITd2r{l%_uYPZ*_ro`iqMg5BKH)&<*xJuP_wh@= z4&3v0Su7E=4&eS_@arJZ{#DdnxjouO%y$&@^dl#T@-@)G`=Lt>u#+}QN4ZCjz?c=Z z%rpAI{XdGn7y5EAbmWRJGPdLCqW*J_>vsQdBIYUk#r?njiRnJ*#|~(J(OZ~%Zn0&$ ze+p}o4`2==E6V3U>%-E|oQ?RBjvEmxR&snF{Zer@`mjR~3%!FmBFJMa&~nEIF}}PG z{_T{~P?kK0^OI??8rj|$gWKMS&v^PE?M{-#f24xUz2zPe7x3%b^G24cG}j^#qS|x_$zS5SogvM5z~A~lv{$>#yoCE-s8~ey};j7 zVgFyiZy9{i!!#@lWX2G_?7Y>@TQ(0r3tU=ff|42eHaMXz%p5!S|D)FPMuu z0sYNFDVHCipPu~^`gin4Z-9T-zJ^#2Y4_r{9b`IrZ}dCB>xEb03(@bq34U$;Tf_!G z#oXFSh^@}T9OkDGH-hG0!M+Yh`g0gpspI1qE58Zb@5Q+fb)QAs3!pnULgpWUyk)hZS3o~&nK+*YKer$FN=F@F zYz1BT;D^z#K@S@~jd{S&i@d168yYZ2AbIhha}WoE&I50P&F&{k58s1+7C4&6PRRd4 zA8g`5tT#%3Kcfw?;PzO%fo%5N9y$%$?gOs-JWM=HuzewmcVPdEF4K^XT!;FHAx_;( z!@pL>3+-P3Pv8%3m;q*-?gOr{%b))Xbxwf)z8UK(kkhB(qk19t=Yi|DU`I3HkJ6CoP6wgwkHasF zpq``god-W|9*^FEe&tk=a>&_c4&($J8qtnA_LFj?`y0TMTOgZXz*hg1KwNq>VtDwA zYmjy^c=FCB_=oE;t^to9fX)YxhTowNgpPd^Why;7U}Hm&%gHk^?}zKb&%q918=trc zF|7-^k0Pe6MGPhV0Kwff3>qNA|AMd0f730!1{?a<1+W!^AA|dd{DtV>E`c6A0o=Zi zv@ib$F&Fx*np@$YAcrB){QUnR?wW)7OTO28I%NDT__+~uJ@X`V;0D+*=-uJd z=<}9iT^Qy50DtkJBjKZA%b$g9Es!=qt?u_t=mzY9jyqoEC-!OR@buTkHT}(DJn{tm zPTcV#X!|1k_&o6Us~Pl3|3>?Pc$Mm&`E$q`xLyH$zYG4wJaWf^Ppd`Bi3f|kk3WKW zs|PUmI0btiAP05SgO2ZDe6$<%ee@QQIvIXN9bZBEzo6si9e_CHXYg6z(K7t5gT4F= z%6khmd<(eHu`B%2;5MLrM~pRJ z!QA4@(4~_RpF=nL_s38aX;a{5Z-Xzt9{RKpzHuk$-|N6<@u7BG`~3j;Zk1qNw?S{N z!S#ir4f=Zm^uav74L-kn75aDR2pxNWT<7L7A9Uq^#r@UbIUV@u7(ZS<1^T)dVu9N+ zMmrw!{dYk=O#;UCjpE$7yC_S4-+c>vX_ld1cp77espzL5OFDkL2lxfL=-9W&Pi!yn z&pdkHL`)CD{I+ZqtpN*&)D`b)>8JF&)jFXnI{tN+wvPH2IKy8^VW zdKhA>5@VlSDC$Gc#eIoUU z$*_aZs1o*hP8Z@M=*PDqLv?)rFm+|0&yh6T44(C(Um1ce9uMC)@j;BG|AGGZX7pL` zQG>9vQ$B%p3iP|zz-C{Co-Ba>d=&b-&!05RvyXs(hOAdXFPB1p&uWH#gT~*%wtxP8 z%*1R)4)_;np<_DYfH&Z$zKTBRB`Fh1-wiyc<7%|?b@1db zu%p-Ch5!7KNPm7KVv|!bZdxGj7lMCngW_8Ky$O999rwb{|F$jc6F%S#Tz?n5+4FmV zDaR^@+zYwAivH$t^g-wces+?mw+V9D73&G_cM$VHpKg~v;CATa(a_;v;JkK#vG3vh ze(28Su%UU7|5?z-H(;Abg2!LF2EG?EXh+;|8|X+wH)_!CC7|0o6m2a^u#O3kO%Ze3 z*Mm2P?b#3MV*V4bp#;_^5m$W)a;<}2o_RQYXBYb5hY(BehWQNe`lSy*CoinLrKOj|m;D!b z_uOmfm-}>?-LHe~L-*A2A;{&&@Y8ExPd(7b)BcP&a(C?GK%D&x_`H{1fc`>H7Q*h; z@oCu73(z|{zGm3ff8fjMc;GXA@Ad!MFZ}1fiVc?!LMA5 zIk8PxH%0uiql{6g-u7tgzn9Wg`1mX0oI?=T{Js--AWm{Y&kQu||wpD`w&<0qd0FC2{F5x1^C26IHH`v%5p7aT6~e=Yst)__;) zn1nd}fr(hlL!3^>?YRFi;_E%-Ji`KvU(Dk)#K(7i4C7|Wr%!@@IwswRd7bkhlkZ|K z_%j&$3}PPQOpHM;#@GY+kDiXXthWGHj!UOu+;yaEbFK@zAg9y7gJ%#6pLZ?dT*QfY zK7#qUe**4h7~3LF|8ZdML$r)}UyNt|3fOC*AI~91J^3KW9{Lb>)I;_sAlAR&EKy<} z;%Yk1za6pW0pfaB#O063F~I&ZP2@nfOT^WY;FCIDnTvgX;K`d)A-}&ecH%UZgLS!G zFm_2}z6`XTh3mx_gT1n=&i|?8aW`V#*B6QO?Pp>P4*jF!Oz>MB7h^8YJhuO#c_*$P z2LI@I7xP`_aRkbCfLH4H`;Rbpx-Z7b+liDfeHd|fBkb!<*w3>fZ7uM*3HEX}=0J9l zdZ_Bt`K0YJ*2b82B5Y%8$nPw~{Czn8ei`O*a6JQK0|)v^$CIPtJYtNkuHF-Kn3yB^ zJoN5Y80Q`K8;mC~p1R~um;-ta>vwYONj078VS|rCH(mfvI|*3k-}4?c@5J>bPlJEJ zd)Xf_hp`H_iMoA7k@uvBan%nnj>SCT!~X}pgU$RHW$#-FJ;mJhW6*~@#y#bZc`@VQ z7TB;lrom3%fS*vuov@qT|AP6{yH$!f9}gXw3SC+Qe>3~*kPU1@9q$0YMHhkoe~FaK zQU6BRygL2~IsFblI<}H?m9sFGIt})-EAD>>`%uTzhrwOjmAm=#FgG~&d2%9(r zeKTYnJf_Zud?05!Zp54;9d8d~9&e$hizim_XK5eg34Oc(I2;Qd%7gZqSPS_Ka6f$y z%!T9kIQUM-Anahm7sPe&?@iG8z2Qf1gAaHLGHAOYSOV)<(ii>mC6RX}X#7|`=7rwC z?;qIrumbdAZjg><_(nQ@a~pgbXdRJr4kA_jz3Uor{(3M+Z_c)(}Ii*h>hdBb|-HqQZ7t;Q7>`THN!rkD-gGf6S zy8IZF#0@nVmlP0dt~eEAa;&Q)5tm(x82I?VA%;E#`%kin%MhdN@FT<o+o@9m1U;cuYc*Act!jkt6zenW3#E#wU30jI5i$M9FMH|ABu z@)I%V@lT9Z+u`TIgQwB{nP~T-d6);l9+5Q^gMrS!0M{cvkNx$?e;4DN&;K9bJr5iP zFvl?&bUuP{26*!*=)CxoXanP&^BbWTAH=>TX`XM>{Xj6a(-}%UE&u%h72M5D>1%24%c)%i}A}LyCKd!8|!JfK5hoaD&YTT@Vf** zI-dVFbYvFRP%#$$7RIt40sfaG?lrF#4oMoPja0k>AWkk|3v2=pB@u!b;Y9)`Ybc<<2)N+3|=vIsUzj>bUo-(_m;_O zQL-FDZ|ASc;Rk(8gw7So!}W=S$k&Chsfw z9)5-lGXHHk&saMzZH)(?-e|t1n-j?Uq!#Ktle}5yH+7G8#=&&bzI~5&&4J9%mHytj z$GdS^xR9JChtbYQ4{7BbYHzvuVteO2Xc`_$$W#9RbIP9>pgiSIaKgorJmvq_l>dUD z{KB|u$EP$}GPfz)5xeyLD~t6j->%ubo_8F6$J=E;?OMb|p73ebEK$b})uZoDre}8P z6>a;(V80BX5!-d!rzU&yq87d_7C$aMJ=5p+I(Lb0ru7>bD)srysG3gc+^uEz>U-IM zG>)H>tFHbJyUpv-xnsw)9*Hjv=JYA*!@tu|_}t*X-iVJN$ckF?XVcfbFsrn>&y zP4!Gm&*YdfnQz=A{?%_wruW*7F@LqLj(u}9(^`2=7N?DTcosT{YuvnL+cv3ZHf>SANowoH7dA{iGuEPQo8RO)BB^9jwe?%JO|JRh zVNj(0QD@uO&?qOzXZGvjTV%0b{j%e!s6c8qAE8Ym{^WebE!8)`aWskOvv(yrw_-bj zBl*9jA*sz;*H1mKVXIiL;LS2fSZeDw4H~!j%N6IUOKSaAZ5lUg8B1-@v?)jHiBDn1 zd*SI>m5XBmvR67Lapnk_wLrYZxEdLClYt@S-Ojx(l*@T<`j&M zi4h0tldM(omXm5L?i@1_ZaO0tD|efFM2CBq&2@t}oj+bx%DK^fi4vYkZ(}ANx2j8$HA`+*dnSKh^zr*Eb&hA-QC3y5i?i z`kJq!_^orJ_;lyUA2>(7biC(}VfRqLd1YPaaUoQ5eqYu3Sp6#QTPpityD0zZm)t9L zZDaI6&xJQe&n-^ToV%5A4Z59kSDeH^kEhHzf6zz+)>{1ugXnvjbOZU z*9t9@kJvTcd#LCA^?T*2UGv+zUXmQvUX{F+n|^skZo08+1Et1B+v~pfZcZ7z-{ih% zarf=rTMOa7t?uJB_dJ4fs&k zcrTIT`p2EQ>9z55`neB9d5=yo!=0skZ@kw21lNGd)Y7JYF8U}cQ^&sK&>G)ExaKq0 zytYN@clL3uTJIKVx{&3O4?bi~r*ToXFTH9B&4(QNIJ%L;{E-ze){SM^NB z?<=10+%y_$I6e`6uI{;$qg-!Dy6%zK7vj>R^w-#jkohS&oJOAXv2l0LX=wWu+pptW zcn12tS!AJA=GmKKAlt4^9<_SNQXk`f`1#y+0{H_b08a-$p_Gpo@+s02$xNX;-&9K zapM0w`u67AXeQgE>Ue)gy?u;BGxBqRx}Ly;>xUZiFYyZR&r#20{knDt-9>w$x}G*aSv+Nk_pf%i|NpJ`-7bvw%^MxXM>MD7 zr$*`ipJ)?(9pO=JzhJCR>PLnSa2@+~ye>f=9*@fY(#CZ|$30AEs#bPASHCBp=z6Ak zjt@seeeXQt-|fb+kbSd?qhGm8-e%AA`y(Iiv&W&#h7U zuhvI#Nyn7m8l^wDJc?V;!7mPGn=6pnzOL&Y>wWrKbfFA-Pjk(_5xHYu(r$N+dmx=? zz*ao2ZguTM{}JlWGM@QAGM+8SzW7SxwE8>mg3gHA2>(sc6T-{Gyi@cPeOD&D^EDhD z-LmoGIy%_IHER1W+d=2(<^uFg#FG#nzdDK&|Cg#GgzK+BH@beP$X-3~om+kWtSXrv z;eDQM)*jy|L|#tXmkl&V50LRe_0YecXTr`PkJ@;Wyf?3AZfR#df&TgDdtdZQx{HqK z-|FKJ=A}2(@t(HEd7q>i89`?V&(o8aLmTl>*m2`GL>n#faG7y@_x;3Pb`;$c@c6Ke zQQlCrwY}H)c4JG(`Izcae&KzhxVNzhp=9$Yz73rxwsrr2EHru5Slr{el>*Ve@W1tX z&(5T}j{Y(^A50cf$jxMWP&z78PumOh8W%OVzIk2zZSJn*1r-XeMvFFd-gkJnD`lB`+e_i=N*>6L5r>-h~ zgz%!aUm`c>??!wbLEy9KuE(t_^M1FXXwei;PKG>+S*4c+d_Vz47w^@xL~Ceta6={P=Sp6wn9`RcNsF50)efwqiM z2tDJvcHTPm4kxQyyu;Ga+Ta8FxwrS9cDSE-E!oiT+wP2x**nAh`T!kjVr;cv0?m&d zO^4C9-+11}&*PrpLAfSkOk-Gi@ZTQ0M18Eb4WckGd$N>2{A|E~)?|p~i@igm`6Qde; z4vykOt|n{BU89cI(f!4KSABhU72=4O__8spR-Y_?cWBb8S@O>B3Q54UNfRX*Iq*#%Z%0 zy}yQ@>toiV`hlO59rNUZsPz4h+ipd+OR*D!*!UrA$4k~1^4})gAv{aw_le6=*uK%Y zuGhDUc=!rhLa2z(#QEt&pTLYq;1?v-g*u{(}z0Gm$ z`sm|1=6y1=*Y)zrLS<%gFV6)>Ahd zKlLmgZEW>91??Nva|*t8uWg;Rzd5izJ;nETH@Y8+mtWA`<~y}h0snpoRFK7`)+l&= zXjZfx{zt^e|Ji+v(>k&`Mn6)Gp?(#hFYQ-YM~uoXA5sr$d+rc+?@aatPa1YHX5y#t zF>@?-lZWFSx1H=)CWlvCrJskG?<@KN(uyU4QVjS0#Nqj0~Z#nzlQUxrW+nQk|WobHxhLC%ox?pLqn2Q;xw~JWPbs zuchnS=<;rqGs-bL53)x4BHFH9j-A|OeAIJRQ?&MX-k3O8JQ#m3oy(@at)FYH0gp@C z8BeAjxNBZ%G7&|{c`kNl+D|C0ePCqVXJIY4+$^SbL9sDsCBX=Q;AQP9|7^UY9G=JcKa(HbJyNowY zP9r-@qCG1*^1VfGZZbZ5AAxTBCaiZ(_c>XcZY`nj?;YyC{oQE)EVs4|x0-**P-b2J zBG|VOQqcN68a`ajhIKOb#iRW2|HczhdIA2NeuZ^%LAq7cvD>2j9`(uV2j+P)uzHm9 ztL@e$dl|>$m5GmAD7j)`RDK+q+o7RUYjWG%c}^+EJN62B$A;_2YNM^G-MrCST9>qU&Ht$s-3;H#Hj6Pb3jJI;GW%tSN zphaHWPo2N^G-r;bzeDM42Xq>n8Vl*b`{o<%Rr@HaFT7`Q-8I$~-$vU%e?^AE*cp?1 z)Enz%eW$*$d$Y0QqcY+DsSmAfGY+rG;7J3*2xdBcW=p$ z%gUANPY$y)a<@v%(+>SRn=Xzbr`NNCy-L$HeC~b&8GMY6YI7gsT5E0YaUnGfuam<> zI#E@9Kh+^$tLcim#xF2Guww_3fsCiElg{F2=^486IvG)CGd%0iEvjX;^0zr|>U?eE zcaPE5+h|)Vf6&cr+7sqlyu2cx^Xr$5lkpk!9r}J?Tk#=;U&z4$^fwXSF}@)@ul<|r z>1!KnIQ!?n5#`S}2K#m(=VYkoUe;$hhNY$gZ+5%(I|KP1bV&Lv5Sna zKAnGiZt0hnliS0L$LHv`|I}01&p!G?MmHO$(F4pY;A$S1{mycyX}`|m{UkHU*(J)`IB5)_Sm$~x^< z*9`g_%cksEhWTxSamKG_@;e_TXHDi?o77@^@Sz-Cu7(d^p!ev_wwtgQ^yrL1cv-=^ zvb1wy@_3kbp8gnbKS3M0OoSzeMya|%uFo@%UF#g_GPY29hW-^Z{#~{tN}Gq({}MUb zUtQOe{nqhu3nl94Un2CupLNs8*i`y+vbhvbf{)XRuUVtfz3*0;XUItQcF#|0a{~T9 ziO(VId_0Pi|F61+jQ2KAEHe+p%O&=w3?XCJ0iL|0-Chf*?)T77V^|r_kHd%d@_RlTm6`Ko6o2qy6es>`-DN!%-@j^O$ z`bu9N9{Zs)%!8pPkfL)A=e@F{Vs}2I_6_~v3sbpzxhu86Jg}x+NefO zd$Se$nt${;g!V6!@qFZGtaDBLPlQL5J!pir`5x+SN-k#M>mqy&p#gq{aFDu+qrI*E z4LpwBtI6hkXiSV{-9GG%aT)PA+g#XwbiC7qZQeqE@OIvGtAgSGX_-FOFSRjdWN10ru4C*sex2J!Nd3fq z>>d^-^{X-24&U<#-y#d4Fg_iB8J?>5!Yhos?eC2LSjSAPioaXP3tfC3O?PQ8gnVRv zN*P zcIW9A`Y(RM_;ym)d#?YFg-yBD#J4RJ_?@O^?pwe{`-iQ=1!;kBHKz4t1W6r*B`(Mh>tpq}#_P@n+(EQjN<5NG3 z;)_P));H&1*Efz}`|zk#J33NcyVuhB%gs5;-#yrM8z)6wWIg(pwr=>sP@|GkT@thbJj)A`UT zp%!bU8>2Y=U*YBBcsT>jGu0cyH|h-G=(H%Ft<9zEOb9Q~wT_PYvak8t z*t{~ET;cay4_VVcXZ@~k)82~8CjSrW6>S|u23O+IRLb`FarAH#<*3!)S8VyQjJB zps4Pm`m|T~C>{Q*mLPB1TNCeBV$bEuY$69=9pXIZ>L@qso+zHy-#X<fSN(7nUi_OsA?yg43yuU{AKPq*)^&!Y6+XOK0#xf>t0pJy#? zjK<<$2;0z@9NN>(L-4wP88)n>IWUXNRwuuU*<5_P#2EFm|J>$eRzF^4)AnC#{jD8f`y@hUbd1BUR0#WIcra9*g2b_ln|swYT9);|_DCuwCd3p>DkYA-&HC zI&Xg>d^IzX%8jez@$$^v?f;besUw{!Wu79V%T`9E8sTFitTxuS7{j#(kh3_=WAdX5 zFT%TAiE!B!QR=gN*7EJy^sZz>zhAjUf9*PTm~|%`R{a^e@{YOu^Td(4@nEtvexGQ2 zY~9@a?r2;-4Nvf|z$E7-ZFBb}Qp1gZatPm2u1b!#7@tSarC%GZPZyHqG3@;*WOU!; z(ouTOe)Mz*dqLi|p`~U9Id7-$Ex<8X*nXERyoHv6+3X#@6};!@=-86GH~7WhQ+N%HZ`$|G zj%e@mbJ)7hu2oFMyB=&dSxmoOTX@u6UrW5;n)_SE_g2?C_G0@EvGyRh?b);MwVD1Q z+w7Q=zhv`%i0V9gjd^8NlpeKKf5=pWx7p$s$%^9&8>bMC)ZgT=RR6xchOKOBeSp7L ztu|JN;{8zPSEJ0Qcv|;#;}o|miDQS%XWNX)JUm>6j&@bh<(Lo_<4?KWtwKy8P6$yXJ5GJlbBa?m`X8&Ruk}09(0z z*K%QNQ@o!cU2i(xJ;ts*$&SQ%l>Zj;o54mJhepM$bMYsH<7bdHIvYYMym*p)T`g~W zKDKEP{dgqWHzzZSx2%ui-T%v$CZFWzqQ1`m-^$%vovb`d=Pn_?i+Wr8r0eVA-2EXn z8$EmXvJN6ovu+^&L+D{s=Po6kv+PX=u62C2byg?mom0?$v$^sL>kGVHhW7bnGlV-| zkK$S*qPXXc#&cej-u-_eJ>5MlsH*)XZOpy?so)$lHaaOfc`=*DL` zo3UZTm8rt!oe^z^|1Ym}Ux+?*c+njCj{cFQvfnzFpes*8a#&}K_9^EatfKRzCDsJb zM0>v&W*sn_9G_rp7SXNe%&qviYK*$p)2GH!x$ytvRn8O0?C2x$F+)8A&^w=ue~s3C z$dBVfsQxH>w3RL$g*QW;pDm$B6P!aUSF0)gGKS?HU!ppBF^)Tp*_=Mcq`7staXPLP z{b)!=j<@zJo>OJc-xj=oq#*seEH`hud3xw4Y|_=%hwARP$u-!|a`*p$KP{`UwKtg0 z*XyVLg;1j>nZUo~aJ4o!m(9%y-($F_a_)9=Dts5?{P)@DkD}bi$Y5b~mqx=M2{qS{ zftU3ASay}{CBlh!Sl{lC=26D?vqUa?h~C#~lGlY%-hhY5;t~2zb~|*yL$q#d=6cz> z++#M6wk|MMcMfx(ncTG8*P8H6^?z(!$I`nC%pvNiT*bA3$JxO4=BKT>#}`#TggWS% zT!8&LDcVl{|FNZWrUS{`K<8^8=I+@^UPHK_T%_J$EcNG}$x&YTpJObO!v-{*jn|iG z=RGo0a0q^WfW~iP*$+Gaq>mfO#}4CI#5f%|-F#g(6(r5noN-8>OqEV`R_Y<|lI2Twy(|jl%eKhI-mhFqg7z6|$V~++q!tFHtf#J}Nyo z9a6=su_qT6x&MyC_uQ0;}#f!=o0{-9H>iC>EHF2sWt z+I{1NX#0jQ*%UIC2rZ7aE+?zw9$;6dp|3}@XGPm6?mI4uH$3MVNIbm1Q?$JmpF$Y^ zv31JAD4qP@Z-((}N54Nw&b3~qt0DA%F^cEUcHJN|N{9b48D#Dp<9H=L-yh}gl-GvM z%N|Y#@G%j#j*e1aEJ@@<@wR?!VVtHV=$&v4*~hyFF0t+;Z;4PZ)LE3=w2yM$h>yeT zr;y2c&TqDnX|lR|n9Ihbk4JZ@XwT0zoRcYAt_uCs=gdjk!pBNv^Nnn>IMW(;B7SU& z%HMQD6wiE(KA>y$9Al?nn_8G}-i-DwOR}v|PVWDX#kuK3YN~#OusL3LC35#dhI83r zQU0sl&1-iWFXbv7U~NW@hq4Kk)|scfSo^bgzZX6@KiXTrS#GH&XzZhJmA{Mf-o)pl zwR2B$FEr~F$tm}n`+h>e#Z*IQB^YZtT zCvsdg&c`DQCQ7p@C9H+du!gYC9j~ooO|8`{nE$j{ybnDR*Qn0U;&VN6bay54btidR zBK<6zvCMM=n~euqyzE+aY>n!ED?T2%Em;@py@{xZOF+rhnjb$7zcGMXB!$Aqf+*5*T(ng7>So<`?Jr+_>ddSV=B~-$dCErf7%a3twl15_k%PiVvuD%D?rF}$?!@Cs^o{P` zQO-Qx2_MmZUJq-SIo8R>8f>@Tpkc#1q5qX*aH z{ddOYX8pbj&19h49nR_0F%&-&Vf@4Tj{aNms0{w@9!~fcA2&t0JL7t>?Gx7`c0_qk zU&xMEME4Nab>;0b{e30XF47Iz}VycmFk{6*7eUT z@rBIwyWRZrnR5o|lkhB49qlKxg~q5N9v!aP51X+?c=fn)-M4u*aFBDrr;Yo8&QE`g z=nDS}j8zCP#QT*Wt=si;L^1Q#Fzd&%hMu?{0?LP z(>BAHG1x(OPMZu|!Wc97(2UpmT}%3o_dBVC&JX zQCpW7<2F<1be&uZH#V|nF&^Xf>#dP^ajmg^Xhnv#-$^y z(~SQGrSQ*qWL(2O;Kzr3teK|jzww`|;vtuyxd;8Z8ZYs&`f}sZSUz5UTZtXCZ(R-Z zDL$>V?;U8Wtq1hK;B@@mjm9hYr7ttl^Z~sbqJemL?+&(kO|p_cCuY*Jv|@%Dvgz2kC~sCx`vUL|bP} zHh0!#BOi0kV43#RogCh69&J@UPQU5eYx?>2spfq3k7RceVdpoAlsWVwW7dvb_RP&o zPW7dnbK>>(-)g+^J%qZ(;)QhfY_PHJYkZF+YQVJ+`Wabq9Vfy^kIbA&&>)p)o3=?$T(h`~X}2kn2xJ;o*yP148)t@hJWZ zUqkqAwDS)7eEaKkf1~lr$BwDvUUGisljfvK=5;dJFHYZAWUK%D__2GrHgLW5(0IJ< z6y+uUKT%f*)yRDaZO&!G$?Kizu3OY$+h@`J9nrqTf425o!h_c&a@f&=*1?VF$cMRk zA+;3^1G~B=gYFR8w)foJt2xQo_mj!>VCVSywzQ<{zj#&?eZ$Gl3HnyZ*bZAnN7qH= zi)y3YCB_PW-*`F7t5Y?KYmf)(^^D`#ilWV|`()x{--X85tGCe6)A$dgx3A#! zin*@cU11H%p$-p2o}WVUE-n}w834l`$4R}P>@htmJ2w6~i)gtD#3 zf18E$;Su+D(V83zU9P`mu@)OpR-0KTpmPj;LCdfM=DMIZ+(KMz3H;!r$ch+YV1O&s%)Pl=uquw`^IY4h3YDe z-e=JIv$3dzo=1$&tOPIw;%qXZBJplJ|@Dd)I=(Z zYd)E~eIyzi;6XiO^IlVPjq$x;4BNHVb;qeuxl-ugiO%8m+3lyVp| zZi&+2f8LE`qCXou$+fC&)?Vmci-s@p?!652IGtNOIk$WwRoyrx!h7U25#FRLzx1+Z zVGoXfi~PJ#_G?+wtV9<%3t>UsoG5m3hbSJ0&eG%|gq`}42>I1Jw`jCAv>N+N25OP5 z*9)`jXggQB@$5wDD86!@>$}->{WRwo4aj&Sbdb9?gV-6{FLrhwZTxC{fTjbiD~;C& zf8Itb?azrN=JG#ob7JT9@U`N zR~z#siITZ-tt*Y2zJ+k&2~nK*-}bDvZ9aCK&3LjW`6auR>RDS|U_M%pZ^kEtR{C^A z8*}FRoXubVu6)e-{8Z;Pud0Jx%fgrXWn8~3tr+ zQKi&LKXwlVQ>>-*v(`)GlAgpiL?!w(c8*%#m-%-;3s~URriQt<|X?NB>%0pMD%qX z9K|b(L~;0U-ozNH_XBm_@^Wr|+0)s~b;kW$dU<4SevRwQB_CPGT$r0TTG=P}w+6(U zGPkldXBzua)?{d}tUo=CY5)9eAzFvkruWKSiidZgXU_flKAL^UuP@PE`SaX*+fI(s zy}ma$oMsJwv-QE^D6j9e=6^EPJJr1O3LQ4aPX$KrAq*bRCREfiyOPoXC<>6E5T1$3H|K_}9&FI+KUDR`*et+S9 zRC)8d~z z=wLicoT_i#mB)t=8f)XEj#2tUJb!`AWH-S-Jb8b8lpp@*%#YHGhFMoHiP9t4nM4>% zCPKLBLFd_Y?e&%HKVGD+SLa6KLVl-9-`XdX+H1y`7D2)|L+m`BPm%hWLIg)^Z(7hyl7pwFyhPqEB?Q;lKp?DXZ1_VD3_Jp zuTy%jaqPsJi}X!RhcnR9V)59513>s-?TG{SSGK%}DR?4^hX*WqS5M z*41qOQoAN7i`-HE>aDp;dPe8qzB!%ej{zqDxr~hd^%LOK6{ZZBVU#cS*tun2Pt=7y4O&LguenM4Lv_IqaftZ>ZaA=a zMtaBe?E1ZWUzW&A-DBo1G~{|Q-s3^3afipdc{}cv>jt@R=T7}PChbNNXUMVaZhbq) ze)SX{QUN($!0k)V?36m7SKm&dSmRCuf89)WbDZN){8yq{CpD4d`8csB5&ueQoFA#J z@gsZpqxO_%w9GtTj( zp-r@rob_9O)W7z7SGsZsIM-oxYSQ>M$dRqo#^Dh~hm7>BhKXd~&RIFEOs%hcM(1pwxX23Ei8^L;p`N27 zZ6X)!IUciPt-GnSQ``f4XtI9u9M9rsH_Y+Q-8zSi+`Qm-eh*o}6@5aw65i;W*N<0} zLrx@>*SqCr1tHX zx#xx3F12+drKwC*m-JtkrT?MQ4*k+IvePqD_w?--mZU(OS(Q; zI>gEyCvFaQ#X8C_BX+4crpnk7*TXA`%iB0qd<>{6mhCu~hGS1S$YsP>3GEaU4=kWg z@h17z#rLFmwp~0&y$6dc+3%8UY?(N;u^Ng(ckP6_f6>kn;&HYsibGH|_N-1HDBdE! zvbc+K<;1nbhl$USUqM{3h-<~-%at!7z7k#gi)Wy>hB)v~GN%tnij_p?vEsl)Rm7?| z?f~&z^}19XJ4bxD_$P%A5!cn9eZ_r2Q?ZVYuOcq%;G@MMT-JL}3X`Y*yIKA`{r{8e z=>KE-Uz1SxrZv@r2mN6H41~+za=^aW zAQ%i+!c{N?u7+!1DC~#sVz3Z>i{MFk3Kqi>cp8FpucZ)-<}!E=o`)CUMF>t)SHMf~ zGQ0w>LN^Q#PDN_KiBJya%gc4XlOtVI8c84e$Yc2peG&d;}lEX7~g?g)Q(IY=v#`IeYK-c!`@;TE91ei6 z5I7JHf|5`QN<$eq7!HB5a3~xGFTjhi99F}x+u(Ef0=|T=U^{#b-@p#| z7IwmS@ICwh!S4J7Kf^B&Bljtg5As6+CxrZL>17X3G1=-LK`ojQl|2gJ&co>$!bMQR8058IF zSOG7=%kT=k3a`QI@CLjIVV*mjOqGN3a0DC)N5Roh0gi!+Pzl03cq~+bs!$E8!*Ost zoB%c8M5qZT!O3t6)PmY@D%63~;B+_x>Ows@6Y4_)Xb5M)+0Y0Y!#U6dnnE*Z4lST1 zoC~d>HMD`Ya2~XS^Wg%x5H5m?;S#tMQlUMhK?mpvouD&xfv(UE(xE%_fDGsfna~S* zLm%i1S&$9=pg#m~8;U>5lZh^5d4#vZ+Fad6Z+u;tF2$NtkOo2OLDolgvFau`7 zEVv8qhI`;%xDW1!2jD@N4G+N_m<#jZVR!@{g~wn%EP%)130Me=;7NE27Q+&F8lHir z@GLBY=iqs`1kWyoRJaijZ-SfQ78ncRBsNd~|99zsQ)AT(n!_2!urAbtGod~-fQE1u zoDKgk>%Y^BoC_>Ri_gZ7KdS%5l;0QjgFO9@^gmoE3KxOGg{W|GC|qC(`k&{S+iL57 zk!S9N?zbTCd7ingTJt<}2Q9UTs_rCfx)YwM7Op2#;HNF{p0NP6a-C(ho#&Z5hfT3I z&Uo@Xb4R?mx2{E?GsbDouKuoP?oeZ%XKuRJ#)({`h`)KBxt&N#J8;3pCTD%$9^s;< ztGWM{XYSBWo@Z_s9nN+E(N*$1&)hDmhr0(to@efe4|$%sBR-6+8^z&2&og(#i~F3@ z=XvHfPhDf)ZtBLaJBWFnx!u5S=;oQbpn0CT^E`9sdFFNl%r(9|&)gms*xwE50&eVj zkZ<>p=b78X814e*dFJ*|#i<@{3QxQUd7inQn^|9^dH^ENGq;Ckih6*_(=vIUxjiW4 zK1rP=9^QG@Lph!v%k#|bp{(#!osj35+qErEPvm*#j^rWnpXZs|!_23+rdr&6=L#O^ z^F->u?U_5l`w-`O=JqgVom`IOdFIaZ%$<;iJkQ*o3mWZyn{`c|XKweRH+jJSRaQ87@hWFfa2nUBZ&4d+CMe*4!qj-II>na}3 zos>v<>D@DsA>4VImxVu$(zDAsPkT4^{Tc2{z zfP2>GxDxPQ^~6}C4N*E@N`aJa@pSQ&5-Gtsydz`VVz0+bFN!@CyYQPRHzVi6XUExJ zGgg~(o(8eTTokpAogcd-*1>z{8Qv-H?|tzh-UT1&J@0Yan&SQJd%Sx+&qo>-#h&(- z^$KrLuZ+DHTNm3H`$S9IV>@F%#qy;TPAQsFJf&pHAt~ij>c?Ae9c!EOk@G;)$k@_2 z9k<7Bh^L0c`o&B3_s)9w1aK&MNzR9cLh8MoPs)c>@sy|HB_H$#`1|qna~?)NY(vD0 zZLu$6r^nMj#eRt$7O$;!EPN_A)X_NBHJ<7dyF1RQojD)u$Y<{*-p218KfYv2>69TUh-oA~Q*YAo@a=wgCw>p%{X?-wa(<7;qTf>_Ec}-K`g_5sM8B84 zd7t>b;f?#m@10oqt%?6O#1g-cV!6L9vGDsM=l4x4`h6dZe!m#s=vROSM8CaLa)0}! z2*_#Kgw^jjavP;KKiJ)RziH_sUDF!D!wJN1M3c;6Gt_iZE@B~w9T=ChPxNqOylm}Qfq2>Zv3)rxILh%s zf7;T|AQ4yOjO>t{QJs-9I``y^%ZeNhhgRR9k%51Ob2w2nhX>_ydO0}WcBo@=JbfcM zcrc!xV&wOY(-i8sB%Tg+yd3ZMl-SmI>cQA)aY|py`Alr+>BiVe@wyt5?=JCl201Mh zuWOxAt`I-&x!7~@RKAqARCp9VR)6KZb4Vg;Q^=1kDs zR2D2~N!J`L3GHUGWC(>W3!Y|sm=VvzG_6jtU)cd!;~SjiyU(YS=s-oj!Anckiw(K~bGIasS- zSkNHH$zvBRdI=UaNbVsyavP0i_34p&xqsV=SC5Zk;C^a69r#-;K4Obv#p0T9NXmlv z{vea9;_1Mjf$`A~^e>3Z&$w9lz;2M58L{xG#E_bo^Ffc0+8(PH&+8CNi?_5c=M#<@ z_Wluh_#-m#N5=h+jP-9v`Hzh3Z^v}x`pD7*{SKeP`=6MlAC9*fWW7dwoPtEQj;Dhx zX2r*%f2?f0bkNQ^@pRC|vT+$Gm$E6I8+2h)eB_FzWX5xYHa3sf)-^Uep1LvCF1|nL z?}T_d=w|rXV~~fRVngEe1nD0VKkka053mK>93-M~{J3B#E5%EPai|n8%~Nu`ww1BI zPx7VUxc_8L{*%@CPuAf-S%GBD|4$m8tkt)~`w*7dqWtB`+3>m3!QiGMl0@KbOrtHj$eARpc^gkheb`bWB*<6#9W1BI&8lmQI-C z>6*1s>7{L)P4)Jc+$e8_z0aLbrYAguuO=bm<|v+Ys|CQhQ93M{N>SH|Teu-oPY9>; zA$7|2QTp&M&a8@fQlqY?#|Lt^(>coD{r}z4T$jC*oBPrmx#{}`aD6ny-7`k_hVOFo zLu%?{Tp`Wl(rl5p@K@#Tt8+d#UT8lx&dcZG#}(D}o{7$h zPS393HmWJNW<|LxYwj%Zfy5Equzi-Bey9L9W-D{I_tKC56v|jK&b`6Ye{ANk4}M$f zfTX;KjQNfD_>;5Pa{If=hsJvN-bNh*l}oM7txz3qXx4M}^a=hB^OU~69eOPHM#>es z%b4?>RrXzDfiERK_4bdl<1Xa_PMNhko$hs1?C&d4k~_E}-x}y#n)V zyjy7NEcEVwH9qOv>yE8Fgo`eG|84=-ZRGW7?IglPT%sQoa;Qi8H_KfWaX0E?Y{^APmbyc|JQHJO`q5&N}offLa6gZ6c50Qan~vv z-~Uw=dZX?T-cx4e``lp}*Ty}_y0aj)TfFLtjnd!%nm2IVvn z>#mNyOQMU69BOT9y9i#`;ExZB5v3E zdlL8nuD#T?Xe3!8$3IPSDz%uKw)OJp$kdzZ^%wf}RH&8vAoWxq?Jbn5cs83`FY^9@ zdcTbG>+Q3na$Q={Tm4IfBgo?r^zCy?l=DtCt_$%q5!T1Yp{+4nG?w=cV?6sTcj)o? zQ@ks3oPIdw$rJ3GgJZj1u9U+WaD!;X}CCN+S`v z)<~qh&$=K=S66o8<5mn-&{M5|LYQA5iZdHUabqjHH6KRlB159MYdy}Z!%A(pFl{O) zxi?y!z2Ls=i!Pz>>)1k8H+NfQO|znluWoMa&5^E^;jCk$3O04*AhwwFZr{}0<*P4w z-~ZY3hevi{Qr`D}I#%qf4ixkY3Z5OcKx)MI~jlVOHS#%c4N$6t;;8{|H+S5 zr)B&Xy2e#IKBdu;xlO}0flJ@NvRJ?J?V8Q&dB@>*yj}Lwt}eDz58ryuiYDjiOVGQ2 z%z5{1pHl4UBcd7M+j7xy>FJqSIlYQD)A|hzmG(^Q9c_0?@0;`Gy2}QnWq16a5U)MV zrT>b`?EZ1Tf2EN;{r_tpu+7u|KlKsgj=ef{PRmToxGXC@>*B0l{rYyanb|8l?LU09 z@h=%t#xE@^t8>r)Vzd6}hnM@O{gbA2K=1JV*e=!nsn5bQi5ck~{-LgBr1wnE-ovLn z|I#pIW$Wp$A6kv9{;$?L@B82XPksOU&wsHrv9|rq+G&@Q(XjDb+5SzD#DgOLrD)#y z{~p#XyFYl7cmAJu{$Dkm_ZN&lyOQ_)-+%u5zyH^N|1S#Kgma-4w1zg& z7S4lqa6ViB7s5qwF?4{A&dB60d>Ju>DZZ2AKbf-xnJ&i zVc&6ra5cLzoC8guDKvxT&;nY*xzGw)LmOxd=RrF-A1;6k;Uc&gJQtAj!RZd-N8nL# zkKdEo9xwt|)?-h=LRbV(LU_(&F)V?n;Tc#8&%#Q08{UCc@GiUut6>eSh4*0{tcMNo z0elD>VH11=AH!z&1U`i=@EL4{ZSXmK0bhdW1#guL&6M&JM3{oB9e@HBiAxIyQ>ox7y=?v<5& zLTY-?-WjPCQ;%{PIkR6zMwN1@M|Di=T`?u4Ql)aKIR#J1DS7;_CHtpkq<5+mi+1Mh ztMTi;o@tkL=$une>QR?xckbJ>O6pM^F3aw$%G7~>wbCTg{;@juMCd+v2C%h=0<^sxv0?-Q%CcX+OQQNEN|iP!_`JS6t$_d#PuOCKET zc?7Qmv3(s?Fm|2V^2fG0sz|K4y`^KB{>#LER_3tSUFttPwytoPy^gs#d~>%~p_JG` zv32$y8XF?NK&+gai^q<0RH?X_{23YA!*B;qcKoa2v!|hs%j93Jat|c_%^LSaq+b7n z1pevTa&1F0al2}BvK7cJpTwR&Qari(T%Gx^RsXl5$<-#vxIBlU|F!BrQari(K*}Gi zdQU}z)+g+6aQ#iS5Wc*IaqyhZ>g2`3p*ycYzY3xy4{!ATxJLk?x1Nx?C zcTVfrv2#{d?#z^2pl|22PQSN5%vpOr^4Bizd3T&uiJc5xa|L{yHw;>mLw-Rvif)GN+zu1->E2xsJ~TDGC{e$ z+kG1PJDf_?74*N$Zj$qxvCrjtGGS2<6BQ-aYk!`ozg15%LBDb;$GrS$@k|=Lzgbf< zQ8~T)+XVfMT9S#$>DM0C!+(Cz{$@?dM1^T-_XYZ&C+Kg~l1xmZUw>b$|7K0eM1{Wn zeS-E_OVI!LY1kiB`*^?pwn+c2dUoT^-zElc{!ATz?X2^6g*vy&Bs=uq&N=^Jjmg|f zRQad%<#!4E8@26;xWA-3zf0iXtZ`37?opqT7KXpkr{5*;Z`8IY;{K?1J?`B6{A<0g z1M??wo&npXIp2%z3j?7FgcrWQ8R4b;_3q`uNl+h}4D#YWTn!7Sd8H`k{%?kRy|Cp@ z_a@aBoK38Odu>mH1+!dZg}!hDTma?aG$;h~XLHN}A4AibvDhf+18I;SYQsTr3^bbO zg>`rYszPv-aXU=9m-C2w+*=jTx!-+NC@MV|+QORnMc&}_pb>=El*T}KNj?kaO3zbf zld|O<+XgcH5 zxGx098%OEuHtD^@SI%`!8dg9BxZU-Zxlm?~Yxm*-P#)fbBJ#G0Pl6WE-c9KGa3u^? zS8y$U5%h9X`V(bW!?_R~nS@X6+yvpJ|KLpHaM*_b;nR;7L2ww-5>A0yw%@a_=L`>* zxRG5EUd87UutNS<`q;s~;KXBZ`__n4&@)rp$IG8=|5xIQaHpH&17Hl?53Aq>m=7OA z5$$bvBmEu7aC5yT91Ov!Ni%p~`XD#lXG>oJ)uqRae~=#&XW4!cZh(j2GkGoS3yvTf z+de@&!uAw+5-ycjM&4KA{qXG=$S1#v_NPkE5nlk?^|cWC+pmemf>V{IFchuDUXR7D z-4Z#7c~BgjOZ0_jU@eS*nQ*jyXTN3KArpo}>Sy>4w``XW!Rf`fu=R8Ng}G4V3%c_q zr#Wykd;*K)1xE*G!OsvJ-~^1<;$MR>^mW%AyY zUZ~uaP!9?~Wo@4YX%HNoyu5-l1K0@R^KR47KYpct*q#E9!g1TsVBf}hoGsWl>=n;V zyoQ&rYERt9zW1f;%RlI4&wIcQdE3O><%Q2BbQTv^roHro;w!{O!pa~-=3jeXF4_EyhyKzr%?#0%j% z=nvyz*6aFbyr;pB?A-9p974d5HzQ{b$K%B>(xt?!Kcl};6dG*N{-^AL_$=Es;Z56{ z;caTAJ2(UyLin`U@&N6BiT;%zdkzG`>!ZQBMjQAP`hTMDP#=A>#F^ro#cjop{@oL)(#m_+z>1&l)3h(3X z4mfj55qX{diEZ82uB*0}6|8}zA_?FGhihjcM{CqsD%4oqsQ za}+FxB91>8o>l&Ey8P|^oOG~lh1i`Z>GJvFRpRiP>}k>^l&K<~_JAwdFiHM+c^lcY zdts#Xg-h@X9)V%d8*YIc)Y}MxGnX|FvR%rKU>7fiiI5HLWPKyb_w9F`ml;Jjufe6HLFFgI+A?-ZAVrSO#PVd6^SE>Hu41E2mOA7;S>eQpfF zan_m862hk)DnMa4Reu`5jgGq)mcUMYK0sUvE`}@M)5XREmOwuJ*deaXCg1DW$*@7* zA^7l}xUl$YdHvuFb5L-sbByxA=}v9Pgoj`R46tuJtb;BPoKTgN|EPG2<5$D>J3Y?= zBQ}uTVfZt|GepwCKX33T89V~MHClS8deV(kYc}H=&VIyaU+ezyRmK@=X5ho+Y^<_p zOLu^-kO}>uynVyOx4?qF`k7@;>rQ^)Zh2p=*FPwvz2G0Uvhq9S-){dG;_t*ii-QB9 z(onwC(NiJs#|H zUF4`II3BB`o#3SH16Y7h55Xxr$#IIlpm#Fd0}sOsgNz|GY3-Rcb*@onNzAq9+oNX! zCr@YBAsd1-A?7T$P?_ME>%e^GcJcS>7R4ItZ*c530gunySAx6+osNaN_`FVfM{r5N?9tkZJ-v1{H3xAMTc3iHC0g#R|f5 ze1B;UdYp`V6|kP`C|d!6Wdz z{Y%BcdD1)51K=0wy~TZC3>*xL;b$my9A1mRg5VhI7;$jobU)OACh###hwgAOe4Z%} zE+tcKpbs8h4#CYq@m|`3mg}wcAQJ||-O5dYTKdwpw=q%XEq$LUo(Q#jn7g0`d}0jN z!%4>D3vtbH##j1X>DOScbTxTzUgO+D`lQL^!ghY?hT`B%u{_j(i{M;nDnB?3I{?~B z50G~iTq4~GZj`)A@E1C5~@o=y-?hXAdmU?NP1qi!|Vi|>WttSdOg3r-Seh*!bW@|KG?ir<6J;Ai+6f>Xrc z{v$X#dqld>cyj_Ql3ofWr4NN7BgvLHI4UeF{eW=_&fBU=zph+xepwtE%c~0`Ta!yX z7%Faw=JR2+vA!14=z4J8IRmyqSqu+O6wiTCwkN`J_y`V^R}q3^#H(z#I0bK|gS)A* z1<0NB#qw?w2UjG)@nrY}YG?bW3~@dLi{8hZCTyU%!%*`FoOh~mI89o9+B$k(i=5PE z7i=F3%UjSl`PtGXuhoCsFE%IF0bdtUiZkB!;=1YGqE`Jf4r1!ai9!p;dHKl`d%MmaKRzrSeXWRDI1i~k*SHgn2JZ>sUE$A z4$_+&utnml8`2f&YSO_m-&k=?d7moRNP4-v;Ec4hyo;p|lpZ4D@U5DyZ6Dt=SBO47rnr;1A| z7n~PWFry1W`*3z)`eL70^$BKt4 z7d|BtJiC8jJ5|}<@I0iRu8%NXx|+C?cmXt({!)G8q{~Xr5!V%OmKQz)UtD^|`{=Aq z?qL{AhNt1DI>ubNR5(mO7Kz7-m!3iAq_>I3h%b;=P}~FxN#7?PES_WkkG)OPy=o2(f+Wwle)Hw zZxWA|S4{d5@hI_$^16xl7O!z!dFkc$g-^sbmA6(rNIXd14*REw2goZc?*j1-+ri0g za1h#8x}mrk)Hf!z@F=)xnv1q_w#%+FKB??BTm<87*Dzie!cH<;tUnrz^S2Nj8-Kgp zI6`ppS(sg_GfAE5xkOx6+1^9hn(^!ie4>p>*U}gBSqb$Wd_7wYGaxuR?h7NK1+<5H z?B+ssJ~4{Dft`+hQ~U)4C(PGE!C~fjST1i9OfaV|vK<_oj)APJ+2I4}D+EWH6Crb= zIbFSL?eHiOJ@K!cg{RcZHxFlOUg00d126@NXz8$@zGMy6`&x0XD|J#e-kw5-i zx(I_?S<{KnzZtF4!HH^cOnQXl8s}&0^3f0R6Y4!k{l}>LMf8leeLGwQ>tXc(=Kv$g zlkz8&lbives=C*!EMaXIk_=yi1D zJhz7S?~uO@E|uR<{xm2kKYUU;I7S{1EA6ju|DAB1{hOhw{QKqaEw2xpFYhrpSpHFH zIQ=Mk2}jA>OWYqmg)#Q+cjW)a-h0PaQFQ;~n|3dT5D*JZAXEjV_kci10z}eGq2uL} z+$2}hNN#A-6cIZjVh8j=#fqp1c2q2gJ}UO!9u*sk9epg{_nAF&v)Rm9KhM9v*Kc96 zd*|$&@;+x~XJ>b35OYYcA$^3hG!ZFHs2T%W51g|B$&Yg1BX16l46_AAbvhP;Q7 z9>Q@S>@Pv8g58_q;g8N}e}L;3NaJywm;(PIJ%dz>yvLE+Bma}G=vO1}TBJk3ayabh zj+BjbE6$;b#i!!D6wp^7eSowId9NV#MgC7HpNqWhNJ%L3X&1yC@}5N6fn!>p>q4Y6 zaQ@>szYFqc0^%!h{4G*0j%fni-%)-)j)&viTad2BaV++ANOQpVRFuCR=lhUfhE$8= zcaTzXoPHwuGDtOj5NAm3a7+^`kHhgcq**wA0*NM~{uSwD9AAP|jN?5>{SY6UPC~5X zIEekbNHL((1iU|E-wt$|`1!v`laTfxeFxvR#-1j#ez60_Nbq|(;&(RUuo~%E{2mBj zOhDR(}jIu zy(rhE3U*_E0OiBrxd>?!^424*#qq1yZ^b?ayf$M0K7Q}Sehl=uJ%VdjJz^aDr;wgT ze%D24*N}FCP7?}$jW+9P*pUnyufqOO=+hf}nqYbZj*o^OPo9nICg`***EZxGfIeR% z{TH%&ZAN=s4J`1xCHVJ;yv|5%fMGn+Ye@T$BFk}|N7{_E9%&!a9;CVFfiKc)7b32Z zXoBJlq;n8!ThYdBM=DG}dx%6+`nAXY8l;vuehTN*;rMr?O8g#*{Z6FbIHu|QVsKo9 z{br=WNHlr&{YY!zcbeS#2b538?~9OVsm3tUhp~vqWvB!D5xY2k?<~}*^%(b9h-)dZ zx^N48lY@2>`=63fkK3T_IU4)8Gg3tn+MF=(!g*tm_Te}WvY$oW zzaQx;KknlYgZq(UTEL%3hZ7? zCIG(=v}2IJ7U?t4a*+Qd(wit(585G|a~fzg`M4i>IY{>+ZzJ~aBL5)r`{4LDlnEi9 zCT_n6`7M!u3u5|Sq@KVj9cd`iugD*bb5`Np)!09Sb7|83PB{JqJjNlv1jjcZf9RRG zo>l{6;IakvJO{@wvH7eA#H_T^H68MK$;0`-bLDkvAiV}YuYkT9^mNdlMjC4=l&Zy-3-0ck48Ig;sX$G2LeO~7#r9G{Ql zk8pe*j(y;B8IJqocsugj<2WCQ7Qq;f{Wr(r{t0o~cLC}^E&Pf7c%(Gc8CrB;J>ubm z#b~#Yrd^DC8l;ugunWIWSc1N0J?hv6xTfK}7AGJs;inxPQ4gToyWo2eDIUMaBdtP8 z>IC^nS0jCcb9*AM6zMP$O;o=asXO=_i!>PNS@3BKT0YWcNGE_k59xL!nrMC?@@kR( zKpKm@^N!)Fvw*{$pvVS z&xLPsjl3A?skP|epsx6F+!s8Kp!`y@Wx?YDq2hQJ%-!B83GUVNebC)kieL*^aR1ZBX zkPcpmm_wR|yp1`yMnd<2Nb$(~FX{*_eliC2Wyg?c zMr(jq2~yU2v|Crg_gA2R7<&=g5TqVB-V1E+M`};Mk%l4fN1W$J`7g2mFH#2_kHEEj z4Rl$8^d8d7NaK)KjW{f7M<_tB0gxU z>>beUCZxJ65ck+Wgme{T9#{z3I6e-%5|M5|-YrPmkq#prLE4E#i?2M1G`cm)qW%0F z&DKkwguWZFf56Y*-+%2#ty&K_;nTCPTK4kezl{C(D*xZfzlU-^^1sw~bovh`rvEYc zry~~*+B@RTq8A9(`uN@D?O%H8*&~;|RIzx=teP%EyVhQJxb%T- z-)Gls?lJPwr1$PPd)#dg=ihYI<#(KL+Ie%gY$D>b9UA(W;*z>=O zobcOGox6^yyuDMG@4s1a>XFg+pFeK)iXJ6#WB;1?QsL??%MR6cJ9O^KHb3ncRu&z|m;s-T^3~57Jg~6i zvH>gl{rlOgK3+HMv=wh$J@|#Rd&6gcyszD-FW;NerOW3nj`+{oc1gv{-=94rxq5D| zS1U_DYW2|C7w%5^J$YyTvbJ{yiw74!_|aDDqtN)9lCHkw!5#0#wM@#L*ZPltKXK2x zF=wsrao%tD%)Q`?i$Ccx^1#UM-@kM28~0DXuWJ49E-xM~T=7Dun7=Q$;hA}(-dpfg z;<~oa{LpLX6P;R&KIzJHKR#!6n?-H9|1zrkio{-HuTMU2OWhfhp1Ed!*Tcg%+&&`f zo2sWrk6iH6-hHuOJ+NTlgDd85{_Wqp{p($;TGdG+cqha6tG{kenZ-u?82>rzL4 z_;agYZhH3VE<@iNb?mzAKE0&Vvg+1-yhz7&EMtUK2jh2{_i>e`fktf@qa!Zo_+n&9ZP+GE!g|p z$iIH>@auQie7N%Zl|Np%_q0 z{L0<6V*q>4dgQ%(%VS4B z)#;CcPY*bpe(K)i$4viwad5$7Z@t>1c<#RM#V@G0bjOw@753BjXT)kj?TYyp7VBX>@}#H|Nr*CBd`ix&HugB|MyC)N{QOk zhK|~dw^ofGk3K6MBhiNLg;hTa*Dvg2@Q*gV|H5jCf4k!;*G;FqSUTC7ZEanL>l%)9 zIhBn+yYM|Uz0ZK#Aw?rP{iAZDV~)a_sz>1;`qsD(nfSjPL-KLmo@(>Qq1v>ZMEs(6 zB~1KRf%DnSpB9aB^Cw0Jm3`>@-f8B49L_n_L-xIb-4DTTdPYt%Y14KSW-D2z&ptC_ zw#MqLo{2j3jRj&u0r{^sP z8A44MAH`N&_z$|%*uAOzi(Ds;7kspweU!;Q(zeH|nvVNLc&M}0#kxtb?Neo2Qf%Dk z=J-4xrD-hIROilu>}4$f3XeLK58bG}(3|!v#SXm-;Iv!E{y7}`^z6?`x8oD{CQ9!m z31h*7bki}fW!TXZ2lV5u-VW@DuCdW^(MquWLfB5vL`?Gkt^AS{OYg;+=q+Q8L;X8d zjb#&^Hd}Yw`X!9pOb=|@3wm7yy&hv+Y|f4Lw?v&!v65oudGMt%8k0_oStoiI*JM+y zvI+MxRz{Q~={zo6Hp z&}*Sft}{mIT@@!^9iwM+jMDoyPP&fKkGb8TcZP^gn==L)Sa13k##BC$@m}en+sMz& z^>!U>qGxs{zRz=;Lhmt|^op=v=X=PVAjrK8a_L#RN$y7qj~($eE@qNj&vGw=T%v2= zE8rA%#{<60U4>^hb`UchWWV>V5Yy#ca!dGsCVoj8-+ z1uXY5kFxZAl*y)}I6meHI&Fkb^d6Y0><0>uDZZzL*j|O$J_>1v;0uhhpdD?mzISu- z*L8!g@HB@GJx_Mhb=`PQ;W;6m-UB2$ZO*z;%eYj2ZjSFOVH>?aXewL7Wjh*dCpqtP zy<8~pz8bu{3uWKqvh>cU^O{I@Q%h9^+0{zEPra*4`jK69x|=UD4(LH@Onf07{o zT_ryffAn2uTwB;N5kt?+ow7CV`#6qwLm#5+OQj674&;ID-U#jl`oN-F^jCjQZ3maoXr`ksgZBr~! zo^!hq`m}>AQ=AcI^lg|4r#Oxk8rL`3G7ol?C|e?7T4>(QgLHUNVfC#45ka?qLAUR4 zp1Ukr5DzQKPW2uS_S!DnW{hPkKMwWN`@zogx{X}P*Po{a*;^r--luey)x7`BKBjNy zO!QdB<`W^_=dzFK`(#sjA76K06m+`@y3w~H8h4nl?oS98@_sDtb;+NP3o-t*!sbIC zJ)0r$XPl%?s?Cj|JVgL3jEl+@yN~0K1QZuKL~|hiap_^b*ua3U27| zbp_4o_qCwIt>iyEuQAy-hwYi-3+P1O_?!5~v)||)Kofm0 zV?xgoiB6j{-eeQszTtnQ`8Jlrm+!6b`47L;fb&gB_L36#h z6SjW{+eyCeD^WSxV$l;JpT$ChHbVLBC{NF!P4>-X`{-Lg6Fwx5M)W!gyzd6@AnbNt zBkYy}Wl(M@$13Sco9@fe^@Fas^q!?j?vu(dclqdlaDy zDcE!mY`RB?r#KZ)sXltI!lc)9*6TtKxd))L6ECWDv?W64eu7T-LZ<-7pgRUADhSy) zT+L}2VmF?|`&Gt+-ivk939ly<-bt|=TzYF?CgPmof^IvZ8$Ihc;W3TzSjjP>`H@bv z(JZ9&4Yo(>qzfcn)pA@HdZ4h+;oyN0M~qa4si_2*L6VeUsm`&;-`06 zO!5m@KE2oC=1&+s$MWf&UlTpS>V$uvs`+h9^uIZ_=zVt+{V~Rm-m@V(ZKS*X5ATq$ ze)Mg!seHVWpJ;vNiXUx1wJj$D8)uuSY4l8vz8f*|-N$yXW!!bUK>E=Zhq_3!7Sp%v zru!gz?nC$PajH)|DfV@4qwLc5m7R_|p}+Q~i>CGK1UkK^Zo(y>ae3RLEPbP3vh7jE z{6`PWMhWrs64%$B+s(ZHVt+peT(sU~r@l559(0FD?;vZs?Q7fWCmDD8zR5(7RsKFo zy|-_o=P@pSdf4`gOE>ZpZMLuQP7`#~?S}UGM#YBHZ#n=iTeXC}YPqT%00v7a*uc`cgh12qQdPm(c9 z6z4Smf=TvctiSJhGw)64yL5z6^gX6Y_CBRsRvgWrW1<%$N>Wgs=6x{9e?-a0IJn@y zhn4@j`suw)Q~7Mhj^^($$xq-KPv4oE=+7$sEj2fViT|UF56y9AD*s=Ozn5XZ6MJ1B z$#$9}$yD}w#+KeUHOZdBw$U6JPCEG&=3?zH>T4ya?_f>*pHa5`6;E?9Im_!dqKJJn z3U$w97e8#!S8}b}-`MCnKyO9-DPVsZV^4FHkbK%~-EGf4$oA75SWf-4jO&zd6VyA` zq(fs_d#rOe%0HlZU@TUM&8Hco!-D?`*?-+(C$+))+D5wC8|5B@bSzSWdPm!2OBUNo z?}(e|_p`051z-HezM$_FomgmJKghnMZ@W$Or`S%KN6bXOmg@=4MPkD8KIpfTb)|UfcgG<3r!Bo9KI${ipj| zLN}-Gx_klqndWFQ*?%|W?_eG1J#r@xZNntCpT4O#>F^}`;7h^AyO{q?f{j12jr5(V ziT?u}12q4giGB_HnC5_U>aQ_4oiU(y<4yF59K(kN{qI!zw^VZtnB+gf_Wz3VrnZpo zX=!dclk9w@Te6y0)WrK2h0V`u9zYZQYPOx;`FHZyzRP6a{pq0_eH(1zy<6e_q>twM zFwxKA{?$CRAuVB}-jdOGq>b)NTjC?SR`~4)hV+(KTf8mO2~RO;E&~(K#}$_IeKbFi z(;n^1J2;j`xauO=OB+4^q`3}Ey!Wu5Xl@}B@7on`JR=1wx_;>`i9Q6xc9gQFE$pGE zVcMcj>N_G6zYLD6u^zh8H>f6E%kfSojg_wzV*V=j`-?)I`kCt#&7oxCe;eaS^GTTK zk11fMSU>pjy-h zsgwFP-K6s_ZYO9i3lsfOWz)|-nm^2>&o@DTkqi@Mf{KvBXG?%VPzbhD9nhU~2-^ull=6^HszftL*sOG0N(X$jj_WUJO^C>QA zqxTT#=rQQ8pNq6n*O5v!7mi7nhZsxxHqs>P2F8-UuQSodF&0mA`>YhfAKg#3^=PB! zx;DxGUio5VJk5980_E)M7rjqyzhlq_ZOLTVOYLWZ)jDy%OO76Ur7I}9#_Ib`lb&1H z4x0Daq{jyK8O{A+qTk0pqq%)d^amA=En*)6PNu#M`Dv?1U3p!!tBtaYzDYINwVCar z`G!n-J)rbDDxT&RH_;DsU7)#zP537R`AJdyk4a3NVdgWJ>kQ2sVUqJG(|-~0znt-> z?`%zWZelxWz9kd?-OA1^^^LBn{PkS^8|X)N>dpD?j@<{N{?`*~P8XA$dpQp18(b${ zw_RgZf5c}!>EUadr^s1W_t~~EMtOp-zEi$R_0e2art-TK1~2>R+hn5Crt1^63)e9Q zH1D^m{Cf7me&}v0KM`?|0zUNJvx$B!^QXD7O#JUx`X$BA73}+#W0AgnHt|1|+nQmn z7}VG=QTQdRxqM9W?_u9{h5e>FN@H*|ueC{brn3EXH7~k}cNt?x^LU!dU&GiO@n|1t zo@*y>ZPz;RJ|2nYDs$3x-0W1i;(bTRHrdr0c6}w-m7(lPj-`3COuRR9OwxJv@X6j2_30$sXY-sLxlZ;Cl+n+t^6wRCFl&`TF%`rpsMwxi; zP%-hO^_vH8ng`BQ_CAICb$*&hz$E)>wu|OYH__9%KGD37CjJ4&p5}RTme<&(GPZAd z_?_mvGV#8f>jJGyVWNM-exgSB zp9}GFIWXP8xVH0Gcm=?ce`#|pcBcZ&5l7^!UXm; z%@b(S>mIJdv_^=D{)4;?RyxqYPd8=Q1qS1xA1&^(4F z`VQ8S=Efj8ZB836VfnPKfQg>Q7^I|_`QN25?36(BEt=%t&GKnJ4ig3!LH@<8Lk;}j zLTPCK(bxGzj7qctmi8BAX+E0c!=&p4%x59Tk!ID{-y}YGKS=P`S6qK+oePsK7ctf} zC#8wLoozkMBgU8ggKjimvimT9^I#Q#F}&0G)NX&y9FS!%l|pVsp+(ZA%lpt(>@ z^gESpSF1S+P4qjIUq0~DJm5s9%~=P}Wxrkx+nsHz-3r;`bu@>KsqAVlJKw`@n(Ni5 z7yBer;C&ABzSz~~YrTjkty^N!Ydp6bv{sBs_66*(vJ^A@c5a(#PGx6#-7YTTc5%B0 zX0%3=v#gCd=ICO7Ug2RE&96@NP+#9@PglBlaBP7@F++3Un)u(Ya6QF;I&d}BE;KiiN$+Lgy`1?!>47`VwQj;L4ruuqyA^`p zZ)3mHIzuLG&sTAitmY{;(Qi}O_wuh7;&L3v<$e$ScY5GXb4QU}+MIQGDdR%(KACKf zMJf76aYJ*2n{=PU?Jun-W1@e?{+S}g-L1;@OXC*_{ws%$OIS}@W5{I3SjOjF#z)t6 z+hY2gsODob*%$+k2}oq)N+s%gs3{PbYX)Om6|Aj37T_Mg$_I3fAZ3>%MKdmKa zvi(!Goz{>r)!`6iH?aIoILCCoX$|=_kC#dJk&F$kA!d?)9{Yyo(|6K!yS)(cL*t7y zN4S%&+vZyowztOKEMQm9y1(YaUh^LV>}lMQ<|Q}rzgh7=oIvaFndtvf_|1x=In0Po zo3oyeVEdm*F_*uE+pq0{{t?#yeh=&q!ZxR0>?@D`{EyZKGU+vx_3D6$R@`=x4d2Jw z-`dfWZsm4? z<_0ujSOxyoEaMEJe$3|j5%-l@hg4vQ&{OM2nr!@-F+5H1(Yfp+n#l{tb)|tvP7oKT_d~`2?Y#sSTrN zJ`V}le!$q$JeelmgIIT3o5iGC1L_XF+d%79n&{`SZ_1&YX`I1+r^AOa0(zIjo}=Ex z=SKDct$kynZ&4UM<)gI$P4su!PMQyu=(O4R+uk^Z?WA=WOy!?pAJw@q*1l{Be(8dL zMzDWqoe&f6&Fo*Av(=#tjU&>*_IQ5volsTPwEHJ`kSV*!x=}K3)-Y-9OKZ} zgEy@mX5wAK{{2$G;6275W~^EE^$NqfIGS_U#J?ZgP3te1bl<|d)7nrbdON1m`bbW? zz7EggYZ$GUW|BXT?W1*Io#hGl7+}7NV}#bZGL=7x^`|u`Oyz&$82Vkn?;XbPXaT?L zRQU^iw3eGmevtLA7Gh%<#|Et_VbX6CNsn19r?=qex7p7$ z2eGMqPqyPc!4HQy4ry+BlYZ?~U(izPR+;Emb9tJB*hJr}^lz!wJ$CA+{V;_6K=UX& z>DmvySbtgr+r)nb`)0#l0^4D^Cn#?W1)PP4_d?A%6zzK@kmzdK05W(u`reGp!Lq3 zYXUEqaa#m@lJ<;XDjGX%zDt8WF|c}DxW09(wdbf zJ&L$ItsmpmgWl~TJ-&|W(ORu{ZIbaS*Lzwo-6=z3IS{r|`}TM@vyN9QpZ?{$O|Y{7 zIu^2?G;g^{MmH6^$!h&Jla5z#Y|`2mPP*3dbk(-tJAc8S%h;dy3OMGoel(xDQ$HOK zDO|T`txyv^kIU1VKTbc;eH!`UtEeB)zArZO=*lu^?K`K9T89k^gN=S#FU&;0it84w zdGFLwV>AFhBtO#HrcS!%pQ~(WX{`|2k2g3@daw^*mfC1uRoYrA?g?tWOp|`~TxV$> zd5w`R+kR)aGwkoe7|{BvCLME>j^#dDTg4=!w~7PI!y#aJxx(;v|L-o0GzR^FPYP_I z`R+~fJF$N0LY$q&aYk#yn)J)&IHC2QO!O~Ve_AunM8A@KOzU)+^y|p_y#x6sj8B97 zDXc@QubcAFanes=kf7FHH0iLO>*A+^k0vvJTDQhjz8Bj_YlWF?=)gAoONhhQISymG zoA2Z3ewfzlHSwOry3x9NCfznFeE&?K^aWRv|#;5~u)(|U;}{#Pg;B*hLF>^qt5qjf_~<<}|uR>#wt%qAUL zLVg?}!lhuW;PcS~@2C z#<1=S1pH4@_@}7#PEGPJXZ$bm@I9@WW-8mAb$>+g%_8;@X?KROa<9MaDq)fUEVcq%(K0Ba%_N+zg!khRn;FzN| zLrwHg85>&T+C;yQeX>`uZy4K0>s*=iAI$pyD8xhq$HZL%zP%Vp<&hns{_& zjA^ZI6a8l;BPo{Fn>5h}F$VnvAJwyuXw5h(Pn*+6C$WCCw!Mk}TCT^mKAp+N-Y9<} z^H?F^_#)#-Ya5$n3}hRAa{0;D6VE^4rxa_vkJcA9mH&h>q&3BzbbbB0M8#3ET5FH! zv^g>8$@)(h;Zkm$5IF-&3mlD=t5*FNaRb&b}FHu3++ z_F-&4w%?Xu-?z{)tyR(qexP*>O&E89{I0A6t&MEbu^r!^mI@dyU<~&P7@n&z?C$Rh z8=X3kjj_|cA}tv7ZqXlI-Vx}$FqD|r`=@V`)uE3g70eBci#&BIY;?tW6WKy z_|^E_3L>?QhK?SFi2uv+wT&5Tg|?mCSZHM z!Zu`W7VLjl*?*~jtziGrY(K4QYtp|h>%YLmH`aY-Z1%BjZwPT$#BsO8gLfAJ_f_2H z(Av8uy^m(SHwt#QVY|=qkbS>^={pM3fWKDo$y$#07X+VHX`c$Zw_)A43c9yu-7kjj z&h}nk)7s+B{{+M>-T!wGeDf^(hSn-J;n$jV7%t$yQsIAf46Xl8<@I$!w<#UD&H6yV zppr4zDcG3IHqtuECjDqMv^C^zcKOe~s$0tT3_b%$V+pKW?qz_bHndc=eGNdn$s z-98DjeJuM=@OIjzNFVx88#7Zz|j0^tWc+QLR)Pr?H`kpt`nS;2+2QrwMhfitE}h zuDHb0Z}!a+h3#1XTLS+W=08Hfud~80 zDfW9IcFs|;(=m3V;QJ@p_uT~jTd@BAL*{lc1-5+*-KmWvOr2W?(jDbYZGSt3yJhti z>KokFB!f0Y&>?|!XwlQG!`TYkWM4PI zMry-T!2c%!pU%MN+3{xHi_8TZcxyBgT8^99^O3b(FSd%^Bru=@>UDKeusjrn{Va<2J=2F-^N3i)AWi!4> z6tFI3tiKU_xrcpu6)x#eel6ODv{7Sfse*0&VcXGZ=6YVwxNH(|&*perE7%_< z`vsq@V4uY9G|PUB?R#B_iPJeI77F;bWqfZFbU$9{eu6(+z-1V4c~bCA3H#<+!R|`f z-ABN*PGNec?>s^8qgn6W&>R1w*9QG=>#GjGE)=j`uCTp3VXk1?DA?9f!2MB$d#djV z0kT+;;X7H~Y)3wC9|t^)#YuP|=(H9^c7=9rt$ zG4+UG+b-BPOu+3Cj@J!>PnL1KzboLH&$!+$VAq=C;#Xj2s_(Oa9nHmdf_g$w5Oc18 zQ5Gdof_?1;1M61`7(EP(9u{m!W*f={`86zmqoDigko=>7UmoL^ zC-~=K_RkK%KW8cb^zpYkVAngCs5ZTpM@Ou;m4bD0@CeIl?Kp?}l^UO!?1L1X^NE1Z zGr;F%;3Hj2V&GiUecH=x-^~Ix(}2xjA+GZ|t~;>ax-YEtE_Uess)ugN1iw|Y-`@5p z`-6bTYrx}uAzoT>ytEcD$z@C`1)o2}K0gU|n=qXLyF0>H_Y0U-0@DLRye#GV9Cwzv zpZ6y0S})*L#dvjwT?prB8?XKGlB1ses8&WLp6ZEcy-lqz_p6>AVF9PmKjQb6O z{*|o%`2y|_GVaF<*!%=+1_(aQVV`~_;J!rRK0D@Z!S=IYdzq^q=zjm1svaa;Ex)zn z0njV#_5F5BWp{FHPeJcq7^^4}aGmDBHBGSXMYe61fa`pP>l$mbfa?Q{>(_$qD`EQ} zLHFL!y`x}vBKtc>(EH!4x4)OU9xdj&FyB?rH11PXJxj613c8&L-ChtdIg?}U55ea1 zVDlV7?-y9_fr8yfvE7dfc<*Msvjx3Rg5K{4dWTu>D_pUtV=af<oGX>9koLagSqKe{3QX9~9Ufo+)rZY>zMuLRpG*!Jaue$OlY+9sq3wp|L_ zHgGIa9i>gr+f98OYWpe#z57D%4+MYiVt-x(-p=c^=o{@A?P3ZWb`zuP}}AKk5-H zdzrWPv2F_|E1z|0@p0TdDd_hc$8?!s`@OLJ zM^{X1{Ij_a@V?7;+E3IUJWs%_o^kusqwFh!&XZW@0Rm?CF=mqm%t{%vg@VnyVDr}= zdfg^qouIHTuzv6;`-x!FKDOyMl-1+t6sXjXrOj@u(|l{8Ggw61a7Dx)nq@K`@f?da z%4k--8(p|-pVcYc+gN`&en}GMTjQ1`VPNZ)P_%2X5CB+`| zN>l!tK1Sh$xk0&2L=m-VdlT{N3<1M?7{hPaHZB_VX(DDWrt9ao9=w+c_|IefTcXi3 z;r5KO`%Y^Va7)mFv3KhIzTlHM_Q|gvdc7pzSy_+%hxo z?y#J2ISKeX>yM7-<1uE~mwlt_la3%1Q?+cvm#)7R)Hp&RuDeh^~f zPL7GQ1#CJqHbKq{UFBvLP5V1Sih|< z`*nLTfqnCqhwL2aZt@G|ZE(rexOReET9dJxfXhtAWtrgDKHN^d@1a*%@Og#udB6DN zU1p!$!9KlDz~l+WWIF6N)%8SNe_B|HJvO^+*O+!>%wG_EHiCWjjfZVP$ku%b;!Eqv z#{zFZ`qnfnj=dVYWo;95=)gLR6!4$H_^%K!Ea!R?cIiRaV$!1p^q`r&=>GLsYpL&9 z0n0STa)+R2d)D(RSG!1@Y3qt~qTt8d*^hs^aMahqM0oEZ$Th_-wJ8l8*IE|Iqpc;{ z^4Pg%-%sIo{wu+^!`Ziw3--2SdvEoyH&5`{boSZ1z*@%!>8tyfk8@jQ_b+=TY!rN3 z#=cDzaJ`jr-6z;^Jlk*&+u+2HycQ3y9fXdAv8L%~A$wFjkcOD=Q;@$T<~V3zSuaDr ziN|qDKE3g@!rJDtQMW%KWn&xbZ4dvqc-8FVVeG^Cf*+=_AAWVkyq5hK`{90>!DmC+XKMwYO=X|`?GX>1pt~vF z9$|mBfnA3LTz$ZGsRyoa3wD>V-75s#7ja!^xx6W^I+l)RT#AKw$l`eD*kO+1?r0hzM z>ml%O&HP&)wr$fk!6a2j{qX1Y9{#*R@Yhw`cJ}wcEat?fzSK6I&Nl54FfC$C=Lwh& zW=wNjm}veFavfYL#L8rjm3v%vYiy`ZK1sl<1@Oucu%><>#mWJ}u0mzk>;85wTy%`) za~+Afwz;ps7X8db>roG#OI&zpz7KHhXM6bSNe{kxg1xQS-c|4u*{$)@*T9qDCwnXv zYu5=pl9jMH+#o}<{FpFDKB*TdGe9vIIOFwS9& zj~8&tS2#WEe`T&2$B7(s)^2lL5snESwDkfOEf|YC1T1$lmQQ%#akk*2mhjQF9#}o+ z!E>{qFSWqb79HbZ>p%hHJZ=vb2|mhZAHC+GS0@j-j{`$fou_A~+XQ=PRW7pUDaId% zYSZr(v_!3@JA?|Mj{JzLMlY96&h}ux%dh$xnxy=iY~ADGpHEykXuk9|=YQv#<1~}w zbh8i>Nr;L5E`6PC!6kxkrYPSe#hxbkHkW<7M8N4M;Pj?|#eIy$9>GTm?4uDLdVdeB z$dB3mR6rS4>-WGh33}3WDV`9RyCAZUOdc?>P51sB8 zu=ovF)Ohgx(}U-w0)|sKo)>!X9VFn}i}CFO98C4?9jRwEp( zX9O&M2NtV5bpBBA>tyBEh`+6eJ{NlM{Y!`kn*C>zfD?VM)e*L=^Wb}@;GfgjKko=Q z`~e)U^x*rmfWst(!xsNIA%=G--`yWSO|Yp4+jN76?5_o#|AfvRzqfs^+i1X}?vdL7 z$J;%4E*ILFJZ@)3!LOz|8Ov?&4)8Y7&*FBd^+{&>M1>c=;}!gr#eSM9;OXNwp<2MR zJL5S)&_71$pJe?lVEQ*Oear*5Y>#*k2|C}+I*$;1bu@hSl!weV*ERPs`PsrdF3)?E z>m&FeQ~4k%cCCP6H^y+5hpk@-{+rDHdsB#y29A$a0#@4c~ zm{9!b{+q%Bf8sFu9*0)2(nI$`!4GZWhZ}*3J$`TBf9gI@0x+Rxf%Hafg4H&0l7MXn z$M+!*zxXoEbz>4^x6DKL9|c^i7}p)p-+2wt_;f_>76J1Fj?Y>Fmt%m-B_29;6zrME z_5=m2(iy8g9y+&tz>L*G9(%o4u<0&k(|rFv58j`+Vodkb(zy-X>|yi80#?TYt2aDk z4im7-R9Gd&mU3L{_FU^!g13^|;G7fzj|RqLrGWJWh4nje+dXWm7G%#;vMuYN%PxB+ zIhso@!FpZr!<`&kPk6|^O~CRvV3{xAmBF#KQqXw@>pW7xE5dlSnrDu$2^<4Id)T~5 zz&efN`cq)ytc%*G$hOuBdIy!>iPixRybcLi-=VM`6c-XOOIMgpjNjmrt=rZ-B^$G! zz3$fAZVSoJSE8)RrZ%uCU%@1l%{^j?Fx$$|4klc_Xz$Q z&;A=B#6}v&#+@F#zxS}Gy9d5!3%;Ac@xIeT<}ofTv|n#iSl}IX!9R8EpMEaAoY#(r zJ>b8gSiP|3c zZ)6PZJlhhYRdQeAJ?LbrTXVT>9W7wdLSb=r+ztWf@r-kC53H^ibeqq*{a1*Ou_`|D z{L?%z>F*);Yqr^q$!To!X@bq$*#D&-a`y^&jAJ}Tc<6Pv7&|Fe55dPB;N#x}dve*H zG!K~zJZ!mGh{3*f4w<218i5PK4$u<+_q2gusfM~ zyJH}Wc|YgSjn2{I+EkBIP{WHnc(3-r^fLjoQyH@~!OtDx=ZFhW-S+zxp2^nBuK3aI z!Ew-ku7|#z1T1nC7Kzqd4lHyFLTyNx{j?GGn%a=d_#QLtA-k90<1^R~;{+d%Q9k}E zeys}=TR+RXnPcn^0q-#i@5!;dg%}^pF<$4Pdw&79$*lXA0&W$I+g!odo#5-UJmlU9 zyy?2D{j2X^K7H}P{a9v~l=FlCYaK9Z#J7`WlM9W&^q2DnR&G9gr zWBdleXE(9W{&K}}<25wJdO)x}&SCo`A*SZA?XeFwy+&&ssh(WsVV4i(D9&kf)`N2P z=L*4}G>6FPfOmMiwgwYM=|cFIP`MH zT$zfw|HOVS;Ob{w7klVEMeyk;_UXFzv(=Nbtorlfa1iVKoyq}3lcthLNG)`MEp5+2&BN?-wfSI#x*7Ho=0L(fvdtkr`5MzH^?T=?pG zPS=JLh1kGMAgT@fScpmb;;Yt!?{3)c1Ap4I?bN@T$aeqXQ4iV!6Dq6qw98voA>$si zs%gCF^9YMYLOhgkJnV7#TgO@(tF4s)z9zW|jPphT=iv(HS7P>xygOU(3b8a?*>t9_ zr64<2``82P{SK@FLG(I-??GV|-p>(u$0*+E@u#@(*4L@cs!eQdo$tWZ$=`k(2Jh5E zzBA6LJjMBV_&QDbyRDTW#91Q8*s>xaay+h>2kw=hu1o{8N`+x{i$H`ZYnY zc@z6|i-2iRVVWFU551i}wQZ++#zbJ1h<_)<_66=v+~^$Is2j^*FyfshaOWeC59)_)w<=!FKHJ4*0--V0DEy$RU1aDIt@E5EiyPg&NJ(T_Z3+!@Y zV%G!C9{3e^X)=m3zH2ZR)5DQ5h3!?;IcZaVFAlKxN|FS>!33i{scDE69 z8^gL)3wF=tc-YLi>p0Z$(wA}nOo)de91r~j+=np#GhiRpGrh$#%E{2*erDS`v7=za zD7InlDltb946akYx2(Ga4Eitz^9A`oEBQ&*HG=^#&$6K_c8&y0Q+|luyNKK9Y@rb z40qYBV{4?c+h;8kaQ%V(dPJ~&7TZ2f;6Fm~x2)#{zxHC=&lTjK!tK>80oy^0?QX&E zc>727EpHOAyH>^BcfLy?U&1aT#6(ZT#1zz^#JIOW=c zj>Cb9cd~EuDs%b6Dh|tiuL^#~48qFKbA>n@!nPd};;<*j;pKv_d%@RX0lPuqJ(&6T z@WB2a!M5+Yj_nt4zlPh!aY9TCR539=p^t$5495OX*xrKuf*t(iQ~jJCu$}s!dOo2G z1Ro7hK1z#;2pA7w3?74>rr4x*=#b#QHtautsksdt&Hh>LlC9g={z`URt5Cpb0N0z3 z1dQh>+uHiac=+#bAvSs=Hr5Dw^<%wW0w$(<@tyMh8f&Y7d4HuB*1!{R>B_iF^3Z#o zpxY^|Tb@VR?vQKpCABjx-_*WpI@WVE`|^l@_0=k#&-7jA(o44maU4h0f-n2CFYg0p zq?_Hs;2^rSv_4&>b+hjiH1EEueH|Qsiice{3VNN)dd&u2CSOkHdiE7$H^t1xCmFfl z2zvG5>)dNXjGc%WyVa$aj{Uyu$6+2ewLs+~oi$b(kJ%hwKL~oA#CmlTeBG9PJx++v zE*zgbp}U{ulWfwRP&pC0pTzqAA?R?T(xKdUfe;V#uQTh{QNZUa6|4Ia z_6XSbVcl~C{;ACWEFos5am@J7Hv6ZyvOUilA>e-unEjD-vb$Z3K?{KXD$+R~I{z-{)s6L8hVcEP~dFNUfm5#U8pZ6ykvHxaqmw!yf*4L-2E7<>$ZrouQk)4r$$Jul0)G+(_4L zE+L-6xLxj{_v3;ui`bVv7#EU9o1PoimYoaN)ddO`LxlOO#gcyyfVNPUMEa>Ma3pp0(oL4;jc}VbQAFj9a1iZR3UUzut z{f^+TLiSf4V%&ZGC%Y0b%GnLq<6bVjb^oA?!rQX072=rQm!LR4Pw>YKu7hd7j_y75 zMl(>+X2**kQaWLDuZy+Y!^V06pDtXF54y0odl>Z2MT#{U@~NHC8$Cy)jqZDrfDF%R ziO8ZS4IQBk&FY4Ia?BgD`=0RU)-PX_JNn6sq z%-~Ow9nbz8EBLcB`*Y3N<~WBNt>-=TI^x2Z;)FID(>NbI-7!Yg&dNV)tQ&+Fxq|C# zdjZcA6`s2iVjgb9#Ewr}`soT!%bLselk}lYw@LKeI7xZfvQG2x=Xyc^Z`D0onQtjy zCv5JGed%t<*elcznuV6?#|n?KI|N)dC|ut3&2JXF^p&F>yJT6bX#7&_Qaz%T-w#_w z90S@%HfESRlVjjR4?mwH#8;e(uXWbZ@P(8)bKvX-K}Q!m{HP}vgTp|9myFS-Zm2s}D5ChG*dW4T@}7xas;eoqO$ zyj=P6fbRzn%s&ufs6EF}jZmjgVBOF6uzd^1pT?DZLz@o~(?_UR`CP9agg>2_Y97>9 zEOB83Y!;1~P`AjQ!C1wrJNDf@blM?cQOa0cE!caEvNzEx z5`2}*zPeA~U&8#Wg*fY};;eg29}nFh6nM{L-Z#1G5c!Jg(9zJnE&J{E@@{c-s&wG0=Aq z$I3ndkGYJ;4j~?>-`57#ek$-E$ow;)y9tw6ZZ{4H@&hdYA;G6~zesmS)Q4*?;C`vX zJt_85A?AAUwJzeokbJ58h}7Thh8TK7(D4k`aiOEU9WQotWGf$J`26RX+s?04Os%pG z3m6Vy4Br#v&tdr^g_!Jwn0!X?UrWZWz$3QS2zE{3*!r)acYoIVbLgEwfbyp63|$Xi zcKKCb?^-~wqm*t(Tm1#UUc&Xs|Fb!^`my}&g5SHd-`{g#qpvH8EPp<*(fu4UL62FC zLhRE04&CeY7JU09w-?2NZ%%-3+6wxg!uoFk{wDmWZ}>TMCz{5T^rI~Xx=$8xyO?pi zS@7#=?AQ2Hn##3%A~YXvlGR1f`DE641u!$|OxO87LfO7t_C>*8-ITxD#qDzxVkYRTu1OV=Tr~5m0X`S#=1>7LEU?IvQFA!<{gV( z#?g@RiGar>g~w9sd;!ntdW_XWx0!<7y_MbBR!o*zx328#$u8d7#w6(d2rx6%5vsS} z2{uh+ACC|)iD69gU2=83rS@c{tNqk?f1%pXPFA^Kb1(MwwE~_OsQ66wJtxG%aE^s( z9=e?;#6l;Ig>i!2)Z?UB*d@epSH$oi9(B52u(_wQxwCb#fZ0LDEDL&(FSUPhsJ2*K z8(w$$S^I*nW(kkj@k1w5S^FQgCH6erT{ymiODeGT3=muwG?4vYK;m&Kx-|-pyDyV;reU zO^s9q>O&;(rY|X2jPrLb6 zxVkj3sHU#enxf2|7$_j~(`%~hBf;uOeR_VegAxfw!Zp=VVw92}&JH3jHH(~@(qYyT zO95$3NVg)Ps#;RTg#an%$|^~nP1e9`C6x`OA@xI60@v0%^rXc};T)^RoyO$2l{yGP5(9 zX62+6PSH6k(6VwT1qx^6rt8d{wEV#2yuu>=J3A*cCs2@Cm^CvqP?Q(QNt>=I6SLDM z6(U11jH(K%4Np_WGpAou8hamSZI3WEB+T6}Z^rXIZ#15~^z^ zY+_b+QD%WHD?bmAvM^_Uc2;g?8nJZnz%RQR27-}DU3gvtE;u?|Gcu=UrDq1xii!%d zCKMNCQusvMxqL`d{wG1kGZi(Ek(CorLH7^m%=ka>nJJz#sUR!kA4Ek@ktIb>kp&e_ zPAfpnqFUt@%m{dc@V}f}lvk9N9Z1j1Ei6hyRkQgU&7GE3kRPB5ZKRTm^0SK4CyUgA z%t={=h?T;B(Ek5=UOHXy0tIPBW`&9}rxz6$Ad0e4+jG#27!{k8UmVE8l_A@%CCy_6 z?M_yHL0&p4R9*paDi#<`$t=jt%noEu&rA;#W#we%6`KX3hGb46rBw^tOkR3gE~4FT zOTCCp_#D|96D|pTwJ7jc=h?Ax(Vnk&Ua*GgsbS+ zq`Gh^-6dG){MLm^b3#?ob0am8V5RQXflyPo#460n$(R-{jZ|1wI5Qk6sj%uo<&FGH zNJ;@Z&FJ1Ojx^MT(r}kT-6vKHN4igCxFk$0&Q1TnTo7F6aZ9MWArod4*=}&1quM^z zumz-3(9)oh^yC6ZqrWprxyB_RAHA=}&S$_yosgBAh8A5u0hP#z4QB>Qg6I@Subql} zdKx-=f&7BZiMUwi=H;p;qB+Gz2YhUunv}{O)Pz5Nz^(9eWJztKF#BpzG$TJV+WB*2 z*^L{qcVwz+(S#d*l+;w!q65&RBf~A6>cluYfX?j9>FL?Ug;`UTc}@Zv?8#XZvg}6I z!5pH_3`YVrwbXNuLUvjfu6wwAMK3(0GG*#pt0q@lB1}um&Q`6wJ~JzKVqPFSZ&F}F z@kC%QWV<4WIAv02%TUstZz!6VkLyg<%(Noh)C988G0xUvi_!`vWflb{7Ubmw(u)z_ zbP;Gu)W0Uek_lX_l$F&F(XY z7|t9_JV_5*H2)~WeeJwp$^5A2IKolEhzI|&E0n7Uem34fm51xmm7Un+775qn{K;AA zh0!Po*f%O%dyIu0Oml*@`gTp=OILMt%kzp0(BL=W9f-;{oI^MFfxO&o)p_(faasZ5 z9bM7F^t1x%TWF2lnt^2BN0cax>LcHA>3J6bQL_d{g3-phZVp#3&z4$;wHa z)I=#wXso@_GMt580|mznRB_cga0;|tm}Pebq6Ld{&G~j$C5!IL^Yfe9sbw^$z)-$) z;l4V6eza4*E|!*ldT|!+TbwjP)x=Kc2GR=c&Xc(ik=2c+YG$LhQSS$RggiizUPPTB zXK4+lUHhCA+@@yY3W>%NJ^LnV>N1nlax9pPk*3>BuSdteM@;L4;1$v7XQyL9PD8%ScPh&%%IieWbJmmvA)LS=HePb{Tcyg`v7h zp~%!wT|Et^mZ}W>3nwTa;{*$L2^h1rF!CI(4ibwfDIEjQikUt)mmYSgCmxj{unaAX zo}F&@Gt+~$!Fl1zaAXPYbIzo(^>8)*YMw=OcY}b5W7lOs47i8`%pvT z38mwM!;PYBDU+Q^z{QAv+^dGuf79 z|HyFuNTaqTtGYBqQD&c?Mrq;!7rX)y6vucUtIC(NPUro;gi?rw|gEOq!^qN|P zG7A`-QH;uo+xn1lwNi3OqgG@_T0No^N~i}P(Sl@&P0_57!{_Q8QnBedP{vgX&6?t` z%M5Y;u19qlX*HBsxwc&TjbZr)70ID0`iZglhHxWwFwR%3h&Gr`cKpJ*B@GnCg`tQV zLMf~W;tI92F&7yaI8kFL_L=a1V`~vDoK83J7)>b)ms@!Fq;?JJf}~rJ$#~$1=TEgN zoY1D&SE~~HBo$QkC3RsutwcmA8rm40QPJe&Xyap-9g@K!nAeburrb&z?AjJR%l@gk zR7C5deE=l1w8jj(8maKjD=Pz%0LV>6B$YyC%dVUbI$!(Bsv>m#%g4=M2oD>pYVlYb zPa6%K(Cme*QjA~)sr`=1nW&y#C}LSt0^J2h&m;%2zPd1Fss2T66iJ(FJ}hBSQo4UuququB&(JU3hVr^wUC-)M^2gJn67>LOj~Ul zervEVzu7@VFI{--0~+wN%JC<#NR8vE!wS3e<2*-Ku}pfNsYA3v{Q@twF9?;;GvcP1 zr#FOfec&^iUaV9h9aVfrW@WIpK2%zSC(~AKPPno%jK}gd)ur{+dIakt;9gD5jNOz( z^@z4C_JI~^|7t4L&TAAqi66WcS7YQ-jS=X3dWg})!iIS|7tX}pb#17W1H|41DO&7S zvnpJh9a_>@QRJHy1;MNk2u_6@HwEf8AT<@2%;?>RkxFA9 zxRKj~&;_7C5rG2jpMauUW4Aj|OahAZXm*>)!O`Sqv^iI? zT(dh$b%!lRXd|@=@QvAKARFm!AR8&ofo!BT1G16S3@j!AEK}q(QcVV+kf#CU0UB9w z0Nlt>GytU}13;1tkUYu)qaH1dKCw$pH9WG5K0CvmwetjefY*!`eVo^n(){6GQ@ZI< zU(+)hAN)1VuhNeJn`Sq8DA<(L_=vD+K6)(92ZsFGQ)()Ag$(b2HF>2?d9nAG_&s3vyw%HNG}cA0L`=+@q|Oguh&3Y(1B?M&S1?41@iscb_1QWq%=mErp9Qg&VgI;P?306kW#hj2F?5rAQj zC^?|tc%f%*g|;zx(@njDr+T6E%A5L?ikqw{)FmhSA|RJYU*ps zA}I^Q^$o$wl=?_RX}Bh3RR4jq7nVfd~L=)eR5_PdJcyE=~e5aVTZ<3u1(()hPovTSC>fM$^ste+4~LeSg|er{3svP*pF4?@MU}k9eqZG{hIiV8BvUqEUc4vTryh2E&!8CXHqC z>%uj40?p2*y8*l++gQZT&TEL|mBG$v_cy8pZjQpGHki>7Cx?!TEZCM+?I_$Rk+Y}r zT~|{{s>L^x3YS#PtD#!~E&*pvP%i`L%{wzx5+N{jK+y}os^ji;0_#Bz*Y^-n-c3}O zsJI`bR;p1WF2(s1!g$NM8bcCVVJ7n+G;W*p5QF;+TxV%ODqIJ%n#2U(Tj!MlarM4% zBXbryF@zUK+)ywrw|xC&EQ_n>SJy0Z)B$Kb10$4Gwf1PE)4g_IL`6zbO-(jEVQ;jHHP->n z!#`1&!L|TzXVblOH2UfhO5u_)?#S&y^+p=3%d1XAI5OuJ>XAZ2iQ?*d59&k=Dcg-e zGf}zp#X~bCC|a#WD&Ov}(HVA}M)k9QQhh3_i8iCoOb1qWww=-JY@O{mU$qONQgwr` z-i=yJZL+mQ{Z(#U9D(mFd*r3Uz7;4{kA#Sd%eB(o7CR&ZLpoLUG&)NJ+F{^;`kVMg zJN%ljQP%?jI+zrn#!Sb%5Hcl-sV;}MOOHg92HlzB*%Y*S)s=SKx=X3+YDT!eB#40{ zvRR*irwO5nct^pQ35#)=Sr|eO%TOdYRE~y0qUy3UJj;8idJmmi)*yP1yP&MuWBJ99C<%BTna?(%4dD#G*7k8<{D zkXdU#$gv*)A=a%$!8&_9tnoI4vLlWg0(y+YPY3fBhUzeAXjP$m7v{FErp|t5GCx#@ zC#Te!a~C4Bnwqy#e9#gJm7`ljx&*7a2L&PYPGgDvyil1;_g}WyK^f|0N&5#zt{W+M z*3tL_&mz>gF>WkM?5B8Bln!=Hw%Z1>Iim)nsOYRKkW@<>u$wk}c#?mpJ|!PFmQ79N zXL`C05u>J&GjO5gD{BQ$^AR6d37UJcaf{i~bS zc-QErGz}Uz)qeEbESDbzH$|*jz`2W+s{1q@fNrWOAl>JP0qQ={8L%$LHVVAjZv0QRl%x zi4Js`X#zj)j2OzG^`wT|K#BHTz%w<~zGYR1cE3R#(nD5seHD+?RJ+sU%;64{Gt1GL zq7Inqi>0R4vbBxXSj8i+ImX_&&6uXTLK7QN^@eow>GGIgpW zFTFB|Pd5o9M`lh~eZWe&%+>L5lyVz?s#{f6zA-Nw11k* zo;}it4=0QB>FqyypA)Yh;-$s{oL5~Fs0(2rp<0cA2KC!e>Q^=1bD^(}@k1Y?L|F~K zYK;;C{ADuE305u&E~)3=I8nVzqVnx~K|R)|c-zB%I4g|T0qW?Z)eB81XCENPzFHy2K0uDTHsK3uu?C>`+*Otl)8!6kDL-iJ{;`iqxe>>d3bV*eOo z(T}0_kD<^PP@@p<)egmYuXZT-|JZvI@VKh-fBYtGlJ5Hgw7?J$lF}tf_tF9-Nonm$ zleR1=<20G1p-CppOu8wG3bF~fprVK%JBoq`!dFF6To6UY9R%3~{JM)@6czRNdCz&z zz31M!@5xMBeE!e#cltClbI&>N`*}a_`<`>|x#!;DclE|?_}~JXZ==2Xi$ApAhW_FY z+NrP>&6Fcx%va`tN-Gw!ou$^q(c` zkLDn2psx*nP1ZyQ=lcg)BOP4ebz}x`Hi7+|>b-&A=kq(tyL<5ke`r+D@B8?DKfha5 zj4KnG5iN9qYO3VpBU+n}5MVy;F^Yq_&}i!7qp8~vRKQ150T;LTY95tU(Qo@gW&1kd%SG2xX(GsEVvFmRX7b zQ>4DAI8xM`PuSu}v4vy%{ZD&1#K?42s*ssaTn~Cf`xH8oCQSWi3a4roxI!t{!A+hr zpV(HEYU))rf{qWGa(vLdsXD5iV$XbHSvaPvsX~&V!1D)I6Ng>NQDmA=T5nuklBTNi z2iEQLt<9yRS3##gppyOo5cL^qdQ^Qm+g zo2NhQqn2?MO+8bM8Y+X#yY3KyyTJc`3Gz;Nm&(WHBfy!DdvxC$C&X0Q>sP^UcgtFRuRv3o{t%g9 zp@6pHW2`rqLDZzF5x0xbec-{q``9baYTV|>t|U|a-MTGp7%|tWF+ou+X`->Y)xSLy zlMC9?XP)tb^KEsuqOMl>XKpFl0zIFXpHj34rBNOAw?q?S`VyRyk|cH6&fx6S*iE2)PS7kH30Xj{M`W|W81lio0KC-|T zXyc>wF~C9_TlM*5PQTejMznn_4hJus6jpD|@I}R3aBWdTCo$FqN46(tu0`wbEYR%d zETAG<5bUltRU4WF84XRoAyFv?bo6)tIaHEU8-|raeB{+9I9*%ErC0+@#4zp#}5!>He`miYEOCZ2N!~$2+j(b`Lw}Lt-4Ni2qRikxYsVqmT zV@E^eC+H(>>L2fCnL^ub?jHp~rR zi+b09TDHekPj!@5;XskjEBwoV!sDd)kb2ydxIkaz3T2OEXSyI%qWV2yRr!W zX8nvXkZmqJ@$hB7s_1fUpboC+nM;v?C|yW2RrD&QxfEEinYmCV0754AWZSzQOnZ6n zcB^uxjutBwR;PM8J-&PuO%p2PD`Rm*bax24mAIk>I6LR(KViOr2jb9*!n#d(Fdt6- z;~Fj3XB;QGN*kP|_0CeMP|fQXNToED7Yp?%fvc$qSTz*^lUA{CfOicfsFq8)R7O*G z(I!`!I$4mUqM>Xpog_FKie11;Xp=q@aBCLr(q{}}W8+~} zO{(QA)TbI!Q%vl$vfWfFn(Zvq=M*ljA|t-1Nr5CmZt4t4jD&iD#{TT6j+DHh%8yhlFREzR1rsJ;(8!*7Jqur5?@Ch@FJzn$q)7&CB4M ziUT)9^immF_kz;9qV)e}1 zzf^vTZ8CNmn+{XYt@XTw@SN8se$MNM1-p)7yZ#~gxe9*1*kRXw0@uG(Tz6K^ad3U3 zfGMFc9pN}nCVM{f6Y+-y1nxL zh~4HH%Je+zj|;l1SofoXZ8O;?zh>J^{2AL0<=lOfBOYcx9?$9Vh@;yDTq6`N{AM+N zNF(l<>04BOG*A5*aC)-h^t0u63i>OQ{&C)41)n~vd^)cDIYIwn%-cnR z&ko}p`>w-h_Y2r6*=Nl{tkQ2ll)p2cw?o)pUlwE!W4reYyq&~3_B+AuL)q>`#BU~t z-=&{_?u}u5=nn8Q(d4P$QlsBLc-FzotAg%BSocXnyv;_ueOs`5xU##_yHK$EM7Dc& z#4dxpf%}i?PP=X>ze$LPLpdIPEZ{3=pAY{_ykDoY?Jqlc>k{IHekrTkiQDs@6mXR> zuH^zR9`o`cNB=NhuxT3Gbe-Un73`B2BiKx=c?uh5SaZVjEW2NvJBZ!CiO4qoK2!Y` zi|4Ho?7oogo*~#am2LZq5D$l_co#or(4r}#}= z9)m017~hvqtosr{ z_mixfM$TnRl__3D#c#mMy z6KvDB1y1HNCoc*ybTVS-0YUbUS@s$shNh?(`blMjgR@%&&M-5?KhIkz@Unn;IZxoU z4xBy}vDwVe-VU4Tx0@dp?D{F&b&VqqP7!d;XIvkTv^9M(jT_&C-nhJ=^N;%kK1MPh zm}TrLvrUgZ(i4_BZ|ZXSFw7;A1xP zkr()`Q+z+*y&!N>$(;Nsg593iEE2eG1iqgOF>*A=$jA%iocr4K@j0~#T)raUtzf*@ z2|k>teE5Z`iv>RBar}Kx(0v+oPXp$-*!z*nIjj#0zIYq^;-{zoWzX@Q$5c+F%r8bC&uAQ)5N)jK137m5Vo~P z@KZDVR13UuI;WxiKd+Csa|^KEC-`p;`)|A8pSQAqPUZL@oYdc%arp--t}DC-2d;Gy zE=<2oW9QEVzmDheZ{)}0ZQ2T(js}l57lvb6!`>XhYsQGjc;Y7sYEQc+p3$}b;*)pd5T|&I9LcB~B_}d2lPGPLZALLtlRe96AM;$hvE#Ubc zb6YRO_au(*^8|j+Q~dT-9wlHt9hjdK;&?X4@vA~SjZ^V7qH2p^^GMj-1l;ymO21`G zFPe9bfo-@Psb9+glXJYE3b0+ z=OMwCH^P>k0uSdh4<8crKFT?oi182ko_0Zu_P#NEBf{V@cvH0$NlW%8o{1gWzW&W9~HQIGjr7_=pVuQZxC#r2%E2TaP=F3 zj}74CK?ffj1UyxY=W2nE4D;~~hwL#sbt+K`rod~(b!lM zXYYv|?{_-vIY!`lGkES4V&_;DJBeYh3b-EOG3hCxzZr=+(j|fqhOrN(3pVXjHm$Gl zF4Q(hIr$dr{sL^a$2$`v<=$}otMK*+w!MM#;-?N@RtUNuW`8UbIBfx^V}-WgWZc&X zyp$_mepvNL#Fr-ihJ%P&fwL7H2a^Qd-(cO}6=YXH_OFEa-No_yn4|5bj+{A+F&R6| z_&-_o6Bl|va>(5wJ=dgdizwWEI8JaI9ZauFkNA#&yHck``5V7Su5C@W?L)Q$#IrKoK---d68^-(XkH*b}nOok8;Re0z2d6 zPKMm`1$!P+_Ho z@vH2!e+n^n6!$A_f=?&1Pj3`_GoXC)jj}@=HeDfL`wC-Q?Xc@`huk9_Wg8eAF#Yoc@Ib4F$2oL9BKZ7G@cFrd-0>{;I&f40ADfr)JB_nv0&m7yeP!}VTk=b0_Jgy`4+*i53*k;e>vVJisc0k%+t^=uK!7JtiBt4 z$Oy>L{SsZnFmr z{Ldp!$UYO3_^4jxxN=m9|DSWj!b%6ORgSW}5JO`)hOQ9!AU24PFABc?GW&Y1Lw7~D zjsp|ZY_2y0F?L(Tmu8+f8a^yno;+Ila+5>;Q-VL1@VK!|@W&DCkB>xbH#txFPX8Zu z_~T3mt{Vioqgd{bAeXLFm^h~Qa(urXoPLRU9rk2l?h}6#y_xvuc`1kfD+Hg9RzAly zJBMALj9@qUF%sCvKyTWi_YZ^f1m+y46UF+yGjuGN$CgP|?@bTg7;|CCDHrM21 z;(Da&b*aZV+Us-Rdfvg+wF1whnCJT=T$=bA!ErDhKCS>3^D=pI7!G_{Xj{c?t1!Wd z8ygmL?EewCR}CNM8YI&i-% z;2OrbP7rXNt8o2l_|u3#;!O8q_`t8>$WdbLBM!dt)4eKRO+RDWR>P*re~XVl%I|*( zn9CXS4Gx`e61W<#xN7$1JGlB1`em|}MoRs+T)oq|E9LY?fvbh=!_jr&_%$(5#<=ek zVhFn~@Xzy>16y2t--p<~pX2*4><`1Kd4F@6mT`f=`LW=<$zl6+0lU5eKgJsncpb+v zm=d_@XKvO)x8~NKhj`w-Y**!tVeZH-^1^<$>p_9LLz%lL2Jzi#g8qAy{vF;ius?2W zp?+(!fUTdgT_@ciZ|p$BKhGNw_+G$#f6tX80vDfETy$3c zaS$$#6!^$6A3qa(b%^rShrFGF{<}FAPH=Em!@5C%iVzbgeMS zr(#ViC*b-FZS zCSJ|UtfABS%~%|~SMbYd_RDobOw3m?F{Zpp;C&a{{sY1GcD6kk;nnoX)OTDV*!^j? z`%s77p0hvsi&v$V!hzAa~O9bBHQ?T6j-|&xZ?Udn*Z%`*z>f&#SZ4;k%%uy zC-vi1@WnLzuLY)fUyfD#<+cgF=w_Sl6@1yJeA!%ey}?jfW2>;tud# zm5&KrJ;Bdd`KJTtfZ)eYwsAG*iiszKhpa0Dmhy9C4(cC_FQfRm$`N}b95TNq@b(GC zo9De<@L8Je`HK)YBRFop1^ID4r9SCdf#)6`*T*?e&YbsO`w8n`N98@=p2pTv3cJHh4~IJRzc`0;QD_Q!!OPVdc(>jQ!x+u63Kh4we8 z_7{3@aOl2P;B5!Xp7fN)rbjy6m(uuAreY-Fy&&kmk+GloV7zS;nbR^X7Ln~y`R9OB zx-LEs*zI!Dw;j!W=kEpmHz@s`mA4DrrP#g&g8VnJ{D&Mq-3i;`@`G~vXz&(SHWOv9 z3Yb66oZT(>Z)e;O3o({d{@U-27wo!@{e7Xp>3HU}IfC7cKQw>a4s3BY)3`fR(EBmgdmH#P zeIlx3{b)GykaAJ;ISyB9-uGa81 zQMu)?`2@jV=cuvgxyrkRI2p-ybvZbj<-qg;?6L70KhU}Axk8Mzag6*%;G&JWxcR|& zp2srJYXzITmCZk^+Aruoi}gPQPV6;(x^IPE!5m7@pk5YmwlL0R4t~bgYks0Wn}%3; zr-1KF#@8qKdl>w^5;|!NpqI`~t>IdZ<9i;AFCW9)eGj(B`HlLFcMG`AU|h)v@21b$ zsdyjf4ZBI}Ha2T~o_7}WwHDZ`Lb&y{zH5N}qm2LOLLSmpY2%*{3ph__oZkcowv7f4 z^?kjt!C=5wKhHnPV=T>|YXsj-hwrw+hB(=jr_V=n&DcKz*vF!Ls=&{g%+FcOpI@-e zB-HRuKB{J$KPzyWR-9g0b)8`IRqV6>6*xVO<==$%aq%?{7*Yb?Tbb_}0-qx|9u_({ zdAq>%3~+sy1Jk#_2h9*n3{gH2Umf6s#-!1(n^s*;bm;z@z{yd}$x5`1i_=Pu)B7Cw z&J{S@!kis1a8{)_Yw~`DSRwn&zIc38uVL7SRfYe(`_gd;AW5o~*}vaPGK*Ma+9VSGLUKDk!lbTf1M zlA!lJO7C-Ja|BK{F(yI|`^w)IbftygmVybZQgL6eCeiqA59ACCB?7$#emMKG9o{RV}h z!h2QFaRuw>K@7#^EzLbwMr7;p#`D&(eYr?mGuNW?$jgB}j_YCECzfBV%b`ZuXJlW_ zxV|WGcN%lI2H4_oQ-5=0#LtG49h`R?1-sU=T~7*q%8`iQQ3CFFE8OGC-|FDx!x2sl zu5!gmrMFbb*U7lI;Rx8Z)nW6a0$-b$uZw_-{6R0xwT`L^)mOB6Q`s(k)YQS#X!$t` zZ31U+WX^7m*k=4$!Tw$t@rfy)rE;&Ts@-ARI`9(bYvQC^z_o^P{YB`Dj-q)3FxzrX z{Eh|NccYAQ&%Ac=#R_w2^_bdh~$KcG3sN^bJ+E= zz)3T6vQ*&zBF4Qb5_2ZsQrr)GOW@`U%*{ImyH91i?{?UAvViRZg{`YHrzkZgJSOfPGCqHayYiW8mwb2r)E_W9a>YALzMYM?l6Mg3V2AbC+QAg`9gw z39+OqI@s|3zw zs~q`O)##1!Ies78bv`(Y8xP(NzrKTg`)Nl!|5b>)a*n%49N4!Beb*Zh&ntv@TFZUY z_aQfK3_68l;YNX%B*)Taj#xPaoW=Qx#?LQC^2m%aG>4#jQWK80pB6CHC|`ZJ@*57G zuW{g=@KwzP;DzRTW$c5RNDLUv_o^5e@BK33BNKacUVMh&hc~hxz8m4g^wDF$#Sy^s z68kBNEx~@u34Pjeh=DH&e4Wic{6hq{$v>KdEsof1aNWake+T%Cn;*YT#e8SwQQ$N# z1^~%>+~KEJ1bdHUdmj;OI!?vFnDUby@pG?`tLFjpTRB&s6Z|on{qau=ANeHc2Weew zl$RC!y@vf=))UX$5p3T)2XDUrp}s9L_PYQQ$EH9-m@A87w9*C-Qmcs{$WwDh6(< znkDF-$+{;+u$#Vagu&M=U$VjcIS-Sm+XX?FX-~MB?AryR}&M3QI;BOW4cdCGa&XXx7%PtA`Eyjku%7zMWfdk`_0(WQeSg`@R zOlX15=aP?eb*75{G36=9w)++n!>X8^~iR^Z8P?d1djI!`X{me%3H!dF}ZyTbG}jFCabu)(0gknUW{()v!9A^Zp!E! z=4uNUzDDMNc>ZT3Rt&CTu$yG}3A`Q6fn=dfuA^PR>=F zbXDGJwb!_{YfE;qdq8EZJmFc+QV!OADO#Gp8>J{@M+HUGL!YJ-{578>4~YR)LQ) zjrqMGAx^rm>d6#JWnerg_kyj1Y8I+1Pq zp2Lqf3V!NSe)7Eef}cOZe*U9?Z7gGZNZ@6w@@09|TLez)!Rb>1ZrT^{s-Slq>%ALx z#l;tKT`O?C4_tpwz?9vt6wP$LQx3&Bb0plpf_-?^xC$P_M6?mTy-j5Juk7U_@ z68JlkU%EKJ|VCPvdPoC~QAn>*TyuC^A{dw^Hd<&1^gyzUqY|rlm&UV6{Znmjv zdi?m*%45h(*kpKzXk|0$xEE&@hvVu;H9ju|4%;>pYZQ0Y&&0R?tkwP%f%i_vc(>r! zW$f2~2zGX$eVS#|3OMeva9kGgk+Fm37OO0-@zvj>XLiyaD)4v&$L~Br|EGNY)4buA z27Qz5&mGE!apmhHJewGzwfsepUkO|Wv*~YdSMsa9w+eCeR>aY0L2rt=ds>Lkg^17P zg5H~1?-q+U+onp6(GP?3INY~#|1?R!_9n*GDDZl{&#UKcw=jUBg1oI0eAmJ`utv~v zjiqCzfHTQBJ1q=055t*<4+#Ew8~n3L(Enje|Ivc}vswSA9I^eRkY7tVzdq||yG_9M zAq(3n0>6iIZuAS>R4_OH65?+W;%|;%_ja~BDd4-x!nYUtrT*(n4%-(By4zUy9fD8K zhfg~NT<^7TyZUGVkW;pR^X+>;^lN9ZkHl%9~FG|PxjgUg8ZZ< z|3(4h80PQag5Tc>zyDCs{~GH*^t7UKhZ}zZZ1>jdkB5U~g5}-|YSDnQ)F6+czp~)!stH zh@C^G{AP@Cqj1LhHE?I=zQIUqxPKO~y~@}=EnsV5-~B?swwAH&5@K{A$LJ#wznc6X zuVQOgth98D z=nHUY8}**;SAg>`jCGL^Ll+{3zA9kY#28){Fq~vzXm#`@;~5`uL@#qc4V`nECmcN@ zVA#kQt`+j*1mwrxo(b$H`Lw1&H$bg{C(+Nx{`Crf zXXR!gcHV{984KNUInZEn|999&b~?$g|Dk}ieP^o z+dtrl%`XdawH$FZ;*c<}rvE5Yez~GzQN%BX*FP!0jPw2|;9kpdafE<-F5_Nd$tUij zYlW{0^54kvdj-E;1l~U*#N>RA$yx#1@r-SXz|BaG*=7s7olk${m@U67KHt_b_L+kG zN+o~#u=xVM8pgL?(EkV4{{h5AT-@HI#x?!yyfFgC)r|3JA$C?Ec3Pp|j%SJudQqQO zBiJ@V*|uQ#iGpp%ux*zJe!Uoe-44Ab*NAs|K13Px{vMhu@P7*a9wOLvD%;f{;HzNY zt`_hm7+;6L{qLCjqXqq|SbvKke+J9%73BYx<)19zuVVc7I{Jgx1fQM=pFS((Oatdk zR>1We#Gw2Gdal?AKQ*zhGQdvzISn@RjD)H12R%#8tZQ8?U@22r65b6$O#O;u>THLd zmqRxB%wW*$&~y3@_kJw+IXvm7r6Zuz#Vsfu~Ow+SLNeE`(JYVwBYBJ@bguWZDQKa>60M)WX|bN z3-~80{7+SWUEppcxO+yx_B>-7_x1Q(y&SRdQHQ^_2(d6v#lmqTUJ~>^!+O6jU>*g` zbKvi|JRF4>u6LAuN#JP`$KNf2A6Kv+{~%y~jxpaM`050-U(UYzxnS35*mb@T2d5wo zUJ-OZ&APuM*ygcqHG&_nfFJJ>>|V-tzbIg)XCdbVKhNcy_cHOh@hs#1yr6pt^R>?5 z&&LIu|EjKuj4A&rWXHwHIK;_F0oN0Z>raBs^t6~q1k8(>uZe=}U$E?t34U&bpI;O7 zj)&eR!G~AEhldC{pJJUq6fiGRm^&+f3ocDRLt}v%JIA9Bp?Sf09IJXRzAu;nY@P5^ zT>Fn(=UhJ*Ff3#YZ-5PP{-W{q?}D$Vs`&nDVEaD%iDJ;6AFhI*%HACxAC%AUarj}rpl>?#y;<9QM3L;PS`d@h8mXR~_v)349FanEJY-?JmLInXvb6 z!T;}v|KBBGz7KfsXUvlq#gB1oz;m;MtCV2RFlEojyuAWuT2Xkf(H@fe?3VZs?y^ji< z|C`U9z9V4&E@Qt~z;G;Mm<|jTTD|&fVt~$9zaZFlG;AyXMtom%HTeFqfa%+e={>*{ zH)l8onBKs+J}KD!FSa`Y-1a=t^efl!y2`f%>~8?}=LCF@GQMkpFOCzM%WV1j{Z)t>sxHsPXtbm1t(t>u)U_{0G^i+ z{QY70d#T`ykFYQPD&TsAaeYRJ(P|Y7Bg&r_a32TU-xYBGALITvbjQWoH=+As*8fEz z9*=~6dX9ja!wY`h0Kcvk>|en4kLZt|tACyKFBZ7{2)O;DV0$fWKTn9otAjHD18=HOBQV!7oQCPP!^v1-n1Yc25)JKg9C?CD=9#w*6MX{tt!y9hEwRa0FXQJ1@I`Z;V+EV%!RAR3n@!tSmCfV4 za|J(s6n?xkqSxTAXFshLaDSO`S6&|9m(1iCS|H%A2k!T?ZZq7QXIPKIOoM*y?Q24e zyn$onn26m5`(N4Dn+5EjXY3P!J+3c44A^Hd{*?m$`N02@5D%vz9@+%m|HHcL1nhrN z*ryD;T=3bo@Y&7aEp9$QbELHb-UYyW1niA#N8`yI4w+qoE%&l5#|SZSh~luT@-E1Z zo6pZij7`FS@^%I0^IHV$3xWM)fv;EC*BJrhJ&f@_jFIZwc~47g4c_@Bh~e^kJI7vrAziTJVXXpYH# z!LFCt|FeYHX+i8VP6zohCFLH-9= z{$~XFpJw^H1nws){1x7_f={o9Pp=oaSpjaoDCqqZ>)ivrQXl)Ch|MO>{-|so=dBg| z)e3)AwT5HCw4K9zd_ur{A}~KJV7`MfuMpy_n&a$Mfvb;$s|y64>1p6JuG}NwIvKbo z2{v8DHr*8A%Z%@TP<)+O^+Um~PqJNK5_F#e-S2bwsanAGeuZni_aVXNG0Nu7%DV*I z|4`?EuT?%R;Qj>TK1YayEr^4M1)CaS)3pwpz9?Y&z2b86u-^!nZf8t~d^3I=x&blp z0|8SLFx}?BR2Sjd`1gH^XU}_5klPHo=Lj)-l!;lvp4-@-yB)Z`BXIRQ#g*s1OYqND z_~(8hHt6?s#(5WdFA6rD3Y!`Pj&B6VUuL;xtTbGWQRf$3m46WAu7=$85uQ!o@m|F< z_Pq=C{+8{1Rgk>~vL~J!KmWg#dH%M5do-UHZHr(tcD;vVpzN3NarZ{({*k~ntyWUs zvjej2zL@N$7d=PykAls=Q8xc}_$LJ)Z-b9F2!6T=etKNMMEjk`d0myG1bf!Oo}B_0 zS27pB5M+;Fdp{`TM1phTd7*t3V|z{Do>J#71xpqutklCORj zDOZmA@1OrN@LvZ0%fNpb_%8$hW#Io;448Rfg_<9XC|@AVJ#OZ?#}UHZV*}QzQyyb5~`7ka&S-^c0<9<|_XP%CEW{qIeCfIaQ zWUgrD^jGkl{@=i5&wU9uy=bmgFUUO&a%T!${ff^8)(Q69$o5oS5q~~VuK4Jze1Bw4 zZg7nPu7`yA>ybQvT_)h(4BX?Om+Uq2@KL>Jc6p7!)#c391%m%?f&aG&_TIquHVT+p zfvHa5@+Ic-w}S7_fbVZ}@YEq-`Z#0y4BFc3?dHCvcf(#AQQ-7eaQcOaKTJ+tru>2USrYHdGr`Lrgcv!3W8^IY=Ia>qLxS#0 zS@$M^lNZ=$or295&`?i7<-ct<~lE&=m3jQIrSN%uTK9E`?+j|h0%fVWrR@Cb1D zE`f*V6c1;V{o7&dsS!M8{e#w`ZW8dE4Ln~G?7W)oTv;He5-<^F^D@_=%{8dc3Aj?g)h5_GMd9kIobIsq zCj#aVGv+S~dUrtYCj>s8VLpy<=V+MS8*ro zp|RDxMq^$;uTk(1T?d?kIUn6$LC?3T#>hNIz}Sa6ZxHN%AKU*K2X|Y9IG6-(x*fLn zNAkkV+b-gH8|^u}&!KmUU{412yd>~?3V3M{Y<(}=`eldCnS#!K=o~RKehmJn%9n>L zzTuGj7Xk0-z!4ZXoo!; z1-`$0#%Ef;vqfyWC3o~D4O=NV15fQkGi7}7p)AA zVZZhZT>Xf-IzGlOYX3WB*S+4m1l@0i?(Krj7X$N6g3Twf%|8%)e>QMWXPW>+z07@^ zH0!9qk=r4g-p$MKN_!6%I(&b=z{L+07xz@%DdfPVrk`=h?iINBAh>uQwo>ev^L2x} z0;_s-Pu8qK?7d&WbqV9TUhvV|;3IEqJXbWo`?4VSV$+Ak#0Fvit%{9F!>*A_+2)_6Td@&w+?L0Q))CHVtj|iMx#GHIiu<7lv zsZGH3YlZ8{%EtuR?_$|agV@8SzL2r+6m0)rWqW4WR@hcS!NV_ueJZ{`C&+&%WM9DYzawD30NAI3 z7m{sc8Xfr68LxZCKtAo|^4Wp*d}4QdGL_F~)v?^NWvepT-c-H?2l~2Jn&U#%eC(7PkZyd2Cp8EFi7_e^hSYM)ndh*kC*X&%%_HW^=WZk{o+Nj!mY-mYid2MpnYd5o0!F4_RzOv9^ZR1eO+FJHLHE3 z#Dbmc-k(n9J#Y)&z0K)-eIrJdRmQruLTg&Q|iTR1f zOkX~o%eOVA`Z~HhQu%aSUHzQI=FC7}N1~x^ZMwZZwTs_cv*~o()<%7-;LF_I?fu!z zIo`ZgO^JEUt8?v%dFxvf^Hyc5+uEw}-cjAQyM1Zh-2RRo_}Z80$#i8B^H8gGQ(|5^ zo6Tf5r~5P6{4z-C?awFXH6_<4PhYago42xYTT`OC^L4bNIx%NWqPlTe+oo)$E1T*~ ztm^Jb=i1hHw`ViCOlLl^wL3SE>PfWZ2RgbliKTPvz(ZSiUwh9$M|x&WcMwe4Osw{B(IfwT>vuhfQbTeG2QlBu#S5hBb{Fo0^ev_!fNm&jVS1N@EL9{Kpi- zncdXw<*3_h*WKPu>h^LwQ|R{41FLSYJ=E=Gx!c>NyFLAur*4lwVi3vA-RW&vv!Qi< zgE!EZ>+b4HcO<&|@}Bp)XzYZx-I?wVFR#1Zb*a8|VpcAXj@lbtV*^Ocs`Y9T_*Yg| zn^?Xa@8z|L+QixB(1|D3%x{>Z57v4qFVoqXOXt0YhSvu;u?Fm5seb9A*H1-z{{Xav z$XTn|2_5V1J7u7=GoAI|sl<++O#7}RrT{x|AWGuwM3~gsaZ@6-1H&<*dq=uGHIPduHZ-1+=t}3CQ`w$<&3n7^HMNOs zIzN!548WWt?X@p!!{Dt3&`vdg_WA?pZaskJf&p~5Klruf@)%X&#-X-v&*nSQos^hE zm5@vI96;;#Oh-D^m+IM<>&~_1G6UImeC^91NxLy2h2Z$9Fimear~10A39L{tn=Y;% z1fD`&STG|N1r}AWWkAiH2NSoZZ+EJvyQ6Vux_#GRb^km6|If|;k(ygmJxEN<)cVue z{623}ie_|q^n2?w9RodSZ+$wyGt=R%!DyK6Otq)IQ`3FvYHO{7WG6s#Ibw{dwmwzC$8)qKf9clkS9wU5b_g<}=zW62W6x}$f0;xFGpG_x{ z7(-xiGMP=MI>_GEOid2>yZh2THFdSMe$$*zBPBGM>_}(Roykmp9#mi?N$zMz%I4{S zsz7IPXc?`Q4%aHIfc7!X!om{8$r?@W=lUOLHV*J?NDrflm_nz9@EQ(5Ea*)$Uq75Ak3 zy7H76G5U)Z5^h}(38mP~!6^d{>(X8uL;)%#W8&XPGR5Ht^C>aKcrQ3sU~2JEO@P80 zVe??kBjsu^L^Gs0e1+8n$;~7>lrHg%6(T6MUO2rypW2|;DJ_^Sc1mwpY7W?F*(D{1 zeqnrx&Iqwn5CZXxhuR<#2Nzxtnu7=~8AxPU5yl)XHoh=0Dn=G842Ntu#He^kBM3n` z=&}!%z=Dy3(GKFff>EQCW>&VKnR~P_nG#Y+xl&?Ft+N!AK^%eWV(1vWnw7HD#}tPq z(lHg^XecyW%1R;$!J2tBw0nFuR;!f503lrV(DCca*Tb?x)ye@0nsw!Z$xg|qy1 z)Tiy^WP2U;gc9}j(^C-E@ex&W#pi(^UI&2o z0Fc@vUEyf#gR=+j*q$n!Glj;7%Oi5b_z&J78x2Qkvf&3>L4~kWLdbUP%ct!{K)tjR z$@iE_#oLZ0*WN)j#8e7vAYT*$-;?cDtF>6Y=a=1kTazcd?=Lv4CrxoxC?Hjb5{_g#tXT~ohtsqa`;;eKWzeSF zky~>UaWD-bF1`V4@H4};PHzm-&I_`sm^PtsFUkl`DR*5kC|02CWb=4-?BF{{WfKvD zRJL4GWDBN^d{bl#%Q@VFm&T-kv#cPG7T?ISW-#6@9mRy1<%J@H%u-N73vEI1;ESl~ z3a;+HNNjnO9g(Xk`k3iaA`K|MV!D$!InJtb_o77=M~shsFkZU*fJV%@;*@dkuP0!l zEZw685|ueYa1c;)$h~de7^0sFM0q+;V>8h5k(7TXw6z-sOSN$A_^e_k?TL0w9Q$JsiG&TIK3sj*2g7cS z>6@Z$6Y*V`s#a840SAoFZ(Lxusu1Z86jF17LEDZpeLb`{Dmd4OKe-6X<4z0Gd-Jrr zi>|)3_vHH1?KP;6%~^YDX*U<`cmmY}>18b|lbhhtnkL%8w4z})INt8{*L!Eh7O11y zbhx%dUOnVtGB>+y7)fpL`scf}b8S=|L?qP+;|nQ@FN<$vj`~IwN{SZ6Hlc%Yyju3l z<#J=Ai(F)jGS=(M$QB*Sf5uJG7O*7LKz)abt+dti6s&mVR#dn3om^dBW*t&n0#{(y zpH<6LXvP{A!YtCv91XWLM^~URe{R9PI8$kM@|gBiKkab$dIx&)-Zoys3d0H=HBBRR zA>UN8*mEL>TkrOY8x^! z2lf4Dg_UuCn5u0xq>n*$`bL$MU$~b4Sy%JFQoXqNQ+tp(o$F_>@21VAU}N_B)L!+e z9MOTDgkEoIub*Pg3L1us>_&lY1&zW*b|VeHph0lhu1sRIHx&v* zw!mUsC0kHjdBSEju7WKfKtf^dL=jZ51!x69S&a6e&~6xLHXIa?7hlB|5;m_uKw+`n zI1o<=3mO*|+l>Re3mS%t>_#Lq=*98K7<6dblI zlLq5KA%4QecH?kKNL#qbZbTY`m zup5|TA?fCzU6E|qA0F=XA-htf0*$+yFqlzA9mNTCe#c*Qp;+Lv78;G{{IFn<(vlP@ z#a1Ti_87s)>_b|reMGDj#%ksj5v=-f6l)Qc1FWIS8ksHCJ`%!eR|;d*@fbm> zVuN4q-*1D{y8U!FQhYUD!}U%%}#(N zOADr=HaHV6I-C8oR!nPy`c}dgQ{5I%o-R`KU{!6g2F89xXwj%$)ot+v#Z}iLWU#8X zSUaYQ#q2Q977ZAVnqj<-ind67N_b#rI;F_4PhI!WwYpUJ+;y;X+faGVV|ffPCvzuTgS%#zoZ7B-x;pbwd+l0XhIF zHW{GJ@Hz4aA(x8PAtAhy)_7v_D7^<3Lh096W#rAT17@VX&m2FN(n-N{j5M$ZOIpKBK* zVKW}U?=N8Td0^<64+$rEp#l!Y=;5G#g5wOAQ3Dm`tS(m6U@hGFtlUK>VRk0se)M?l zfh;ShLZjUfMESQ>p~ZDMJ%rL-5%sDs$YCQKASNhBBnY_E8>+Y98dit==T8Vk0~^ z5aS|{WC1xaL2;1Gpiz7W83U(SA%TBwD9nn#K8+4MsAy?(*0Tiy&Y;4?I<_FA(M&=H zLl#@l7D!rSD_LfWb!r?A;8g2Qgm5p*uT7EInvkm82ge72*zvs1a!?>OVsqoT4k5XcZ{FX#~D5pRJk z8=N1N(d@JBw@3-3Scrx$#D-$*vUXF3x77{?V`u_w2!SDyeqm^Alq$rAqKzq4g5{K< z39upfY=rxsA#qWn2pfhnq{Q}79!hL(t5+=e^ULD901LuUMc6QCk{DXyE-^QT(A-vU zOgMmm9I6N#Mle+#k|T!F+*Z#flo(Bgkx-%l8-f|ShiZROs)JKeTXks0Ty#7IvMe4B zR8$N7P+~=CI$Aj}qFD0)iPj0DPhE{3r{bHl#+2eqKfr zIfb7pPvST3@LS~uu`Y})+N7S55#LNb0fSZr@h~MLim=fgmBwnE`o=%FLf>}jC5vuA zw`Nl1k|YmW*+?xKvpPd%C`C208w?7EB$m{`RHh)Y8`F1FhQ42w1^3vSyWDC02RFZ} zuFkrxKem{p>-*U9xx)Dq^9nEsV>zs{nt1O0E-NlIv(h;qowss6Bz^ zXi1TlH(5uw6UlEVHHAr=TI zH45n`9BAI;v<)+W0HUv9&=j#fW%LZGQPG|ImR4(m^X4oP$<$#(;6AjWLe?FgUq}K_%Ca9IQ1^ zdw*I4dJC=7^@^!uu=J7?{R<0};RPd$mPojYt2v0=LB$1@niPr)B3*0n8U~FlLkiPE z91`0Gg;Z`ZNagV%80cq&^jvJ)L%M>qkTffF)>Pd-={Ap-=2PAhsw$g`ld3yX6Y{)W z36U02P7?jN<#B|jJ7R;9DwA}cga;EprNu;ssK+!5*cr4*RGX$?&}K$ck=3JG0)RTw zO0Z~<22o9BY;^i3Y)ugw0{nwARYFz5UL>JKRwIc4fI$o^x?+HH5Q#;RZ#?FV0kM!; z>r*8;|ANg;eEr1E)!-tJ5<{DK>@863%i8+0nJzqGJJH$Qlg_pEcDH9UxlCt1vAa7r zkm^a~@&g^+nZ(k$b#1%b+q(POdj>kvZSC27Dwj+5&fV!%uQyHRZf#szSM61!?%am@ zx`u|iOYp6}vwg{q`8$@T>eKa0((`vzdqY*aJJpfy%t2-Q&Qvxr3(wHsvuHtLpfA_m z)tBx_;GxN0O`@zUu_95kJC*F|&gE$K$=^3@v3RUyXV9mgi# zYH9=rYrQ@X7v1n2<*q*6wAM4V)OD+x#Ie~l>Zh~)SlGt&KbK$Lp6Scw6N-#uPgJp5 z>n-uHgg(%oPvAEN@T~KmOna&a?K*J^O@}=q$!Dx5)7O<4eb%Dw-nz!*nkM~m4+%`> z!7u9KZ@i#o%aX}~zP?m%x+7UvSGPD>uUq#3)NZu$b>wztvLr@>9qh30*`D@fzPmS_ z>`3KPXl;y2b!nspMpe(D1Bb9Oi z7>fm0R#hORvPLCTNJs%D-8BqOWjshS9qd_o3vFY_IjgzTSgkt^^%cDZeZ^ZzOjt@R zTO4sJV@3M{rna30?u- zDjn_IggY8a3JZdto#gIOfHB%Tp+Avi6{LdT>8fF>pDGXadKk2ZC(t`HFVqv!k8U)k zdU`NUH#M(Xv!OZJxM@>zo%@zO18AF+|aagb5nA|sx2EDlXc1YNt#+Fn@`7_ z4B=NkJ5g1h%;D2;{2#{e6?m`2f0bo;SHmOTM`JK4PEEs*)D$hgKDl!9sq5CXwAzLk z_~c%Ig%MTg?D46-vLivS~OCYH}S^qjpuS+f%ige(|h*UNf|5fi)BvTYu zGE>CA7*a-K(V7~Ds;OuV#rP!@Q$ZBk=+ITY-6@B zQQdg_@zsepC-Q0h-a;y$PAoU4c1_vt-RTyb=gs!d_L{o0;RAHe9zN2%H%|vn#Nt)V z33Pc)5?d$}dg%mbXS4JSB2jZ}QmLywOTUwd?a0z$s-!zh$F*+%D5=I_gTOxy065Gr zw%5WmEH7c%L7k&wzM>VFcMYC|(-C8yl8OUHI6gw4{!1-zs(4=FJSO;R9oJz25kRoii6vMpAK@3xYv>e}j)Uxxz+kw-q!EL!l8h4SNHLu=b(Q%A z_Vj1^$%^y-zsCqo#Y>~2%0!djb;TUdSV(6#{>_Z#nPyYb8VG1k%W02U=@Mq^~W*@h=TdJ6Eo{uB)Y;&lYz(ENI671Xh+8g*eNnXjEwU+dM^)!}?yt(fB7|4rv? z8kYvT+w(LF@elT8aD6A03msEeeCXOut-eB{m!8&-@tvl0+O&eR*oSx{z zIG(^A5xBn7olD?>*}Vz8ccgJus2%f>j=707xG01xKeH19xio5}6Kh&FCNOQGX@t6! z0#|iN%CbatqP#3oJvY&kPABl&*0g}XGu_j##CN3gsqUWK+-jiRklsT>y95pTJ9(8P z0bsjR*=`!C6FXD6#Ex{jFOeJQPiNcH9k@ofV_%{#y(gVb^mOmYrn38}K?bF%JO=+n zZXclIN+e4IH~D>Y6OFjc)!l(M139XsQDefS%q$B18c6l(_O5D{7yW2Y^X4?>bnWVg zlS{P~w;;h*x)z!0Ob-gVHIv}!jPE%tF=>yfWl6ruvSDL#+se%w)@(SnWpF!#I18dd z2UfBbg9rk#rM0Os$)eY-IVG9&8tUp6)Yq?5)~;E<$qv?zo*vxigKKvbuC#!*a?_^f z4NbhHg@aq^XUcKaNWC|BRb^G>Tyxu+CR`UHMqAYFJl?vhGA?Rq-3)86I;P82TMxh3 zJk(3Vxjp*Fa_3_H?DtWthdQ+Ng4bnjse# zW9uL-lnT01oow9cty;6LIWa4_3R@Xqzp^#io!K$aiA(=DV1GtoE!S5Ux>cjqslE<0 z)p4V*MJG9ESe#wNUADdgKa0+X((U<7c3-4Dz+ksnXZKzpu$vn{Xgx+4T>yp&xITNz zK&NM1O6M$ob6o}9g|+h``YkY(uQw->8#_C5X|)3YpR?Uv`1SCB63m75b?MpYS;36j z8ep2()u@2$#6tygh1Ni~eo`L#ioqVXE3l-v)s4(@m>-&9UZStzSRZWUSA0#I!f@>enk2*qqI-&vkKHaUOC` zZtUscv^12%lnBC@1Ew;iZ)vqn%Occ}aMf8vG++b^hKhA+D@`1Xm=S?HzKA@%%uql# zt&5m!S)bZ#y6=L?b)J?gT!$Tu||5p1d))aE4${wD4~f`g78e}^H@Ldj23zi<{sv#YNfw$3Yrp^ca1Cwo+&Rz z)*$4B0MfW4IV!3%?8ZP$VIQqf7Isz!ycTSxHM(awWnVr`gI+o!ss;w z-7c83>+hJmQXJ}8y5HdyK;vp^%I{@uYm6C@-rhdV#94t2NL~MehER&R@W&((yS;cB zu?@n_i&YbDWdv&$4TI4nm1?=nw%bJ5jrQv?kW?=735=C!-07%+$W~q(Y#diD{j>dW zB`q~Yr|?ms5hv40PU!s0k2lNjNj@7=6>TGEt<{&Hl+p=cB#ba-huVZs<2c?G1)*wu za%ROnPGXJYSR$X&)3iXh?Zn;sIGdeC4<`v5ugo!v3!&u|IL%fZ{bmR(>{i3AxmqMtPg_^e}kf0b4!Aji?z`+T9O;Fq>8%-llk8M zj_$0N>>tSQX;1cLkdnLTR+ZY>xqL@vfYxBK54) zEH1L7I^c$`M5-@=HTG;uora{^u~!65u?m~$PvPFGG``PGVAWq;lF6nzx@k)(Zm#lg zok|!K*gcY_wM;CyZ`imY*|>4Ts*Ri1W6eC-vTYOA&RbWt&^4JFy{J`NgR2^N)#B<# zJ^t6#EvZv)3-nuqeyi7SbxTxP-2(l_2OIRadJkhE#b%N&ztq&8)vz6E!iVY$57p73 z8d?<8o=38H4fP8aFIX~v(SpU;YrTF`a{bEFlP%u-hQ*5(K4=CnmO?W z>ZUPE)AH9N+C01KstDH1qVKW{z?KJ_dCLx|ewM4U6#?w9;LD@$z6bRHxX8O{5yJ_{-|CCU-Jq5$H zUZKVnBt4T;>!;{2^qJ^p41x3TLe=>OJwmM3quR( zxiw`|usNN>BU%+}F)<(p1#-o}K!659{AqM4!h)0MSbJphbdA*U8O02PjQ@kob7%uptbVgQ_w?l zV>E<1)!*4QO@D^H$3aD-A!3$Kj8-ASW}&ofuyNtl0FO&s*R$UbJ~Y8EOruyZHG{Bhr=!$WoIb_kg0v1QPJ7^=gO z!%<-ufpx2;>s#O#!eEaH+Fj;|ONOhA<%c>h3^mo1Q_#O_-h2 z>FFxm{Md@QCU>Ch30gVA8kzlUh>qm^1odhRvtYRq^42wN+;WQAy2H{6MAQ!^f*=hl zrC|^$1!^tilqwOsEKo6}maY ziAqUx2C@@pAnfCybGGtA&)x$q;rO#-(;9(iC7b&?HmQ)&^{tWJ|A^R8Cl4`!19b|5 zU)P#y>SjR)XVr<7j zbI@7-UL>B$o9{IEnk%spiqJLd)lX~A5$WMomV}A*Ph2SB=>{Qns$ek!_|(ksVMk>I zfG>(Jo!LNTX%T#;&oau?>T^U(0A&_h$9mk+BGL~AR|kEc=TSpI>QwTXk{@sjZkmy)uC4{Xq>T(}K(KLt>N;5()1!Aub zq9X7SkJ;;YU|XxPAh}KLaYerRU<*`n-PouIT?h{xQH(sY)yxE4Dhg_aNYdSyl4G+B z08+IA`&3)GaN5oF_IP&!TBQY?sJ!dx=-!QMJ5&$1 zN!ObX4Tuokmj0TX5HRr24Pu!rS8Gc5WqP~&)R7P}z5ru|vp?ZBX{EHgD%d@{Kc|z# z6daFj4V|3(c8D>OpfH{(4IJYy@`vs@;F$0a*ntruBbep0h;X2&j^FB?;^JC0v@zQX zYJ=@i=8g}GSC}OaEnzV_6sSQm@U;4BJDNA>o|4L?)wQ%5ng-DWp|Ifw*Y(r2eD-N3iP|prlrHQA6=B#( z;gn=C9i9e;hGAUqXaA;KJDoc_zJhyt5SHY>ByEGv zMUJgc<#wq^gokj4j?T|nqj_0zW9Q0jHnnd%mgi{eNv4w?)JKcN>Tt9Y@Pw;HD&YCI zXx@kG1K5oc$TJ;~nq{&^bD^1c-4T52IvLg|^zy8|-e6WTgEH==tbq~Fj8%(Zv;2AM ztXcjs>j|?evjahXVCdt~E`T3v%b2hrvm3V=cO?CNI!SE)!99T3j3pK&lKMVDTIr-1 z;GUueo+w<8(&1nx8R+B-4)}m|Zs9NXK;ias+S^5Ub@%iHn4(7lC@L5g1PzHsQuFLz ze)cUShSy~m2Jxk+9(`7Q1Hva2_L90)Ks_K z-x0^t0c=B~C-f$R5y7%2neN-&oyDGnB<>PV_ThFr{RtoFNzY!*{JN#bFR0Te@z_8# z3wPz~uualU?P>GFo+=qUDmHRzu)z#VbM%#ksUxV-k;&mmUxvT?pX>Hw3(i99r%}7x z63Y|%A<`$|y^Qw0sjuD+Q+{H6c`}*o-I2uedy~DXy}auR8(|h>+YMU!QK6Hdw&qy9 zL9VBR9u+Xe-AZ5>yVYpBQ+Rcc?(e`Rn8Je%e(tF&I#vq%CkfP(TH(zf!-GW7+gddv z>Y^;mpu#?i6oN)tnUz zrkXLejZ%yspxs=#vgo(9apj=%F6{Ed?zN)FQiO?Qaf-siA`$3kj_OZYe6&R*9Foo1 zY$m%toy(=VXvHi5WdIcfj8khwJdg28i69+d1N!v+1~`N#X=SsR^;wMyoTpkw6k=Zh zb|(7Ci!r#-pVJxJ;>IGs%(jx^xBIKN|6-kzHf3)(yN$o!qpWXK?;ZY6C1bQ%S&bOER5c90t?>bw{F?I!OO2r zx3{NutxMstQnPRmi|Y3Jb|m$0WYBhI+g@5NQsFeZCe(0tBJ>p-DbhVXy$uVz&Sm&9 zn$ApH4!?kbpQG8=pU<@6iBmh<{vUg90iDIsHvCV}Qmi->3sNL#2<{;XkP?y<5-8T2 zKoSVVh~O^8-6?Kuu>!?iix#({!3xES6m6m3Z+3RGd*4YYeg4n;Ki@gud!6jPv!mBs zKC_b8&Oy#@&diB-4ddGK@5O_|_$MMEZ7aH#bBJA^dL}m6znA zd}PNCqJ4ZMuWkgFi--!24-Y9RD)?1$uHYBuT%nPVa|KW5qPC_DJen7EuF%laxq?^Y z*pAK>nv!bNi=3#U%|gOM*h1pX1ulaVFS~cFQL%h_u>8JG`i@V*CbLYRmQS~PDat%uzrGsI>Y%wee;Vyj}mA`P+e4*;x7REXi2 z;joBh^#6F>CI6Mp|A&`MdT8zCC$5yLYX2FBO%_`K{X&PtsLkGv8TQK9Cbh(LmaPAD zEt!au!bUhEWf>waLgFfeSecTXljmg6li8Fi4*%n1^bs{GxEkYYSJKkz|G)rCvDq>OeX!KE+DC&}mSVFj!}+NBxK+|# z*bK0jjimE2_Q(EFe$0w$NaNZ>z)=vB4rVakf`4+;GpdcFNMj&-mc+Fr%|Q&J7&q-$&e&x7AH>-Zb#uZg z$c0hSI$c93j54zmM;$Y;GbiC}H&IPybk*rdNY#~D>Ux-zmqusrS5${uNxMnpwZ1w@ zZf2@Z;!Q90=$YD1)}+BZGC0KUJujJ?=p!60h!$WdytKLY<}EX|yU80eLA`hisdJny zHmEyGm{?l}KGmu3Y#U{IUE$&5=h39O&EKn;{G6iPJdEotcgl@@Ln$b-Z{s>RHcs}I zc&QI1ulasxMco=HE=KVEEW41WGv-Ub`Dd3OoI-wQ8^Koc4a|b1!Izc#l7jEQ=C#`Y zYW($|de(p4E+{TMA~f;;7{0H0Y;f1uw!CH=9MvPXZTG6>D+aYS{{7*(1iiu4<0zKU=^KnDRKkNr&>jUIA?zCZnZvq__E36f|}9KkNXF)&7ra z$N1nN1OK-2f>nk`V2t*1qb~DmDen>;?bTDRBm?BPne`%%!1#FiwNY!4w|uNqX^Y;{ zh%!`d?j_p2F)SUfcB74P`9HGJox?iD@)C#k=&7>{m}se3O4%adD-+J0xt$=tHev6A z$~q@)t_>v%R7);PCpU;OhIxxo;uNagKhIby*>3IKo9s0>qEdM?4Pks}!h4&+T?=Bfqj?9fC-@)1*yK;{Bx=$3z_ zVP5hI2-xXOB)he0+Jwxsq@dp1OZKA8$Ec`gXIv$b7}bvXByE`#b&o*f^W)CyTLPV8 zcs!s-STOIVOS#f6JBP?nQNuyj)^Mdz-w3v&r=(PpItpPuTTU%;%+4~U4a5^T@sVMY zPNevLuybcV1;O-XMJjx+IHm{mYOZI zpzf^Ma=RfkRRC{GEwiaKgEpN-p%H3Z7~c2?u>%_{pR8ly6sdjg!miI`7^V|QIVLZKTZFDv)b^vb zts^z9WzH`01qJ;@7P|sN`Rp?78EN0K{gvrVECON2GR>c8M#(ya(znElvKw`QOb3{z zC9#5Z^Cnf@JniVJ+!&v2?HCir*1ME42V!Zvu?E+a>(=mMH1+sYVi005F3Aw#^5#xl zNb+KzEQc6WP17KObqfD-0@`M?bpr1dv}pH#k1Zoe{4_t%F{mT|tAimz!{ohIJy&{l zYXx16ltl~E+saS1*n*7z^X;rs^pR-H4DVuA()vm4+He~^HT%>{`|WLygr?HQk3`y{ zQL%h&Mn+HeUWQE%rCC-PbdC?Ruhmj>taeoz8_w05Yp((yer54X>S^D2$2(6c_NCQN zu@qWZ`quVt;^n5~2O%x1e_+p&iSh}76#7jDNvQxS)J{6Cv30B|dQ-2a9{#P|JW)}P zsOVnwV-b0y5WAwhjwNVJQmxYa`l#fYTpud*Kx!JZQGkFcV-XwEQK`RmM$Y6W6D~`8 zt39d;;zl52>hJsLEj7V19b<5iNoH_z)!=H@dXS}VE~h-4wX!2B4uhMj#XNP@N|B|S zPFjiMFc*^)_Lc0f>@cvR3I-ZK9LKlTdGB;mmLVypsvC>&>9S#O#FGwSn0Ln#>#SrS z|4t0vKWfTTmiADIYiB72RwSIsP}1T^!IVYC+W!40S!>#pLbkIneINR|3_G-uYOYEg zdoxQMM^*LNZ7?2h(t;hebk@u4o7i7jOsX_||J2yt5xGQnkgsZ3r;R1kZ^GE;+K(!y zV=Z^+HfhM{5}k}|(_ESHl9shQXUo4pkv7WF133=wcx{iI{Z(?Dv5xIPP?u2l039e4 z7Zu0n<~#z`!wt@zIqRXB=G9!}oedo}aoz+}YAilUsZHhMC> zsn|ybIL19N-q%o8QwU0C>Qi%3XMJ``97BWRJmNcO{|m=xA^Wvzi{T?xgRlts zh^XBJSUmbgOC;qcM;AU@7^PkD*o&U4OTN0)ZeOW2sKU^3JbMmH;+TDEftzfgcWL0J z$RC=>E61J<_85ViTp4LGn@1p1CFYk)g8sArF`sXhF6 zv+i~s+E+|m;3`zi=roxS*0G8nz-m#LHYXmCiDnA?Y9mr#rgdS}s!4<55)sse#fnOX zMQZC7?Lxyv(p9T!5qw$77oF);Wok9_YFbOR`6-ipshQUjPqoSx*(T$@Lhbu=#QI(?#M`aE-^4vJ{(NPXJ4*5Y+R}S(j z*5IhlAwTuXV@egPa8OPg{@EP2J6+Y-#*910cL<3HinfLFkM|TB8fxZVWz(Q&V>?;f z1WLA2vye`_0}>LGw1(82P+pHdRavD&K%f6llp~DN7mKxV1AdNLCYso8*b81oa_4w1@Oi z9tKJN2-h0ImCw9Z6MO{LwTggo@C~-XHJ#j!yIDqll0BTn0?R&*-Q3hRY^dCRx67Bt z!jhXfRe1|S&Qnt_Zq`*zd@slp0z(2cbvA`MF=Xedp~U`-ZD4DjWlzJbXvC1#iW{+UO+`Ko&Ih}BgG)pxDN8g^hH&uGRXbfFxJ9cpqf%lP z3E9G3-DuI$@~ibSPYc&Vv`7llW)y}b&E1%H7 z=+zKz)rA`$Q?4T1;I5nK%W{V!53yD>T<`Wb>q<@cq=wS*+K0NsD`n;1uJ_uazgCy!XjZtENrc$ z-Fme)bJ5(bW+XJ{uJ!3a82dc#q^k!2WH${l1MbGob*!_C+`G10d`gF`)4{Q>u}G6b zE!Qf_yQnLyyZk?_`UoCsZ(aU+7#r-?+KOMYf32p`m69J~L_@GNxzdG9zr_lREN^+( zGtmBuR~Nz}+_d}0p4BEqQfQqx1CvPBtYkVgXsD#t-Xf~U7j!D<4=?Cf(fZv_t9ZR{ z$_5r{7~?7h`LNf3_gy7^d{3f<^b=5jI2#Ns{sWwJQ>w?#NUmnejLi}0XH}6QfUv_; z+c@#J?l;JKiJL}}7S=jSd#pM7E`?)7U8_@?cSl{Aja_YXK)t|@^AaWFDGY-yRvzp6 zZl7hnO5}+0h8e(*4E&mU? z*M&YQ#E^ckcDe&ibV}Q7f$yr_Ge~uHKKFePjEp5?=$yh^s zWt$Hg_`~V<0Z6@{7|BbWeYf^k$G$gFM>cfFp4j$RW><32t4x-IlG_a^2MUN%!~WNlG|}Yf%I9fH zx~_kg9KDXb;byO+GRJ;8DbvfGz`s)<+5ZRWn?057uk4YOv4p`lhk9C!@IN%<2Y35E zDYnPg{_5Y&GIFF9;#Fh4TXpBq!kZwuttU;5sd~yx>9kS% z4vDrvdx!{=CF4j-6EkR{8xQNU$XJ}%Q^0zaXr5dQx2sRo-Ve!0(Av!Y%ABpa(ghV| zYu!BkgJL3B2g{pSrWiRCCdP>=)=-4;m!ZA3>FKN?91@JVGd5x*GO9}>K?|{lp3S)e=^HSOPhYvlCL@y)J z+eq|)$EvpZ-yiI+jei^AmSRn#hJ4{U`L)^4&*yR5rHmvdMBpj*ogzAn6OM-Qu04R);` zU2Cb{NJ@{Et<%sVI1)c+i_~b>7Ln1eE%KsW+X;)zu}I24Un*+pA0+Il-p1{vPQBGh z_OJSJ{M9F5CjcKhqHL5xe|Bt6ray(U&RoS<_VHPqTPP`)>J~!EOA8(4rG{jt%Cvut$y6wEmDQMtcCW!Q746zO8I@s~j>@o1 zNM+bfNz1ZK>VvNF&hk6HmDLFxRL2;3nGFfmSq`M7_)ojC!2Sdgv|Kwe(d#8@bF$a^ z@Ur21`n406_w^$yTKh2&<5>`iwbV;3LB3qxSy-U0_AKJ0U0WESU0dj{UE7gg=2)oz z&zFQ78V;7bOp^ZQJtySZdL?~o;{7O1nl0Bb#4JqtLZ7%DqdpW?J<$P$r~ipbS(HUzfdl}=EMu${}a7-=J6(N$3PK9?(7;5d&tPulIsxv zS-F`ETO8T+&)22@|9I`(%ueEZgb?#woJ6RrlXwverjQU`167^BDx0|UIcv6)sd5?2vX$lS%jA{! zCr;w0PG#baiMoX$1OLpS%o0^rhYyj?Zsn2aws>q*riQp$q)#nH8M^zXc0~2t;SU z?jyvzodMHHgZs=7Q`rvHek8D3|sQfkI3))yAGd9PmrVy^~%>SeisfHlSoIgc=0-BjM zH?0vO=O*ePA7gYLZMO+AcOY|TK7Un&7JG$wT;53>LC(L!4jNFs4E*~UIe>mvXv!F+ z&I|Dhz1~n)h~l+Wo5*w4S}NUC+M-{Q<{w1Q#{URT?}V6f#4O*tU8UWTQ%A_vLgn1L zE5xopgeU_)kLmWtv97$HQ_P%;Q9gg2N*!a|(db=tIitip7e<~Wl){#;ISPLmuM2eg zXg&6;v=C=>IXON`hq8avUcyoIa{ISJ6k?n}e29cfI=yAO>>%ugL!UDL*e?02U>0>G zq~hFZBf82w7uRqSYZ+I1Of&K}|`ubRqNg*O61RYlCwlY-zu`xE2|D?%vDs6u zG58dn7e?*_WcnxURF81%8uO09Mx>_wJm_uAL-r_e5*xSWOsq>%M=!eiL z?hLjNyPS5p5C^eE53q^5@H-MRTI5<@AKQw~LhCKk_T_XEs~zy2iF{rKUvC#7!m)?1 z;Kxeh)1%P24u0&Wj8DE4ViPtb>tP{^m@X3v6_daMJAjCxM&btM~rjvxY{j+`Tcl0e`JpJ^nC4@6{ zEeUOoVt0E%^O7g9B^F;*it?W1rG_rkHZYIr*fsc6s6O_g3bx=fvCR&Af-b+eatTq? zS&ontVmyQ>&m1HKq6-raA&2;xtP`+j*!YXyLW}}0ca9tI#DK$j854Qk-Ws1dMu=wM zJ7lC$YJbk|Cqz1A^&savh4K5-hz-!|;={1bT$g-`J?p{zvE`oQk?AS$A3O9_Ng;l( z;v{M^SHC{ki-Pc#<~P9w%`=vsN|nCu>z9+G7Xaah9;zLQi@99G!QBLb-)_no)>t@KwUVqe3)B zCI&d*ydSyp4pMdG@Aj-_TBd%7mp|xh2%}^YH1_?N*lM#74K5K^Ho#{VLhr(m33PmI zh!8jLAy+T4p&gKa=)drdYCQ}8)hR)xe|J&mCD7~GV{j!FTZeAWp-tyV))mM_f9Q4= z93}k4c)u=(m)O`08&#h1JA`%6i|ob~e1xAN$kvg!@E;l9%(=Qn4`t1x1+Ybvg&4XU z`63;I?6zQTkHJC0+dc5_6L2Yp-IyaoFn^u3MlpJjgSJNRzK zoWR_-SCTozVU>kgTvMgxZ%^n^2f91LROZnBrpnET9ZC**6F2OG7M}F^Ju>hHKIDhC zXWUfntK?Z>G&Z3PHd4Y`7Ni>13V`iWW4e))9SkvPF2c%)GgMVF? z3t3o3JUtG1I3`4u8me8UKUfdxb1v6O|mbS|JLcvnxI_>y-SMy$lCEmZfD_XOzTt z6sgQQ16!OO`dY!`5VGvT8i2l2QKvfkB_YjaAxgs+M;Nz={RbyxX@L+8pAcIh8#Bh^ z>)_*M=r;!5eGk76>G~qudo?wCDc0Uh%hcGh_*{;+x-F=PkCm`;C-eNAeG6pKXM+%X zyAq2*|F_V3rzI9R;6?w~bpr~6(=6Wkr|LL~ z8rYWNbxqm~sw+g{R8HdV88s)z-@wd{Im9DRRo(z>zW+KISlBF z40a7>{g+PV{M-uP1@C5oPZU0+Ju*Ee7M|=O7HNYVHWuQym&kZgZ2t}P9X-1Oedp@) z>D?867eO9>M3yYFZj2|-U9eFS(ltRgv5%=4&k8@BMg~_9BXSrpn>lU+hbl|KYa+H6 z8L7J!UyR&lETw9#-itlev@B%r>NaF(*OhII#e zw5^&DVI0?xRRcy0V82zr$UFkRpxb#Ya`5>BY!^09LZ?qu#liK|+4wCt#Yhp0!K06n zvGT}A!VrAI*Vs3Id}a;e!6#-tZ|HJ&8*}{woO%&|G5-;)m)3piBvzsCbCKcDp7>n! zW^`mYzCJy+c_nkdjBbz5i9cd}c9ijM%hk5vg=xYJaPj+Ds@za%F6N7MV z!0z!zidY@m{>^x9(E4Fo_P3CSV(9czcovE-3&wsr3|YWF$LjKJwzf~*e1zkfJk+>JEUm{UswMX5 z7uH57>Ra^Y@W=F#8NEc#k0NiGvg2ntrXzQc>2Jk6Y+NcKsy%~#{gJ(0#L3f$VbPIS zX;hC9e;YlkbAAgyWlk#iWx%4U*gq^HIzxH3$LqS3$7%ye<;f% z(fh3xWD2^KQ|GyiCt=tra&|3=TscuRMk4<&l}JT2;HBx9Gowa@h6cpjare z#E+_^I<~k;E&EyziwSZ17-OwsKL{FaMBle?{+xa@6-Nirl~17eoBFE%Wytso==%)) zc{7#06J%FHWDRwW*-IRFSf#DMPvN~sJ7O64cCaPB6PaB9hPAqns?{wM@^+VN>MZyf zfAB3a7&^YZ2|U2=4B)t5*X*%qKQk>;S^KE#6TjN$cfvMyXPiguCzM3S+Kof@If7q; zdyIYE>J?d2fqN0?Rff4Qplx6##wOuc}`P89ah4xuPdtGM{>98nI?&F4Zl5 zu=wZrUgV_~GO~2G5Lt$(_Sepv<9?HxeacSi`uQJh_aPsd8Nb0_W*s9{yd(D{kFp05 zsdA#2>nYvNj3B<0u%1})L*PgJQd|{$SZ!jVckB_tk2K6>=clYM*I?fUzQ30y*SH@H zU8Z7#U0u|e^S(^lrblIXqwjOBWd3envxm_f9|o>}7-qz1A%5o#^h$S=*kB)fYyF9>hO>t`lDLsMpFqFrlYfnMl>x0; zE5AYaJ8%xA?N!FEiR`S0=lRh0wb<3&=xG?zg9_iR-J;#KFw7(^E%usaV821}>dtcs*STGa!Zs1McOvp5II}EOcPDvU%iNf&W9s1v3 zmf2I|uC%X5oSNV4f3+Sjc+Z3;*n@TO{zYo$gx-(i*s+rNa2D>Am6dBbY%28Zw2OF$ zex4xH$IrnV1er1r#sXfA40-wO465|+9Oy{G#UI$IKz?+}Z)!&?mOIs1c zV=q>bHx-(^>y3R_%en>{A4TRJ!SxnC5E)xSoezbk=vV}N8Bm^mKhoio(L;2j+Z1A2 z8+`9hOk0{5O1A^D-`>&80UrJdUELp>BRxTfo=-qmEc6I3q{{2y-=-l47r^aD^5(rF z=E7%{+KPRG4-w2c;Tz(vs_b79BLy8 zf{n4!=<-B#%L>JoARD?~?0ld~Mj^xHUYfO{SdU!5?xX}anA>dZcy;KzAPk@Mg7F>V zRoQmfA$Se0OOf~8*cU5AwTDhERZhQJs;u>C_N|Vx_n47q9^iuk6`9BPtdDXb@7=ek z-1OKL1Ll+e92p;5f;eSAHVYd0a;!mL_OnjhUmHC^53I0U>KSnSDf=nRWh{KM!ZK(u z!6E~)>@ViE~qU2@7&Thw+uSK5fU>ma{ ze=otOVO{&NifsblG)|)VcI0Li=}Bq~`P&zHu)=rH`S0cUcVt9Df!1be4EsUL{#4g1 zp}7S9y_W{}1|qNdi3N7BM(e|V|8Dr>rutZ0NuB+3tGe>{@4tAK#uvYEnYBYv{1m*D za5E3|VqOvo$7v^#ANpCL!y96H=r?1RHCLtMnfoB*X1gnKtuEsQkY5RDcX5yNAbU9Q z>QzPdgld}oHe+rr-m+hCSF0+{aF3=aGEoK|o$twgFXUcAIQ*ATEE}{tqSD51D&}x& zFE+U==TB5_qx8fS(#kC2b=;GqFO)-zJ}=h6BcHbS!w_>~BBu@AN{^)c4c z&+xw+@mbiYaP(|oH||s5cUPga50I0Z*v~V_Z=v7JK1VddKEvx4$fXbRJKP=lW{$Ve z?L$AZw@Z30Ixb;teSG6!EvfzN#g4oWvVo5~?dw%SE-R>-W$qC3en;%mP+eaMQ|Fx) ze;){r)u8{<%!54b&~3mD{l{trhU zU!$9iq4Atm*j{+xP28}Zc?2UHr5JY_^A4%YSPh&+(^T*#j=k-*YFz7Ymt=UevXm)b zc>;K*1Kd)AEX*wmndy(szM{)q%P*0Qb!Llz1n%h~X9M=Yn_QC5s=s6KPQp8MGYhiV z{}O9#^!r_H_EDJM8}w^B^x~6A;tlO2Y=wprS~o%FMymACf$A)Om%uki=yD4ComaL0 z=^}kXGe3Qfk(Tv`Y`5bl$JXZcJBQfJEJ;4qz!LBMO&n3<0y^Zx{Ym1gIq=m5xg1s> z+u0l+e3Do?7yB8|`gT5Ka#HerCYS3oExBBk_Z-??dxBpMHS6SFgYF}H1{8!Zd$7}O z(5JS@T?GBgtH&8Nv0qjgjDGC?0>7!#>1*aMA?;z} zy0P$N1#7`^*uHS$kYU)Msn{Oy?>>ZB_AmO?{Zd)%ZexASYBuHtp9VvNE9k=5)yO$= zvg2rYQXTU5I{VjS}|da$viHiIswD74X5xpM+u1+kmOW;#SDC*?OhYr=XvNzlnFP(1^NC zp_Ktox3P9AjGz2i<;*F9-c>@s-k?8MRbD&r*?_)`AO^~z%c0R;p8IFQ*W%MsqZ{eq z-*9w3l=H8?#Brp{V;h{2PYD;ht8=`?wyw;}8ip8YI&yacJ74b?>iw>SaHm-wHR@ImFckD6il}b;$4E?~{_YN^k3-;n@8ycs|E{0%NH{r)vC!G2Q zxkG36Q1@^ka?0BE9P$u@-TNn~o`N6rK!*(|i=Mv5P8hHYy~+KE_0&&Bjym^2Mv5X! zZLl|07Qq{I!+>|-*I)|sf39-o(tbTUZ@?e$=@y5CH2PYlGPZOu`jeCETj+-Ym+PT7 zsj!{!d!rloVb-XUT-3=7?G1Q>E@izOD)0wBOIXjEL&Do=;`ln|ytK>@`0)vapUC3`aA=PV#W42@ z?1hX2_aS*$3v--@?h?Y$gH$tB+VOV-GG72Yx*Z#E2_AT@bF9HWmTrrV-&SSInBx}} zi3?wIJmbDcGv>?MK!Q8AQNl0Vv1!aTUXM9cuKL}*N}bQ@I=jH%TB_0mz^5PnxE^!3 z0pFrXdpq;I1Y>^H2D`_)?J)Xgz|cS=mGs;UUD|pa{{gM;z+VG?#U?jHXYO%+wwygA z&dW}q6P(+hWX~i!va%Ju=RBI2a%2}`1j>Hm*fL2zf5v@DVu+uh#WC^*A{2YU)@%?ynYbzlzRW-e`aDL5YP0_o317f5^ESq#UL$OIE81;#=XoXJopYS|Aj<-SVDeM zqPBd^FQu{o3LL(gn#?B+Y&Oqk-YxiN!wmy{Jp#P_e4E=E^O4kupspb{`%fzf`MrW( z9A+6Gz)ieRpK?etWHMhdu-RI8dGHUkk{yx>xvEaJcTe@SoM$CVww{*RZkB94Ewd|I zvh}q7a;t{1dJ|XgT`MrUw_a#zV*u-gmNtg4UTAMFs}am3DWi8YNy_NiOp-EsHIwqq zZ-XY5v`iY^TGBFUbZkk>q|vn{t-XJ*cdn)N9<{XIjh5Ei%BJ$L;$yisimiCbJpI}z zw&ExA^lPKoil@xeuho|(^pVwt-mkjQdr=pah5Tr=_E}>6^NE79;7gY3`%QdUJF27h zU69&RQFs-zsg@UjB_)r}zlIfFd&k zc*Ruv;XkE@hjk2#)BXukfLHSX^v&DX!za+w%QK}nP({%9>`~$OuCo?6Nes6XyZ;26 zQ!6v~v5vBC+ebW#|M>+wxq-av*d{OTneMBDeZyaTJeXJ;d!LiNp~bXYgzqYVUuwq@ z@s_pFP|C1LX|O}l^Vu7JfG;OjzwigKsyDV78+e)VhcWJy>Z}9UBW@#YFm~<{wyM!| zp0A<&Z{nQk-_TzIICN%>lO8*F8Xt~rJcFH^`ZZ$^?~Jd+zW%4&x1j6*cy7U;Oe3}_ zOU&O`m_9;U&65ssi)US86@Be7M)&h~nM3FeS(~+NA9XH)U4F(`j?fmLaq0~IWfne{ zu{%MFm6> zNBD{uzzUV0;LEGBPO`*ZMtNmok9#H6RVQd9q1%4=K83wOcyJ8f*+!A~iuK?<_LlHf zneb&2qKRLv@F#OLAfy!XFw(d}o{-0p8{rqSShJ=OFYuA2k+os>SrhIhUSjM=#KX9MI7KkvgH&7x27$&_XOj5P-`zlQW1 zVvk>RS!j)%T45~lX#QsS+YHuREgd>ri1xViTSo}l5%+6Mv-NXs<$b|+DlE>5`%a&a z@R{j`&M=!FpQ+N_P$L7<3};Kwmynw2?eM|fUqa33b8aJ+r`HKndJx{+<<`ti)nYGU z%_YR?^Qo{`WYBJZ%dOJr0^D||pE;1O&la|Dz`>372E41l61X^1x3It{ljppM*oqM< zClWS3S-|7S%+b3GOGd^%#`Q^5TEfL8EGP3YK6puZnnsMVbs)7SU&SSrz8zD_RT@kfjUo(6mpr&7gjEa$7R zZ8QM<4|BW9U*%sa4gGKt#ZccqKSOtpz5`kIfUgzq_cE{UAdA%paw`Zmj7N4SwpL|p z$EtHtU))J8mG1rDSX2G|<-_<1z3HTQ1qWosb(0t1rd(ZW*G(yfPopta}!83OT-(fE9vnrr|U49Z82 z8pMb4EpibRAE&2O1zQ~72x+3|qkY8P#{U3=q86kZ*&}UsOo3d2{ipwZB`gXwZB3hd zutaMnqWDU>kP>EV9^mQi_aArMF(!_G)uH{L4v%I5-c9|5&C|z+53L#h3PbR(Fv9rf z%rS9lIBI!%HEd3eh_K*rVdL7?+{+`dnU~GO-{0m3az1|Uo}lIK>*?Oi)8^~m)QjpL zR?p_)7w8-Cp4w_8z0a23zKE|^vu1wHgkNNMFVQkAQtFxZ@0~(|h) znTMCHsk^UtLt=ZC>yQ`0zwMU)=FQf^!{+P9MBM{@0(`t1q)gw=ZTWb=vzs$tMvabJDNQ! z0-0&c%sPcJxCW%cIJYmOuIAuGBxLHyb5Yf}XYr}JHhy>F2(MhjQQs#h97y0Z2xE;i zN=p!x5|sK~l?>J$#+fhFb=^JeuLa?>r*dDG!x7Sq;*o7Xm0P|p&aN3wqAdHl)Lq$9 z*oM@PhSrQfeJHf7!I;c*+pnrlZOW|B9J)&w{D8f{M=E{( zW7dGxRa*Ymyv3d>*KV&^G`JDqZ8XcifksoCGOiE7`CjU7Fw2b8|1FD=Hme8%k;@Y4 z|93I+zl)LoU5xy{wip?A&9F%3S-yWG;jC^~BMz!(DpW4devM<1y@dU=bw&<*VZ(Xu zatqI-;6uJGi*3EiLhB>$3x9@f{~SAr-Sr~g1}gfjSoE%>Kxb=b>n%p$k}c+6z)uO|z1Pc|+#(>Hy+ z0{@~3?9JZfo;7~sPyG0E&i(ZmpwuEYhWu>;J`#Q&#;%odx z6ZY;X@AU$m!(VR#pKqXvgw6PHD||&C5=Qm`Z}9rF6q_dpcovyCKPZQf8^Lo<_^BJu zsnnG?DKi@~@Mr}-wXZw)XW?FXJ^U+UY(Gic^7t&`jh5iQ@k^eg?axL6cr1LW4I|p0 zqK}J=HHGJFnzES!o)Qk^U`}!j9n#Zo>UDMD4-NOj$6u}*rEH>s_jG7n2wy2-2)G4) z4}UuoBeCcnS3%{c|5%-);f;jczUn*!JSzvXuZk=*ISwzr<6d|gRVIHQj^v)Ajs3-+ zi4poC3r@%lvBR6Zs*XEjZ(<&&J~G?LRAuP9vLo?ML1dMAOYlYZ8biwsEr<=6cW-34 z4z#*ei2n0HuZLVy_fjb~KZsFY--Wjk_Ka-H#qzK@KQ$f65*${K-LC8{(j!CV;O_-+0=8T|{D*6y&pp<)DXzLyUbC zx%n~+8w?5XqapphrtNL5So`S-o-@yJzjv_8OAjBNph@dP@Dv_I-ZASMsh!Bmz|X<^ zG_lhx`W(oHO<~4hY>#x@4{rlc3h<0<57N+}C3r+b&+PDP>lWHFj}ld(MRVkPI?rDq z^YZ<(1v%Mh*?|6X{wNw8e4qn$_tQ^aWZKCa9fyvQjo3RzzCshwMam9)G8R0N5TWZ@ zi)-{d99g~3ds1F(tlU69Gw@v43+}THMK+lCjCra}hHy6fR-i*o;4S0l0Ot*#p~K*p zWh(XvnOnDuzM+X1yu4fn`Iy1;b&QoZ1b))?@AK#cI%B}CjNr_iyE1N2JmW%p31fBn zQ&()T{YXrH3%zY`k^$!W&@bR@Q=7n{ffRawoQ%mKa+03QiHJ<;!lDy>~Z%SzDg z@hFvdjdbn=v)p?K^w6*UH?hgv8@$V-_sm(s-r4j=pAyPWgzno_+I5C{o=jfF&0&)c z`TQOGT8O%(YGQ*o(k_RpJKRsO1{d2>NE&P$eHVcD&r3q{1Z>4UHkOgKx4vx7f#Wv;Y$i^E?w8q6 zC0%U*<6TF#Ijk@c9PT4qwUFaO9Pf~el0#LE-Raf2JUCimZ+YH%n!=dQ=>1^)4|9xN z&pe>#9NKK(q-rnU%ylzlI}diEFE)N4w&N(9o#dZGwk7OC=B+Ruz9ulYQqb@ixJW1k zos0Z{tUXZI#mEmuCmJ`!uESdcR#R`=Vy=3qoJ|Qv33f4Dr619*WjE&dIes6R`Di5h z$b^Jt1=U&pu0Y;?tHm?t;B$5#^nvE1sQb1ndVhig-n_j_On~0~@`u?@rtS3eTpR-npbH0xBlaWL7g-lhgn#t2b1b}N&Q9Qd zi+-v@*Df{L^!)@M=z*R>_g`*tp9oq$Mt6N*F-{4Ifr9;e@m0{eV62hDoOS;1`xri5 zLsrKz4;wsWUTM&m_D6^#Mw|6zY9(W@8Hn9$fIWdGUZL0^&O4xE?+9p(uD$Ha&s;nBS4Lg~Srf{bE7xXMPLr56s@KPs9HpLy`4)j|TfD!3kX7fWys8 z*sx%DpH1b<-&0#udK2`i+lM$gExMJ7wvSYPR}bX%27VqHxHOuZJQ4dZmf(=5IdbdEO_w~hr)?kdrb1Ww{kq(Yvt=ax#1da{%*%@$fa4=4ZjyRq zH=%VKWURz#_)rG@1Gfv<&g@s9J@dB$O+?0i(5^bTFCjKUCbK_>XSA_Gn(p{7_35=0l~DhLWvjFE8>qx><%>R z0RC%`sa(kEzBqgkJRJNB{`#_g{hSSu$5r-umFAn*F&8l7&9S{zi6a-{BT09oPx<`f zRr;EE3m^3gp233*=v2=b)%Ha#d?fsA!?>p?mmuH!?163?;1W)ZKs^bEXREXP?Tfy} zjbSer87euRSPVJ7ogQA1E{5z*n}F|PTq{guED0e#&^fhAXQ)8zxg4EIOKgiQ<$>2L zm_sRKmVcS5fm?5N{xbx>R~A`dUOkY}_34oDW~{X+u67<=$P2v3`ZWTdIT`&Og3fjT zPk7_D6&<*WzhS)czpDP^^QT`ACa!p?uHQXHh9vLc$#?WCqKNP4Z*m4~>{L}p{_eO* zY{Q^4!FLa1p7uf};pzPb=v6)9n>xgji;0t0VDHY5uggk*`nSTWaPun1TE;>D8lj7$ zk<*db!Kl3G8g%Y51{vIdjxy%Q@U_Mjvz<&0gVt8~2AwEJe{X9eUzgAo`kJ~4|9~CK zi427ACQfR|&FMYp%1LB|KAS?b4xLpmm#9C3cJntfHuUZ~mUtW7_LARsA~tOcz7|^c zNyYm5XZWP^N46dS9VGmX9Arm-4LA>fB^+S}NOT zgN)vVPh)%FSCEr@KVsXN*97oO$jAJUlO4IRkFsFsX>UX4B)TI`Qd&mS51jM_n*HjO=l?(otFdlrs=yAGeB*Phg@N0GB8 z8;MP7V0)m0GrC+JIy?s7&+c;F1bcxV)$0o_OAuG)Wi5<67Gj*;ccATW;D%fpu&ba- z)t7SJ0zWpCHBv8ZA?ZcTFDv{Hy>HaU57YlqK`IHb zK4l(O_^CQ^eH8lmF}4obJIfpgA|D5_Beq(q=8w!NYAz4-#1P{mAGZb~Hw(;OJ}R!# zv!T0$T^v30Bd^e)Dtv91jkvP_?E|sJ^q+S;doIYr68e%bDOjE5?~!k?xAR$ZVUr}> zh7U(?qesZdg3quk$mGs`_?}g&d<$~v9Y(t)>iVnJ*ofa$`uqBfP1%BZJPfj&we&jd zQC{}7T7icbJ_B3O1^&A9K%P=luN?D>#h*a)5y({#{9RX9^(BA3*Q@j$aId@)dxBhB zAvbOApcjr%_7nUa^EY5rVa6zroJM0SGUI=kvxN3PBjc%%pUJE>p}zs^sGDm9v3W9m z`ydxfpzAj1D!~(aNytTC*}y%J`Sq-b-K&Dlxd~6;W&Lj08~8E;y$DZFIe46Jf^EK! z{y^Kc3*k4m@+P!3AUYnJPL?I!KgvP8Rw*nVG?-rfm$ zfv?%zp%t<>16y};8tZ3dVs1M0h%u(Xy9-BEeffKtd0w-~-|f+QUv6fdAJ!xDUD1zS z_>7Fil-Q+x^N|ulJcd@64d zIOaH|()n~;p23#~lwYfiZjx7|5qrpO8D|l)bA^YTpzoChs!jMho&($Cu}%4l-bSt^9Ht``8h&a%7-PBZ`51$qmfeQ zOWlc6bh)-KA9=(s_|Elf_&=F8-;{&C_mLNL z@c=k2WjqO~komLih@EKj;t0NWrposl$-J@?M>7{IbPC5GVaM|H#wMl4-s*ZH>ufE? z^-ybVmEQKLI&Ut6ZeCAXE^~hCg3X7n2J9b&obcL zh_U21X0IU6qgEI#&2y?I=v+bMl{zb$L-#EB0rGa1f_FKQ5oECh{AvYnex1j@%R+Pu z*||9k8w8J<*2cDPBo<-J^PHnMW2aEIKj2yE=V}}u=eiZfMoDpGxUex?d1J zA6oHpj!4K)Y}bN$fq&aA@Hd#gF0%heo_7N6UzzQLzhq56hPdW2`!C2%Pk2?u3%_;S ztYc06u#=dnk;-Xbww{ldyX#v1b{TUe*A=0=*=Bi!K=;|DT71jDD;OC$I-en?)|n75-jZ08OC5 zfC08+286<+21IN?Sn>nTB0LG72Hts~$!=)6qAKg(rRYimalDS_57lKa z#7g7US^kz|dG2Uf;UPei8>fA;Sp5|vAGf3seey7e0x)Y~t$HsW5>-2hgoh9Tk z=O*Ab9eMvI4{-_j9zs`oU{{>MvBwVNs1E)E{AQtB@_o`N_gNz#yCYf=6JBFJzo~wA zY=xddOho3TGN6O(xk#z}KHmGyKkJR~l#=G7hzH_Xei|x|!tgp{s*7|q^I#0O)PZt|0 z^IQQMAC5GyQ%b$rsF&=HF!Y_`HqVLz51xs zgH*)w?Xl^dkPYU2Y!dUOsMavzPHb3(J?P4LeEIK2OY__hS(^H>x}IF$%ykjT%t#G~|5lLyW2zi-d>{U-{{sEGMSO}p9vX+eua1mnw$@hZ)mhNff!GV= z?GdTdu2xtE|E@FlblBNuufXHJwKVzk32>C~{DnFnSdFamR{9VhK;HqE;8j6rKZNzwX#6QOtzQ>D z>2}3vJ8&bm4W6uphWo*zWm)i|jf9=hCmZq_b^ty+V$Z1s@=^%=uw%2~^8`2K#)-8q zbD#4KeuH_w*`eBQiD&=MhCEzDj~w{dITvdx_~cHz8}NDS0_@~;{1bh+fO|S@**3=f zEb5>lnRZ_Y?KWW7wqr+h z8l|3u{DxyA;X~uB#JM`3To)m0=&Xc1&|*9CHHf@tsjy9b(T{biY;~kM-%n6y$KL{Z zkx%5OO$_V*GiGT8WMv;Z*ADsJ7ESCE#$0!rO#vuO#(L*2BF1cB= zWIzXo(mswjE0}fXTyUR=uk1s70d4n#`$l9_!n~8}Tw{bfM~sJ`YgO9ucPo182%E|= z{x0MzJL?zdP#n2qT4Dq^{lIm3XeU87&3^>lc%WU1VZ*6Y7MoX3UCZAm{kboMK6E&Q z4?WNPkfj1ISxcZR+c~Uo4IX{$%o?l|>!e-81bbBJ<6*=BtB~VL@Masj^%K4oI$jt@ zUkT`wx2h+9{~EwL0ht|J3_6C>PY>|jh>ZUMt{)>mw3AR_J@)7!x>Ow63}Jn?3q6|7 zz7X|l_@H0#un6sQR6t(f<12W!x*I(4B@TyA74x7UUdV_mv0pZ=OYP@Av|pDN{pw}r zEyPa``3;*ii1?7c<8QME`@k&!6Z-g<#nz6&pC>R+<|n}|0-1n*Rv5&XcMF&~@;jAn zT+C~0N`5c&%d6PvUsdh~WH3E==LLrkfhw1gfuqd340aXSGobP;;`dL$c{KceVB}&C z!S_TJ+u7!Sd_UVyB#_}LvVf6l>M@6W}CZXi3{M>ykXpDMsEBw6BC}M|2yz? zGJ4kvA3`54W!VeZj16p$e|l)P&qRF*wZUgj8thk9b#49q%bzt$RI^$t&bl z!gA!oHU?fYpYP_VJo&pCURvQgI5dLR?HK1GGLv>7^t=I%&#kponwNIOe0OvnSxMC% z-;5nHUT;h4+)IH+U&^a7?<+5fH=u`a zkZes1xE?>-kG1mzb$x}pHJW30p-aXPV*hlkr$6DI5!Y=>;uA7rM@nET(&Mv#Wc`Ug zUPnG&z{ia6Dd$4`bvD(P^>=6jTllmMEKU4*o*41CwN$0cgU6_dr1?h9KdPDAI<80Wk8$kGV>WErCbJ=lv~%8wlxh3;oG^JVIbuhsQ@a4}#? z0rm?EQwEP``r_}Pd6DJF6LOqMr(?0SMr~|L4r1YY>Ut_;W%VIeFM`0J(fD%aSm-*sl$rZBd6^r!xu1D-uFYP-9Bi&T@f^Chy*##X zBQn8U-d$wP_6(mx`EurP{VB5d!hXDB%*lZBj5k8pFPZ0rUud|-C{<^ttvm~XY)Wu# z&NEYAsdOJ?QNl9H%3*irA47(~xdDe2IzUeYdVqTXbQ@L<8a`6F6QJQ-bogZk_+5p) zRAeKjFE$xG7m+s%+5BSzxH0z}yU>%H$m$n_$#<{V^a9&;!k0bK*(?MTK)*7oGXCaxn54i~>kMobg zms|KyypO`IGV)V$3O=qb^X$jE^McAhI34|`�?`l!_cDT;u%bpB0ce;fi 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 z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj.DELLM24.guigui.user b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj.DELLM24.guigui.user new file mode 100644 index 0000000..61fa7c7 --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj.DELLM24.guigui.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/vc90.pdb b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/vc90.pdb new file mode 100644 index 0000000000000000000000000000000000000000..06bee27f113fef2db8c6d68aa99cc1b6ce820d41 GIT binary patch literal 143360 zcmeFa37i(iwea2Z%m^bOj0l2?iioJF2uNJe;IN1&hzN{(gkffY(P3tsh0P6@s4$X>BqnO2S-gocUKir4W-~^;YSe3tNnCTY`o`~ny83jTe!A$H8}I#n-}k;x z|9(w9Rj1CWQ~x?uT~ANo0^Fwf*#F7>tMsixphBQRphBQR zphBQRphBQRphBQRphBQRphDoi_x|z5Ki-h%`{i{1$Om^+Z*r+{l20}r7i~3?Hv&qo zhW^{!y5@kWLH@0;kKP&-mFndEfk78V(S%_A-q!XW6ygMpciu*)YPk4F1hgL;ATk+87NRS&9Z?FQ!8uw?$`b@*SNmU5z~*U1Pg@x_X;> z#yT3YylN2gWy_jZw)HG)qRO(SwvO)Ju9ir+4~*uP1}FZB|3jSRlzZN1&mk}KOegfS zKQfjuEDg)}5E{!;op2qriIWkO&sEXeZ_N$HLQ`*ZgXb33}$H1^DC>28V| zg2gUYF2~=JcoO9*&2MdQUeda%y``DY8R<4HbiJjrK?Cy6upJS18de;!F&b37FKr9^F{bZzao>Gg zp|&S(+2@Y#M4_YG#hcX4$Gwc^qFmicTeN$``kt2d9%tHp76u1q=H9y2<{sbYnrL-P z>#Egp3@Vq}sJW$+I?|5#w43SokhtIBcEam$)9S{qDYztw>+8_OA7^%byneGRmd2ZQ zm{U2uu>IboqD!T}YJaQ@IculriTyn@_VapS`(l2o^M9)If5$Tag`1eJVr>>1CEA*3 z+G6hKY=qm2eWEd!N?)|CvC@^~Iv}5ITl;0&%G(IHB|dR|j1$){U89{jcg@j$X&+0h z5n@l`VzOzMwJTsRbRCcVlMA!9{Fb&W{VMOHv@7Md-2*c1hU|sfmFA|o{!VlKomw!C zw!iwhnl(}SE^EPo#)q1??^)Yn=CQ9-bg7K5`XGHQ>br8VJ{*$qA#XeEL-*>At{x_& zOT`DBFWV+Qh@Q-s!;Jk>l-CQdZ@pSE`QTV<8@nZKh_1A8c&3f8Zb3hoD{*r≫@E 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 D6O=Ul literal 0 HcmV?d00001 diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/main.c b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/main.c new file mode 100644 index 0000000..503faae --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/main.c @@ -0,0 +1,68 @@ +/* +GPU Implementation of Keccak by Guillaume Sevestre, 2010 + +This code is hereby put in the public domain. +It is given as is, without any guarantee. +*/ + +#include +#include +#include + +#include + +//Cuda +#include +#include + + + +#include "KeccakF.h" +#include "KeccakTreeCPU.h" +#include "KeccakTreeGPU.h" +#include "Test.h" + + + + + +int main() +{ + + + + Device_Info(); + + Print_Param(); + + //Test_Completness(); + + /* + TestCPU(1); + + TestGPU(); + + TestGPU_OverlapCPU(); + + //TestGPU_Split(); + + TestGPU_Stream(); + + TestGPU_Stream_OverlapCPU(); + */ + + + + TestCPU_2stg(80); + + TestGPU_2stg(); + + TestGPU_2stg_Stream_OverlapCPU(); + + + + TestGPU_SCipher(); + + + return 0; +} diff --git a/src/amd/stress/__init__.py b/src/testsuite/applications/keccaktreegpu/__init__.py similarity index 100% rename from src/amd/stress/__init__.py rename to src/testsuite/applications/keccaktreegpu/__init__.py diff --git a/src/testsuite/applications/keccaktreegpu/keccaktreegpu.py b/src/testsuite/applications/keccaktreegpu/keccaktreegpu.py new file mode 100644 index 0000000..919a3b0 --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/keccaktreegpu.py @@ -0,0 +1,119 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.TesterRepository import Tester, Test, TestData +from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from testsuite.test_classifier import TestClassifier +from testsuite.applications.keccaktreegpu.keccaktreegpu_build_amd import BuildRunAmd +from testsuite.applications.keccaktreegpu.keccaktreegpu_build_nvidia import BuildRunNvidia +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import execshellcmd + +import os +import re +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, cwd, binary): + self.cwdAbs = cwd + self.binary = binary + self.app_path = os.path.join(self.cwdAbs,\ + "src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/") + self.thistestpath = self.app_path + self.prepareobj = None + + def buildtest(self, logFile, platform): + isBinaryPresent = True + if platform == HIP_PLATFORM.nvidia: + self.prepareobj = BuildRunNvidia(self.thistestpath, logFile, self.binary) + elif platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.thistestpath, logFile, self.binary) + else: + print("Invalid Platform") + return False + self.prepareobj.buildtest() + + if not os.path.isfile(\ + os.path.join(self.thistestpath, self.binary)): + isBinaryPresent &= False + return isBinaryPresent + + def clean(self): + if self.prepareobj != None: + self.prepareobj.clean() + + def runtest(self): + if self.prepareobj != None: + self.prepareobj.runtest() + + def parse_result(self): + if self.prepareobj != None: + return self.prepareobj.parse_result() + return False + +class KECCAKTREEGPU(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"keccaktreegpu": matched_with_names}) + +class PERFORMANCE(KECCAKTREEGPU): + def __init__(self): + KECCAKTREEGPU.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + KECCAKTREEGPU.add_matched_with_names(self, {"performance": matched_with_names}) + + +# Test keccaktreetest +class keccaktreetest(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "LinKeccakTree") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + perf = PERFORMANCE() + perf.add_matched_with_names() + test.classifiers = [perf] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== keccaktreetest Test ===============") + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/keccaktreetest.log", 'w+') as testLogger: + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest() + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL diff --git a/src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py b/src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py new file mode 100644 index 0000000..5cc5777 --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py @@ -0,0 +1,64 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.applications.keccaktreegpu.keccaktreegpu_parser_common import KeccakTreeParser + +class BuildRunAmd(): + def __init__(self, thistestpath, logFile, binary): + self.thistestpath = thistestpath + self.logFile = logFile + self.runlog = "" + self.binary = binary + + def getenvironmentvariables(self): + envtoset = os.environ.copy() + envtoset["HIP_PATH"] = "/opt/rocm" + return envtoset + + def buildtest(self): + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + print("Building keccaktreegpu..") + cmdcd = "cd " + self.thistestpath + ";" + env = self.getenvironmentvariables() + cmd_build = "make CFLAGS=-D__HIP_PLATFORM_AMD__ ;" + cmdexc = cmdcd + cmd_build + execshellcmd(cmdexc, self.logFile, env) + + def runtest(self): + print("Running keccaktreegpu..") + env = self.getenvironmentvariables() + cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary + ";" + self.runlog = execshellcmd(cmdexc, self.logFile, env) + + def clean(self): + print("Cleaning keccaktreegpu..") + cmdcd = "cd " + self.thistestpath + ";" + cmdclean = "make clean;" + cmdexc = cmdcd + cmdclean + execshellcmd(cmdexc, None, None) + + def parse_result(self): + return KeccakTreeParser(self.runlog).parse() diff --git a/src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py b/src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py new file mode 100644 index 0000000..c015ffa --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py @@ -0,0 +1,67 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.applications.keccaktreegpu.keccaktreegpu_parser_common import KeccakTreeParser + +class BuildRunNvidia(): + def __init__(self, thistestpath, logFile, binary): + self.thistestpath = thistestpath + self.logFile = logFile + self.runlog = "" + self.binary = binary + + def getenvironmentvariables(self): + envtoset = os.environ.copy() + envtoset["HIP_PLATFORM"] = "nvidia" + envtoset["HIP_COMPILER"] = "nvcc" + envtoset["HIP_RUNTIME"] = "cuda" + envtoset["HIP_PATH"] = "/opt/rocm" + return envtoset + + def buildtest(self): + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + print("Building keccaktreegpu..") + cmdcd = "cd " + self.thistestpath + ";" + env = self.getenvironmentvariables() + cmd_build = "make CFLAGS=-D__HIP_PLATFORM_NVIDIA__ ;" + cmdexc = cmdcd + cmd_build + execshellcmd(cmdexc, self.logFile, env) + + def runtest(self): + print("Running keccaktreegpu..") + env = self.getenvironmentvariables() + cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary + ";" + self.runlog = execshellcmd(cmdexc, self.logFile, env) + + def clean(self): + print("Cleaning keccaktreegpu..") + cmdcd = "cd " + self.thistestpath + ";" + cmdclean = "make clean;" + cmdexc = cmdcd + cmdclean + execshellcmd(cmdexc, None, None) + + def parse_result(self): + return KeccakTreeParser(self.runlog).parse() diff --git a/src/testsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py b/src/testsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py new file mode 100644 index 0000000..86aab4f --- /dev/null +++ b/src/testsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py @@ -0,0 +1,43 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import re +from testsuite.common.hip_shell import execshellcmd + +class KeccakTreeParser(): + def __init__(self, results): + self.results = results + + def parse(self): + passedts1 = False + passedts2 = False + passedts3 = False + passedts4 = False + + if re.search("CPU_2stg speed :\s*\d+\.\d+\s*kB/s", self.results): + passedts1 = True + if re.search("GPU_2stg speed :\s*\d+\.\d+\s*kB/s", self.results): + passedts2 = True + if re.search("GPU_2stg Stream OverlapCPU speed :\s*\d+\.\d+\s*kB/s", self.results): + passedts3 = True + if re.search("GPU SCipher speed :\s*\d+\.\d+\s*kB/s", self.results): + passedts4 = True + + return (passedts1 & passedts2 & passedts3 & passedts4) diff --git a/src/testsuite/applications/mgbench/__init__.py b/src/testsuite/applications/mgbench/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/testsuite/applications/mgbench/mgbench.py b/src/testsuite/applications/mgbench/mgbench.py new file mode 100644 index 0000000..1560842 --- /dev/null +++ b/src/testsuite/applications/mgbench/mgbench.py @@ -0,0 +1,242 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.TesterRepository import Tester, Test, TestData +from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from testsuite.test_classifier import TestClassifier +from testsuite.applications.mgbench.mgbench_build_amd import BuildRunAmd +from testsuite.applications.mgbench.mgbench_build_nvidia import BuildRunNvidia +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import execshellcmd + +import os +import re +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, cwd, testpath, mgtestfile, binary): + self.cwdAbs = cwd + self.mgtestfile = mgtestfile + self.binary = binary + self.app_path = os.path.join(self.cwdAbs,\ + "src/testsuite/applications/mgbench/") + self.app_root = os.path.join(self.app_path, "mgbench/") + self.thistestpath = os.path.join(self.app_root, testpath) + self.prepareobj = None + self.apprepo = "" # Default + self.appbranch = "" + self.appcommitId = "" + + def set_mgbench_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["mgbench"].repo_url != None: + self.apprepo = test_data.repos["mgbench"].repo_url + else: + validrepconfig &= False + if test_data.repos["mgbench"].branch != None: + self.appbranch = test_data.repos["mgbench"].branch + if test_data.repos["mgbench"].commit_id != None: + self.appcommitId = test_data.repos["mgbench"].commit_id + return validrepconfig + + def downloadtest(self, logFile, test_data: HIPTestData): + ret = HipPackages().pull_repo(logFile, self.apprepo,\ + self.appbranch, self.appcommitId, "mgbench") + return ret + + def buildtest(self, logFile, platform): + isBinaryPresent = True + if platform == HIP_PLATFORM.nvidia: + self.prepareobj = BuildRunNvidia(self.app_root, self.thistestpath, logFile, self.mgtestfile, self.binary) + elif platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.app_root, self.thistestpath, logFile, self.mgtestfile, self.binary) + else: + print("Invalid Platform") + return False + buildstatus = self.prepareobj.buildtest() + if buildstatus == False: + return False + # Check if test binary is created + if not os.path.isfile(\ + os.path.join(self.thistestpath, self.binary)): + isBinaryPresent &= False + return isBinaryPresent + + def clean(self): + if self.prepareobj != None: + self.prepareobj.clean() + + def runtest(self): + if self.prepareobj != None: + self.prepareobj.runtest() + + def parse_result(self, test): + if self.prepareobj != None: + return self.prepareobj.parse_result(test) + return False + +class MGBENCH(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"mgbench": matched_with_names}) + +class PERFORMANCE(MGBENCH): + def __init__(self): + MGBENCH.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + MGBENCH.add_matched_with_names(self, {"performance": matched_with_names}) + + +# Test src/L1/fullduplex +class mgbench_fullduplex(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "src/L1/", "fullduplex.cpp", "fullduplex") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + perf = PERFORMANCE() + perf.add_matched_with_names() + test.classifiers = [perf] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== mgbench_fullduplex Test ===============") + # Set repo info + isrepocfgvalid = self.set_mgbench_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/mgbench_fullduplex.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest() + # Parse the test result + if True == self.parse_result("fullduplex"): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test src/L1/halfduplex +class mgbench_halfduplex(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "src/L1/", "halfduplex.cpp", "halfduplex") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + perf = PERFORMANCE() + perf.add_matched_with_names() + test.classifiers = [perf] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== mgbench_halfduplex Test ===============") + # Set repo info + isrepocfgvalid = self.set_mgbench_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/mgbench_halfduplex.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest() + # Parse the test result + if True == self.parse_result("halfduplex"): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + + +# Test src/L1/uva +class mgbench_uva(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "src/L1/", "uva.cu", "uva") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + perf = PERFORMANCE() + perf.add_matched_with_names() + test.classifiers = [perf] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== mgbench_uva Test ===============") + # Set repo info + isrepocfgvalid = self.set_mgbench_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/mgbench_uva.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest() + # Parse the test result + if True == self.parse_result("uva"): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL diff --git a/src/testsuite/applications/mgbench/mgbench_build_amd.py b/src/testsuite/applications/mgbench/mgbench_build_amd.py new file mode 100644 index 0000000..6f7d038 --- /dev/null +++ b/src/testsuite/applications/mgbench/mgbench_build_amd.py @@ -0,0 +1,74 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.applications.mgbench.mgbench_parser_common import MgbenchParser + +class BuildRunAmd(): + def __init__(self, app_root, thistestpath, logFile, mgtestfile, binary): + self.app_root = app_root + self.thistestpath = thistestpath + self.logFile = logFile + self.mgtestfile = mgtestfile + self.binary = binary + self.runlog = "" + + def buildtest(self): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + # Build dependencies + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + print("Building mgbench..") + depfolder = os.path.join(self.app_root, "deps/gflags/") + cmdcd = "cd " + depfolder + ";" + cmdbuild = "cmake .; make clean; make;" + cmdexcdep = cmdcd + cmdbuild + execshellcmd(cmdexcdep, self.logFile, None) + if not os.path.isfile(depfolder + "lib/libgflags.a"): + print("Dependency (gflags) build failed") + return False + # Build Test + mgtestfile_hipified = "hip_" + self.mgtestfile + cmdcd = "cd " + self.thistestpath + ";" + cmd_hipify = "/opt/rocm/bin/hipify-perl " + self.mgtestfile + ">> " + mgtestfile_hipified + ";" + cmd_build = "/opt/rocm/bin/hipcc " + mgtestfile_hipified +\ + " -lgflags -L../../deps/gflags/lib/ -I ../../deps/gflags/include/ -o " + self.binary + ";" + cmd_clean = "rm -f " + mgtestfile_hipified + ";" + cmdexc = cmdcd + cmd_hipify + cmd_build + cmd_clean + execshellcmd(cmdexc, self.logFile, None) + return True + + def runtest(self): + print("Running mgbench..") + cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary + ";" + self.runlog = execshellcmd(cmdexc, self.logFile, None) + + def clean(self): + print("Cleaning mgbench..") + cmdcd = "cd " + self.thistestpath + ";" + cmdrm = "rm -f " + self.binary + ";" + cmdexc = cmdcd + cmdrm + execshellcmd(cmdexc, None, None) + + def parse_result(self, test): + return MgbenchParser(self.runlog).parse(test) diff --git a/src/testsuite/applications/mgbench/mgbench_build_nvidia.py b/src/testsuite/applications/mgbench/mgbench_build_nvidia.py new file mode 100644 index 0000000..7f474ab --- /dev/null +++ b/src/testsuite/applications/mgbench/mgbench_build_nvidia.py @@ -0,0 +1,82 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.applications.mgbench.mgbench_parser_common import MgbenchParser + +class BuildRunNvidia(): + def __init__(self, app_root, thistestpath, logFile, mgtestfile, binary): + self.app_root = app_root + self.thistestpath = thistestpath + self.logFile = logFile + self.mgtestfile = mgtestfile + self.binary = binary + self.runlog = "" + + def getenvironmentvariables(self): + envtoset = os.environ.copy() + envtoset["HIP_PLATFORM"] = "nvidia" + envtoset["HIP_COMPILER"] = "nvcc" + envtoset["HIP_RUNTIME"] = "cuda" + return envtoset + + def buildtest(self): + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + # Build dependencies + print("Building mgbench..") + depfolder = os.path.join(self.app_root, "deps/gflags/") + cmdcd = "cd " + depfolder + ";" + cmdbuild = "cmake .; make clean; make;" + cmdexcdep = cmdcd + cmdbuild + execshellcmd(cmdexcdep, self.logFile, None) + if not os.path.isfile(depfolder + "lib/libgflags.a"): + print("Dependency (gflags) build failed") + return False + env = self.getenvironmentvariables() + mgtestfile_hipified = "hip_" + self.mgtestfile + cmdcd = "cd " + self.thistestpath + ";" + cmd_hipify = "/opt/rocm/bin/hipify-perl " + self.mgtestfile + ">> " + mgtestfile_hipified + ";" + cmd_build = "/opt/rocm/bin/hipcc " + mgtestfile_hipified +\ + " -lgflags -L../../deps/gflags/lib/ -I ../../deps/gflags/include/ -o " + self.binary + ";" + cmd_clean = "rm -f " + mgtestfile_hipified + ";" + cmdexc = cmdcd + cmd_hipify + cmd_build + cmd_clean + execshellcmd(cmdexc, self.logFile, env) + return True + + def runtest(self): + print("Running mgbench..") + env = self.getenvironmentvariables() + cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary + ";" + self.runlog = execshellcmd(cmdexc, self.logFile, env) + + def clean(self): + print("Cleaning mgbench..") + cmdcd = "cd " + self.thistestpath + ";" + cmdrm = "rm -f " + self.binary + ";" + cmdexc = cmdcd + cmdrm + execshellcmd(cmdexc, None, None) + + def parse_result(self, test): + return MgbenchParser(self.runlog).parse(test) diff --git a/src/testsuite/applications/mgbench/mgbench_parser_common.py b/src/testsuite/applications/mgbench/mgbench_parser_common.py new file mode 100644 index 0000000..6d6fd8d --- /dev/null +++ b/src/testsuite/applications/mgbench/mgbench_parser_common.py @@ -0,0 +1,52 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +import re +from testsuite.common.hip_shell import execshellcmd + +class MgbenchParser(): + def __init__(self, results): + self.results = results + + def parse(self, test): + numgpus = 0 + gpumatch = re.search("GPUs: *\d+\s", self.results) + if gpumatch: + gpunum = re.split(":", gpumatch.group(0)) + numgpus = int(gpunum[1]) + else: + return False + + result1 = self.results.count("Exchanging between") + result2 = self.results.count("Copying from") + passed = False + if "fullduplex" == test: + if numgpus > 1: + if result1 > 0: + passed = True + else: + if result1 == 0: + passed = True + else: + if result2 > 0: + passed = True + + return passed diff --git a/src/testsuite/common/__init__.py b/src/testsuite/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/common/hip_get_packages.py b/src/testsuite/common/hip_get_packages.py similarity index 55% rename from src/amd/common/hip_get_packages.py rename to src/testsuite/common/hip_get_packages.py index 17edb87..ec9d275 100644 --- a/src/amd/common/hip_get_packages.py +++ b/src/testsuite/common/hip_get_packages.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.common.hip_shell import execshellcmd +from testsuite.common.hip_shell import execshellcmd import os @@ -26,15 +26,45 @@ class HipPackages(): def __init__(self): self.cwdAbs = os.getcwd() - self.conformancePath = os.path.join(self.cwdAbs, "src/amd/conformance/") + self.conformancePath = os.path.join(self.cwdAbs, "src/testsuite/conformance/") + # HIP & HIPAMD self.hippath = os.path.join(self.conformancePath, "HIP/") self.hipamdpath = os.path.join(self.conformancePath, "HIPAMD/") - self.appPath = os.path.join(self.cwdAbs,"src/amd/applications/hip_examples/") + # HIP-Examples + self.appPath = os.path.join(self.cwdAbs,"src/testsuite/applications/hip_examples/") self.examplepath = os.path.join(self.appPath, "HIP-Examples/") self.mixbenchpath = os.path.join(self.appPath, "mixbench/") self.gpustrmpath = os.path.join(self.appPath, "GPU-STREAM/") + # Vdi and OpenCL self.rocclrpath = os.path.join(self.conformancePath, "ROCclr/") self.openclpath = os.path.join(self.conformancePath, "ROCm-OpenCL-Runtime/") + # mgbench + self.mgbenchrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/mgbench/") + self.mgbenchapppath = os.path.join(self.mgbenchrootpath, "mgbench/") + # CUDA-grep + self.cudagreprootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/cuda_grep/") + self.cudagrepapppath = os.path.join(self.cudagreprootpath, "CUDA-grep/") + # cuda_memtest + self.cudamemrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/cuda_memtest/") + self.cudamemapppath = os.path.join(self.cudamemrootpath, "cuda_memtest/") + # Quicksilver + self.qsrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/quicksilver/") + self.qsapppath = os.path.join(self.qsrootpath, "Quicksilver/") + # Gridtools + self.gridtoolsrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/gridtools/GridTools/") + self.gridtoolsapppath = os.path.join(self.gridtoolsrootpath, "gridtools/") + self.gtbenchrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/gridtools/GridTools/") + self.gtbenchapppath = os.path.join(self.gtbenchrootpath, "gtbench/") + # Kokkos + self.kokkosrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/kokkos/") + self.kokkosapppath = os.path.join(self.kokkosrootpath, "kokkos/") + # Laghos + self.mfemrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/laghos/") + self.mfemapppath = os.path.join(self.mfemrootpath, "mfem/") + self.laghosrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/laghos/") + self.laghosapppath = os.path.join(self.laghosrootpath, "Laghos/") + self.mpirootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/laghos/") + self.mpiapppath = os.path.join(self.mpirootpath, "openmpi/") def pull_repo(self, logFile, repo, branch, commitId, reponame): repo_root_path = "" @@ -68,6 +98,46 @@ def pull_repo(self, logFile, repo, branch, commitId, reponame): repo_root_path = self.openclpath repo_location = self.conformancePath repo_dir = "ROCm-OpenCL-Runtime" + elif reponame == "mgbench": + repo_root_path = self.mgbenchapppath + repo_location = self.mgbenchrootpath + repo_dir = "mgbench" + elif reponame == "cudagrep": + repo_root_path = self.cudagrepapppath + repo_location = self.cudagreprootpath + repo_dir = "CUDA-grep" + elif reponame == "cudamemtest": + repo_root_path = self.cudamemapppath + repo_location = self.cudamemrootpath + repo_dir = "cuda_memtest" + elif reponame == "quicksilver": + repo_root_path = self.qsapppath + repo_location = self.qsrootpath + repo_dir = "Quicksilver" + elif reponame == "gridtools": + repo_root_path = self.gridtoolsapppath + repo_location = self.gridtoolsrootpath + repo_dir = "gridtools" + elif reponame == "gtbench": + repo_root_path = self.gtbenchapppath + repo_location = self.gtbenchrootpath + repo_dir = "gtbench" + elif reponame == "kokkos": + repo_root_path = self.kokkosapppath + repo_location = self.kokkosrootpath + repo_dir = "kokkos" + elif reponame == "mfem": + repo_root_path = self.mfemapppath + repo_location = self.mfemrootpath + repo_dir = "mfem" + elif reponame == "Laghos": + repo_root_path = self.laghosapppath + repo_location = self.laghosrootpath + repo_dir = "Laghos" + elif reponame == "openmpi": + repo_root_path = self.mpiapppath + repo_location = self.mpirootpath + repo_dir = "openmpi" if os.path.isdir(repo_root_path) and os.path.isdir(repo_root_path + "/.git"): print(reponame + " already exist") @@ -101,8 +171,8 @@ def pull_repo(self, logFile, repo, branch, commitId, reponame): cmdexc = cmdcd + cmdPull # Clone the latest version from repo execshellcmd(cmdexc, logFile, None) - isHipPresent = os.path.isdir(repo_root_path) - if not isHipPresent: + isRepoPresent = os.path.isdir(repo_root_path) + if not isRepoPresent: return False else: print(reponame + " cloned successfully") diff --git a/src/amd/common/hip_shell.py b/src/testsuite/common/hip_shell.py similarity index 86% rename from src/amd/common/hip_shell.py rename to src/testsuite/common/hip_shell.py index 7269dd7..df948c7 100644 --- a/src/amd/common/hip_shell.py +++ b/src/testsuite/common/hip_shell.py @@ -19,6 +19,7 @@ # THE SOFTWARE. import subprocess +import re def execshellcmd(cmdexc, logfile, myenv): proc = subprocess.Popen(cmdexc, shell=True, env=myenv, @@ -41,3 +42,13 @@ def execshellcmd_largedump(cmdexc, logfile, runlog, myenv): for line in runlog: logfile.write(line) runlog.seek(0) + +def get_gpuarch(logFile): + # Get GPU Architecture + cmdexc = "/opt/rocm/bin/mygpu" + gpuarchsh = execshellcmd(cmdexc, logFile, None) + if not re.match("gfx\d+", gpuarchsh): + print("GPU Architecture unknown") + return None + gpuarch = gpuarchsh.strip() + return gpuarch diff --git a/src/amd/config_processor.py b/src/testsuite/config_processor.py similarity index 97% rename from src/amd/config_processor.py rename to src/testsuite/config_processor.py index 1b0dafc..0cd99e4 100644 --- a/src/amd/config_processor.py +++ b/src/testsuite/config_processor.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.AMD import AMDObject +from testsuite.AMD import AMDObject class ConfigProcessor(AMDObject): diff --git a/src/testsuite/conformance/CMakePatch b/src/testsuite/conformance/CMakePatch new file mode 100644 index 0000000..2da8a78 --- /dev/null +++ b/src/testsuite/conformance/CMakePatch @@ -0,0 +1,9 @@ +--- CMakeLists.txt 2021-09-30 14:11:43.980141598 +0530 ++++ CMakeListsNew.txt 2021-09-30 14:11:43.784142432 +0530 +@@ -1,3 +1,6 @@ ++cmake_minimum_required(VERSION 3.16.8) ++project(hiptests) ++ + # Set HIP Path + if(NOT DEFINED HIP_PATH) + if(DEFINED ENV{HIP_PATH}) diff --git a/src/testsuite/conformance/__init__.py b/src/testsuite/conformance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/conformance/conformance_test_classifier.py b/src/testsuite/conformance/conformance_test_classifier.py similarity index 96% rename from src/amd/conformance/conformance_test_classifier.py rename to src/testsuite/conformance/conformance_test_classifier.py index 4a86acd..54edc0f 100644 --- a/src/amd/conformance/conformance_test_classifier.py +++ b/src/testsuite/conformance/conformance_test_classifier.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.test_classifier import TestClassifier +from testsuite.test_classifier import TestClassifier from typing import Union diff --git a/src/amd/conformance/hip_dtest.py b/src/testsuite/conformance/hip_dtest.py similarity index 66% rename from src/amd/conformance/hip_dtest.py rename to src/testsuite/conformance/hip_dtest.py index 0407dea..e86b267 100644 --- a/src/amd/conformance/hip_dtest.py +++ b/src/testsuite/conformance/hip_dtest.py @@ -18,14 +18,15 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.TesterRepository import Tester -from amd.Test import HIPTestData, TestResult, Test, HIPBuildData, HIP_PLATFORM +from testsuite.TesterRepository import Tester +from testsuite.Test import HIPTestData, TestResult, Test, HIPBuildData, HIP_PLATFORM from typing import Union, List -from amd.conformance.conformance_test_classifier import CONFORMANCE -from amd.test_classifier import TestClassifier -from amd.conformance.hip_dtest_build import BuildRunAmd, BuildRunNvidia -from amd.common.hip_get_packages import HipPackages -from amd.common.hip_shell import * +from testsuite.conformance.conformance_test_classifier import CONFORMANCE +from testsuite.test_classifier import TestClassifier +from testsuite.conformance.hip_dtest_build_amd import BuildRunAmd +from testsuite.conformance.hip_dtest_build_nvidia import BuildRunNvidia +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import * import os @@ -34,16 +35,8 @@ class PrepareTest(): def __init__(self): self.hiprepo = "" # Default self.hipbranch = "" + self.hipbranch = "" self.hipcommitId = "" - self.hipamdrepo = "" # Default - self.hipamdbranch = "" - self.hipamdcommitId = "" - self.rocclrrepo = "" # Default - self.rocclrbranch = "" - self.rocclrcommitId = "" - self.openclrepo = "" # Default - self.openclbranch = "" - self.openclcommitId = "" self.logfd = None self.downloadresult = True self.buildresult = True @@ -59,46 +52,12 @@ def setrepoinfo(self, test_data: HIPBuildData): self.hipbranch = test_data.repos["hip"].branch if test_data.repos["hip"].commit_id != None: self.hipcommitId = test_data.repos["hip"].commit_id - - if test_data.repos["hipamd"].repo_url != None: - self.hipamdrepo = test_data.repos["hipamd"].repo_url - else: - return False - if test_data.repos["hipamd"].branch != None: - self.hipamdbranch = test_data.repos["hipamd"].branch - if test_data.repos["hipamd"].commit_id != None: - self.hipamdcommitId = test_data.repos["hipamd"].commit_id - - if test_data.repos["rocclr"].repo_url != None: - self.rocclrrepo = test_data.repos["rocclr"].repo_url - else: - return False - if test_data.repos["rocclr"].branch != None: - self.rocclrbranch = test_data.repos["rocclr"].branch - if test_data.repos["rocclr"].commit_id != None: - self.rocclrcommitId = test_data.repos["rocclr"].commit_id - - if test_data.repos["opencl"].repo_url != None: - self.openclrepo = test_data.repos["opencl"].repo_url - else: - return False - if test_data.repos["opencl"].branch != None: - self.openclbranch = test_data.repos["opencl"].branch - if test_data.repos["opencl"].commit_id != None: - self.openclcommitId = test_data.repos["opencl"].commit_id return True # Download the relevant packages from GIT for this test def downloadTest(self, log, platform): ret = HipPackages().pull_repo(log, self.hiprepo, self.hipbranch,\ self.hipcommitId, "HIP") - ret &= HipPackages().pull_repo(log, self.hipamdrepo, self.hipamdbranch,\ - self.hipamdcommitId, "hipamd") - if platform == HIP_PLATFORM.amd: - ret &= HipPackages().pull_repo(log, self.rocclrrepo, self.rocclrbranch,\ - self.rocclrcommitId, "rocclr") - ret &= HipPackages().pull_repo(log, self.openclrepo, self.openclbranch,\ - self.openclcommitId, "opencl") return ret # Build Packages @@ -114,16 +73,19 @@ def build_package(self, logFile, platform): return self.buildobj.build_package() - def clean(self): - self.buildobj.clean() - # Run test def runtest(self, logFile, testcase): - return self.buildobj.runtest(logFile, testcase) + status = "Failed" + if self.buildobj != None: + status = self.buildobj.runtest(logFile, testcase) + return status # Get ctest info def get_ctest_list(self): - return self.buildobj.get_all_ctest() + ret = None + if self.buildobj != None: + ret = self.buildobj.get_all_ctest() + return ret # Test HIP Dtest/ @@ -199,7 +161,5 @@ def test(self, test_data: HIPTestData): test_data.test_result = TestResult.FAIL def clean(self): - PrepareTest.clean(self) if self.logfd != None: self.logfd.close() - diff --git a/src/testsuite/conformance/hip_dtest_build_amd.py b/src/testsuite/conformance/hip_dtest_build_amd.py new file mode 100644 index 0000000..bd355af --- /dev/null +++ b/src/testsuite/conformance/hip_dtest_build_amd.py @@ -0,0 +1,67 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os, glob +import tempfile +import json +import re +from testsuite.common.hip_shell import execshellcmd_largedump, execshellcmd +from testsuite.conformance.hip_dtest_build_common import BuildRunCommon + +class BuildRunAmd(BuildRunCommon): + ''' + In this class insert the build and execution steps specific + for testsuite platform. + ''' + def __init__(self, logfile): + BuildRunCommon.__init__(self, logfile) + + # Build HIP Catch2 for AMD platform + def build_package(self): + buildSuccess = True + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + isCatchBuilt = self.validate_hipcatch_build() + if not isCatchBuilt: + # Build HIP + print("Catch2 test not built. Building Catch2 ..") + cmd = "cd " + self.hippath + ";" + cmd += "mkdir build; cd build;" + cmd += "cmake -DHIP_PATH=/opt/rocm/hip ../tests/catch;" + cmd += "make -j build_tests;" + cmdexc = cmd + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logfile, runlogdump, None) + runlogdump.close() + + # Validate if HIP build is successful + isCatchBuilt = self.validate_hipcatch_build() + if not isCatchBuilt: + print("HIP Catch2 Build Failed!") + return False + else: + print("HIP Catch2 already Built") + return True + + # Execute test cases + def runtest(self, log, testcase): + return BuildRunCommon.runtest(self, log, testcase, None) + diff --git a/src/testsuite/conformance/hip_dtest_build_common.py b/src/testsuite/conformance/hip_dtest_build_common.py new file mode 100644 index 0000000..5b758f7 --- /dev/null +++ b/src/testsuite/conformance/hip_dtest_build_common.py @@ -0,0 +1,90 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +import tempfile +import re +from testsuite.common.hip_shell import execshellcmd_largedump, execshellcmd + +class BuildRunCommon(): + ''' + In this class insert the build and execution steps for test cases + which are identical across different platforms (amd/nvidia/intel). + ''' + def __init__(self, logfile): + self.logfile = logfile + self.hippath = os.path.join(os.getcwd(), "src/testsuite/conformance/HIP") + self.ctest_file = os.path.join(self.hippath, "build/ctest.txt") + self.builddir = os.path.join(self.hippath, "build") + self.expected_catch_binaries = ["ABMTests","MultiProcTests","UnitTests"] + + # Validate if HIP build is successful + def validate_hipcatch_build(self): + status = True + catch_binary_path = os.path.join(self.hippath, "build/hipTestMain") + for catchbin in self.expected_catch_binaries: + if not os.path.isfile(\ + os.path.join(catch_binary_path, catchbin)): + print("Did not find catch2 binary " + catchbin) + status &= False + return status + + # Fetches all available dtests using ctest + def get_all_ctest(self): + if not os.path.isfile(self.ctest_file): + cmdexc = "cd " + self.builddir + ";" + cmdexc += "ctest -N;" + with open(self.ctest_file, "w+") as ctestlog: + execshellcmd_largedump(cmdexc, self.logfile, ctestlog, None) + + testlist = [] + with open(self.ctest_file, "r") as ctestlog: + for test in ctestlog: + if re.search("Test *#\d*:", test) != None: + dtest = re.sub("Test *#\d*: ", "", test) + dtest = re.sub("/", ".", dtest) + dtest = dtest.lstrip() + dtest = dtest.rstrip() + testlist.append(dtest) + return testlist + + # Parse the test result + def parsetest(self, log): + log.seek(0) + text = log.read() + status = None + if re.search("100% tests passed", text) != None: + status = "PASSED" + else: + status = "FAILED" + return status + + # Execute the test case + def runtest(self, log, testcase, envtoset): + cmdtest = "ctest -R " + "\"" + testcase + "\"" + print("Executing command = " + cmdtest) + # run test + cmd = "cd " + self.builddir + ";" + cmd += cmdtest + ";" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, log, runlogdump, envtoset) + status = self.parsetest(runlogdump) + runlogdump.close() + return status diff --git a/src/testsuite/conformance/hip_dtest_build_nvidia.py b/src/testsuite/conformance/hip_dtest_build_nvidia.py new file mode 100644 index 0000000..2ed3ea6 --- /dev/null +++ b/src/testsuite/conformance/hip_dtest_build_nvidia.py @@ -0,0 +1,75 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os, glob +import tempfile +import json +import re +from testsuite.common.hip_shell import execshellcmd_largedump, execshellcmd +from testsuite.conformance.hip_dtest_build_common import BuildRunCommon + +class BuildRunNvidia(BuildRunCommon): + ''' + In this class insert the build and execution steps specific + for NVIDIA platform. + ''' + def __init__(self, logfile): + BuildRunCommon.__init__(self, logfile) + + def setenv(self): + env = "export HIP_PLATFORM=nvidia;" + env += "export HIP_COMPILER=nvcc;" + env += "export HIP_RUNTIME=cuda;" + return env + + # Build HIP Catch2 for NVIDIA platform + def build_package(self): + buildSuccess = True + if not os.path.exists("/opt/rocm"): + print("ROCm not installed. Exiting!") + return False + isCatchBuilt = self.validate_hipcatch_build() + if not isCatchBuilt: + # Build HIP + print("Catch2 test not built. Building Catch2 ..") + cmd = self.setenv() + cmd += "cd " + self.hippath + "/tests/catch;" + cmd += "patch -p0 < ../../../CMakePatch;" + cmd += "cd " + self.hippath + ";" + cmd += "mkdir build; cd build;" + cmd += "cmake -DHIP_COMPILER=nvcc -DHIP_PLATFORM=nvidia -DHIP_RUNTIME=cuda -DHIP_PATH=/opt/rocm/hip ../tests/catch;" + cmd += "make -j build_tests;" + cmdexc = cmd + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logfile, runlogdump, None) + runlogdump.close() + + # Validate if HIP build is successful + isCatchBuilt = self.validate_hipcatch_build() + if not isCatchBuilt: + print("HIP Catch2 Build Failed!") + return False + else: + print("HIP Catch2 already Built") + return True + + # Execute test cases + def runtest(self, log, testcase): + return BuildRunCommon.runtest(self, log, testcase, None) diff --git a/src/testsuite/hpc_apps/__init__.py b/src/testsuite/hpc_apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/testsuite/hpc_apps/gridtools/__init__.py b/src/testsuite/hpc_apps/gridtools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/testsuite/hpc_apps/gridtools/gridtools.py b/src/testsuite/hpc_apps/gridtools/gridtools.py new file mode 100644 index 0000000..de62ac0 --- /dev/null +++ b/src/testsuite/hpc_apps/gridtools/gridtools.py @@ -0,0 +1,242 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.TesterRepository import Tester, Test, TestData +from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from testsuite.test_classifier import TestClassifier +from testsuite.hpc_apps.gridtools.gridtools_build_amd import BuildRunAmd +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import execshellcmd + +import os +import re +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, cwd): + self.cwdAbs = cwd + self.app_path = os.path.join(self.cwdAbs,\ + "src/testsuite/hpc_apps/gridtools") + self.thistestpath = self.app_path + self.prepareobj = None + self.gridtoolsrepo = "" # Default + self.gridtoolsbranch = "" + self.gridtoolscommitId = "" + self.gtbenchrepo = "" # Default + self.gtbenchbranch = "" + self.gtbenchcommitId = "" + + def set_gridtools_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["gridtools"].repo_url != None: + self.gridtoolsrepo = test_data.repos["gridtools"].repo_url + else: + validrepconfig &= False + if test_data.repos["gridtools"].branch != None: + self.gridtoolsbranch = test_data.repos["gridtools"].branch + if test_data.repos["gridtools"].commit_id != None: + self.gridtoolscommitId = test_data.repos["gridtools"].commit_id + return validrepconfig + + def set_gtbench_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["gtbench"].repo_url != None: + self.gtbenchrepo = test_data.repos["gtbench"].repo_url + else: + validrepconfig &= False + if test_data.repos["gtbench"].branch != None: + self.gtbenchbranch = test_data.repos["gtbench"].branch + if test_data.repos["gtbench"].commit_id != None: + self.gtbenchcommitId = test_data.repos["gtbench"].commit_id + return validrepconfig + + def downloadtest(self, logFile, test_data: HIPTestData): + ret = True + ret = ret & HipPackages().pull_repo(logFile, self.gridtoolsrepo,\ + self.gridtoolsbranch, self.gridtoolscommitId, "gridtools") + ret = ret & HipPackages().pull_repo(logFile, self.gtbenchrepo,\ + self.gtbenchbranch, self.gtbenchcommitId, "gtbench") + return ret + + def buildtest(self, logFile, platform): + isBinaryPresent = True + if platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.thistestpath, logFile) + else: + print("Invalid Platform") + return False + buildstatus = self.prepareobj.buildtest() + return buildstatus + + def clean(self): + if self.prepareobj != None: + self.prepareobj.clean() + + def runtest(self, testnum): + if self.prepareobj != None: + self.prepareobj.runtest(testnum) + + def parse_result(self, testnum): + if self.prepareobj != None: + return self.prepareobj.parse_result(testnum) + return False + +class GDTOOLS(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"gridtools": matched_with_names}) + +class PERFORMANCE(GDTOOLS): + def __init__(self): + GDTOOLS.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + GDTOOLS.add_matched_with_names(self, {"performance": matched_with_names}) + +# Gridtool Convergence Test +class GRIDTOOLSCONVG(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = PERFORMANCE() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Gridtool Convergence Test ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + print("Gridtool Convergence Test is not supported on NVIDIA") + test_data.test_result = TestResult.SKIP + return + # Check if Boost package exists + if not os.path.isfile(\ + os.path.join(self.thistestpath, "boost_1_72_0.tar.bz2")): + print("Boost Package boost_1_72_0.tar.bz2 not available under src/testsuite/hpc_apps/gridtools/.") + print("Please download and copy Boost package in src/testsuite/hpc_apps/gridtools folder.") + test_data.test_result = TestResult.ERROR + return + # Create GT_TREE_DIR + gt_tree_dir = os.path.join(self.thistestpath, "GridTools") + if not os.path.exists(gt_tree_dir): + os.mkdir(gt_tree_dir) + # Set repo info + isrepocfgvalid = self.set_gridtools_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + isrepocfgvalid = self.set_gtbench_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/gridtoolsconv.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(0) + # Parse the test result + if True == self.parse_result(0): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + +# Gridtool PERFORMANCE Test +class GRIDTOOLSPERF(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = PERFORMANCE() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Gridtool Perf Test ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + print("Gridtool Perf Test is not supported on NVIDIA") + test_data.test_result = TestResult.SKIP + return + # Check if Boost package exists + if not os.path.isfile(\ + os.path.join(self.thistestpath, "boost_1_72_0.tar.bz2")): + print("Boost Package boost_1_72_0.tar.bz2 not available under src/testsuite/hpc_apps/gridtools/.") + print("Please download and copy Boost package in src/testsuite/hpc_apps/gridtools folder.") + test_data.test_result = TestResult.ERROR + return + + # Create GT_TREE_DIR + gt_tree_dir = os.path.join(self.thistestpath, "GridTools") + if not os.path.exists(gt_tree_dir): + os.mkdir(gt_tree_dir) + + # Set repo info + isrepocfgvalid = self.set_gridtools_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + isrepocfgvalid = self.set_gtbench_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/gridtoolsperf.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(1) + # Parse the test result + if True == self.parse_result(1): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL diff --git a/src/testsuite/hpc_apps/gridtools/gridtools_build_amd.py b/src/testsuite/hpc_apps/gridtools/gridtools_build_amd.py new file mode 100644 index 0000000..9bd68e5 --- /dev/null +++ b/src/testsuite/hpc_apps/gridtools/gridtools_build_amd.py @@ -0,0 +1,119 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +import tempfile +from testsuite.common.hip_shell import * +from testsuite.hpc_apps.gridtools.gridtools_parser_common import GridtoolsParser + +class BuildRunAmd(): + def __init__(self, thistestpath, logFile): + self.thistestpath = thistestpath + self.logFile = logFile + self.runlog = "" + + def setenv(self, gpu_arch): + env = "export HIP_PLATFORM=`/opt/rocm/bin/hipconfig --platform`;" + env += "export GT_CUDA_COMPILATION_TYPE=HIPCC-AMDGPU;" + env += "export HCC_AMDGPU_TARGET=\""+ gpu_arch +"\";" + env += "export CXX=/opt/rocm/bin/hipcc;" + env += "export ROCMHOME=/opt/rocm;" + env += "export PATH=$ROCMHOME/bin:$ROCMHOME/llvm/bin:$ROCMHOME/hip/bin:$ROCMHOME/opencl/bin:$ROCMHOME/rocprofiler/bin:$PATH;" + env += "export LD_LIBRARY_PATH=/$ROCMHOME/lib:$ROCMHOME/llvm/lib:$LD_LIBRARY_PATH;" + env += "export GT_ALL_DIR=$PWD/src/testsuite/hpc_apps/gridtools;" + env += "export GT_TREE_DIR=$GT_ALL_DIR/GridTools;" + env += "export BOOST_TREE_DIR=$GT_TREE_DIR/boost_1_72_0;" + env += "export BOOST_INSTALL_DIR=$GT_ALL_DIR/boost_1_72_0;" + env += "export GRIDTOOLS_TREE_DIR=$GT_TREE_DIR/gridtools;" + env += "export GRIDTOOLS_BUILD_DIR=$GRIDTOOLS_TREE_DIR/build;" + env += "export GRIDTOOLS_INSTALL_DIR=$GT_ALL_DIR/gridtools;" + env += "export GTBENCH_TREE_DIR=$GT_TREE_DIR/gtbench;" + env += "export GTBENCH_BUILD_DIR=$GTBENCH_TREE_DIR/build;" + return env + + def buildtest(self): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + # Build Boost + gpuarch = get_gpuarch(self.logFile) + if gpuarch is None: + return False + + if not os.path.exists(os.path.join(self.thistestpath, "boost_1_72_0")): + print("Building and Installing Boost..") + cmdexc = self.setenv(gpuarch) + cmdexc += "cd $GT_TREE_DIR;" + cmdexc += "tar -xvjf ../boost_1_72_0.tar.bz2;cd $BOOST_TREE_DIR;" + cmdexc += "./bootstrap.sh --prefix=$BOOST_INSTALL_DIR --with-python=python3;" + cmdexc += "./b2 install -j8 threading=multi link=shared;" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, runlogdump, None) + runlogdump.close() + else: + print("Boost already installed..") + # Build Gridtools + if not os.path.exists(os.path.join(self.thistestpath, "GridTools/gridtools/build")): + print("Building and Installing GridTools..") + cmdexc = self.setenv(gpuarch) + cmdexc += "cd $GT_TREE_DIR;cd $GRIDTOOLS_TREE_DIR;mkdir -p $GRIDTOOLS_BUILD_DIR;cd $GRIDTOOLS_BUILD_DIR;" + cmdexc += "CXX=/opt/rocm/bin/hipcc cmake .. -DBUILD_TESTING=OFF -DBoost_INCLUDE_DIR=$BOOST_INSTALL_DIR/include " +\ + "-DGT_CUDA_COMPILATION_TYPE=$GT_CUDA_COMPILATION_TYPE -DGT_CUDA_ARCH=" + gpuarch + " " +\ + "-DGT_ENABLE_BACKEND_CUDA=ON -DGT_ENABLE_BACKEND_MC=OFF -DGT_ENABLE_BACKEND_X86=OFF -DGT_ENABLE_BACKEND_NAIVE=OFF " +\ + "-DGT_USE_MPI=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$GRIDTOOLS_INSTALL_DIR;" + cmdexc += "make -j;" + cmdexc += "make install;" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, runlogdump, None) + runlogdump.close() + else: + print("GridTools already built..") + + # Build Gtbench + if not os.path.exists(os.path.join(self.thistestpath, "GridTools/gtbench/build")): + print("Building and Installing Gtbench..") + cmdexc = self.setenv(gpuarch) + cmdexc += "cd $GT_TREE_DIR;cd $GTBENCH_TREE_DIR;git apply ../../gtbench.patch;mkdir -p $GTBENCH_BUILD_DIR;cd $GTBENCH_BUILD_DIR;" + cmdexc += "CXX=/opt/rocm/bin/hipcc cmake .. -DGridTools_DIR=$GRIDTOOLS_INSTALL_DIR/lib/cmake -DGTBENCH_BACKEND=cuda " +\ + "-DGTBENCH_RUNTIME=single_node -DCMAKE_CXX_FLAGS=-D__HIPCC__ -DBoost_INCLUDE_DIR=$BOOST_INSTALL_DIR/include;" + cmdexc += "make -j8;" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, runlogdump, None) + runlogdump.close() + else: + print("Gtbench already built..") + + return True + + def runtest(self, testnum): + cmdcd = "cd " + os.path.join(self.thistestpath, "GridTools/gtbench/build") + ";" + if testnum == 0: + print("Testing Convergence Test") + cmdrun = "./convergence_tests;" + elif testnum == 1: + print("Testing Performance Test") + cmdrun = "./benchmark --domain-size 256 256 --runs 100;" + cmdexc = cmdcd + cmdrun + self.runlog = execshellcmd(cmdexc, self.logFile, None) + + def clean(self): + print("Cleaning Gridtools..") + + def parse_result(self, testnum): + return GridtoolsParser(self.runlog).parse(testnum) diff --git a/src/testsuite/hpc_apps/gridtools/gridtools_parser_common.py b/src/testsuite/hpc_apps/gridtools/gridtools_parser_common.py new file mode 100644 index 0000000..099003e --- /dev/null +++ b/src/testsuite/hpc_apps/gridtools/gridtools_parser_common.py @@ -0,0 +1,48 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.common.hip_shell import execshellcmd +import re + +class GridtoolsParser(): + def __init__(self, results): + self.results = results + self.conv_tests = ["HORIZONTAL DIFFUSION", "VERTICAL DIFFUSION", "FULL DIFFUSION",\ + "HORIZONTAL ADVECTION", "VERTICAL ADVECTION", "RUNGE-KUTTA ADVECTION", "ADVECTION-DIFFUSION"] + def parse(self, testnum): + test_passed = False + if testnum == 0: + count = 0 + for test in self.conv_tests: + if re.search(test, self.results): + count = count + 1 + if count == len(self.conv_tests): + test_passed = True + + elif testnum == 1: + test1 = False + test2 = False + if re.search("Median time:\s*\d*\.\d*s", self.results): + test1 = True + if re.search("Columns per second:\s*\d+", self.results): + test2 = True + test_passed = test1 & test2 + + return test_passed diff --git a/src/testsuite/hpc_apps/gridtools/gtbench.patch b/src/testsuite/hpc_apps/gridtools/gtbench.patch new file mode 100644 index 0000000..e8a6756 --- /dev/null +++ b/src/testsuite/hpc_apps/gridtools/gtbench.patch @@ -0,0 +1,120 @@ +diff --git a/runtime/computation.hpp b/runtime/computation.hpp +index 81b4497..1d29deb 100644 +--- a/runtime/computation.hpp ++++ b/runtime/computation.hpp +@@ -67,8 +67,8 @@ numerics::solver_state init_state(Discrete const &discrete, real_t t = 0_r) { + } + + inline void sync(numerics::solver_state &state) { +-#ifdef __CUDACC__ +- if (cudaDeviceSynchronize() != cudaSuccess) ++#ifdef __HIPCC__ ++ if (hipDeviceSynchronize() != hipSuccess) + throw std::runtime_error("device sync failed"); + #endif + } +diff --git a/runtime/device/set_device.cpp b/runtime/device/set_device.cpp +index c3c6e8f..4efc8d7 100644 +--- a/runtime/device/set_device.cpp ++++ b/runtime/device/set_device.cpp +@@ -9,25 +9,24 @@ + */ + #include "set_device.hpp" + #include +- ++#include + namespace runtime { +- +-#ifdef __CUDACC__ ++#ifdef __HIPCC__ + int set_device(int device_id) { + int device_count = 1; +- if (cudaGetDeviceCount(&device_count) != cudaSuccess) +- throw std::runtime_error("cudaGetDeviceCount failed"); ++ if (hipGetDeviceCount(&device_count) != hipSuccess) ++ throw std::runtime_error("hipGetDeviceCount failed"); + device_id %= device_count; +- if (cudaSetDevice(device_id) != cudaSuccess) +- throw std::runtime_error("cudaSetDevice failed"); ++ if (hipSetDevice(device_id) != hipSuccess) ++ throw std::runtime_error("hipSetDevice failed"); + if (device_count > 1) { + for (int i = 0; i < device_count; ++i) { + if (i != device_id) { + int flag; +- if (cudaDeviceCanAccessPeer(&flag, device_id, i) != cudaSuccess) ++ if (hipDeviceCanAccessPeer(&flag, device_id, i) != hipSuccess) + throw std::runtime_error("cudaDeviceAccessPeer failed"); + if (flag) { +- cudaDeviceEnablePeerAccess(i, 0); ++ hipDeviceEnablePeerAccess(i, 0); + } + } + } +diff --git a/runtime/gcl/run.cpp b/runtime/gcl/run.cpp +index 827e567..8bcdcfe 100644 +--- a/runtime/gcl/run.cpp ++++ b/runtime/gcl/run.cpp +@@ -19,7 +19,7 @@ namespace gcl_impl { + + using pattern_t = gt::halo_exchange_dynamic_ut, real_t, +-#ifdef __CUDACC__ ++#ifdef __HIPCC__ + gt::gcl_gpu + #else + gt::gcl_cpu +@@ -103,8 +103,8 @@ struct process_grid::impl { + + pattern->setup(1); + +-#ifdef __CUDACC__ +- cudaStreamSynchronize(0); ++#ifdef __HIPCC__ ++ hipStreamSynchronize(0); + #endif + + return [pattern = std::move(pattern)](storage_t &storage) { +diff --git a/runtime/ghex_comm/run.cpp b/runtime/ghex_comm/run.cpp +index 60e9e35..21c2777 100644 +--- a/runtime/ghex_comm/run.cpp ++++ b/runtime/ghex_comm/run.cpp +@@ -82,7 +82,7 @@ runtime::runtime(int num_threads, std::array cart_dims, + "the product of thread cart dims must be equal to the number of " + "threads per rank."); + } +-#ifdef __CUDACC__ ++#ifdef __HIPCC__ + MPI_Comm shmem_comm; + MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, + &shmem_comm); +@@ -308,8 +308,8 @@ public: + &patterns = *m_patterns](storage_t &storage) mutable { + auto field = gt::ghex::wrap_gt_field(domain, storage); + +-#ifdef __CUDACC__ +- cudaStreamSynchronize(0); ++#ifdef __HIPCC__ ++ hipStreamSynchronize(0); + #endif + comm_obj->exchange(patterns(field)).wait(); + }; +@@ -356,7 +356,7 @@ void runtime_register_options(ghex_comm, options &options) { + "dimensons of cartesian decomposition " + "among sub-domains", + "TX TY", 2); +-#ifdef __CUDACC__ ++#ifdef __HIPCC__ + options("device-mapping", + "node device mapping: device id per sub-domain in the format " + "I_0:I_1:...:I_(N-1) " +@@ -368,7 +368,7 @@ void runtime_register_options(ghex_comm, options &options) { + + runtime runtime_init(ghex_comm, options_values const &options) { + std::vector device_mapping; +-#ifdef __CUDACC__ ++#ifdef __HIPCC__ + if (options.has("device-mapping")) { + const std::regex delimiter(":"); + const auto input = options.get("device-mapping"); + diff --git a/src/testsuite/hpc_apps/kokkos/__init__.py b/src/testsuite/hpc_apps/kokkos/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/testsuite/hpc_apps/kokkos/kokkos.py b/src/testsuite/hpc_apps/kokkos/kokkos.py new file mode 100644 index 0000000..f5dcf88 --- /dev/null +++ b/src/testsuite/hpc_apps/kokkos/kokkos.py @@ -0,0 +1,202 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.TesterRepository import Tester, Test, TestData +from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from testsuite.test_classifier import TestClassifier +from testsuite.hpc_apps.kokkos.kokkos_build_amd import BuildRunAmd +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import execshellcmd + +import os +import re +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, cwd): + self.cwdAbs = cwd + self.app_path = os.path.join(self.cwdAbs,\ + "src/testsuite/hpc_apps/kokkos/") + self.app_root = os.path.join(self.app_path, "kokkos/") + self.thistestpath = self.app_root + self.prepareobj = None + self.apprepo = "" # Default + self.appbranch = "" + self.appcommitId = "" + + def set_kokkos_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["kokkos"].repo_url != None: + self.apprepo = test_data.repos["kokkos"].repo_url + else: + validrepconfig &= False + if test_data.repos["kokkos"].branch != None: + self.appbranch = test_data.repos["kokkos"].branch + if test_data.repos["kokkos"].commit_id != None: + self.appcommitId = test_data.repos["kokkos"].commit_id + return validrepconfig + + def downloadtest(self, logFile, test_data: HIPTestData): + ret = True + ret = ret & HipPackages().pull_repo(logFile, self.apprepo,\ + self.appbranch, self.appcommitId, "kokkos") + return ret + + def buildtest(self, logFile, platform): + if platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.thistestpath, logFile) + else: + print("Invalid Platform") + return False + buildstatus = self.prepareobj.buildtest() + if buildstatus == False: + return False + + return True + + def clean(self): + if self.prepareobj != None: + self.prepareobj.clean() + + def runtest(self, testnum): + if self.prepareobj != None: + self.prepareobj.runtest(testnum) + + def parse_result(self, testnum): + if self.prepareobj != None: + return self.prepareobj.parse_result(testnum) + return False + +class KOKKOS(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"kokkos": matched_with_names}) + +class PERFORMANCE(KOKKOS): + def __init__(self): + KOKKOS.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + KOKKOS.add_matched_with_names(self, {"performance": matched_with_names}) + +class MINIAPP(KOKKOS): + def __init__(self): + KOKKOS.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + KOKKOS.add_matched_with_names(self, {"mini-app": matched_with_names}) + +# Kokkos Unit/Regression Test +class KOKKOS_UNIT_TEST(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = MINIAPP() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== KOKKOS Unit test ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + print("KOKKOS test is not supported on NVIDIA") + test_data.test_result = TestResult.SKIP + return + # Set repo info + isrepocfgvalid = self.set_kokkos_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/kokkos_unit.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(0) + # Parse the test result + if True == self.parse_result(0): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + +# Performance Unit/Regression Test +class KOKKOS_PERF_TEST(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = PERFORMANCE() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== KOKKOS Performance test ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + print("KOKKOS test is not supported on NVIDIA") + test_data.test_result = TestResult.SKIP + return + # Set repo info + isrepocfgvalid = self.set_kokkos_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/kokkos_perf.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(1) + # Parse the test result + if True == self.parse_result(1): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL diff --git a/src/testsuite/hpc_apps/kokkos/kokkos_build_amd.py b/src/testsuite/hpc_apps/kokkos/kokkos_build_amd.py new file mode 100644 index 0000000..a021c2f --- /dev/null +++ b/src/testsuite/hpc_apps/kokkos/kokkos_build_amd.py @@ -0,0 +1,72 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +import re +import tempfile +from testsuite.common.hip_shell import * +from testsuite.hpc_apps.kokkos.kokkos_parser_common import KokkosParser + +class BuildRunAmd(): + def __init__(self, thistestpath, logFile): + self.thistestpath = thistestpath + self.logFile = logFile + self.runlog = "" + + def buildtest(self): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + gpuarch = get_gpuarch(self.logFile) + if gpuarch is None: + return False + # extract the gpu architecture number + arch_num = re.search("\d+", gpuarch).group(0) + if not os.path.exists(os.path.join(self.thistestpath, "build")): + print("Building Kokkos..") + cmdcd = "cd " + self.thistestpath + "; mkdir build; cd build;" + cmd_cmake = "cmake -DKokkos_ARCH_SKX=ON -DKokkos_ARCH_VEGA" + arch_num + "=ON -DCMAKE_CXX_COMPILER=/opt/rocm/bin/hipcc " +\ + "-DKokkos_ENABLE_HIP=ON -DKokkos_ENABLE_SERIAL=ON -DKokkos_ENABLE_TESTS=ON -DKokkos_CXX_STANDARD=14 -DCMAKE_CXX_STANDARD=14 " +\ + "-DCMAKE_INSTALL_PREFIX=../install -DKokkos_ENABLE_HIP_RELOCATABLE_DEVICE_CODE=OFF " +\ + "-DCMAKE_CXX_FLAGS=\"-O3 -DNDEBUG --amdgpu-target=gfx" + arch_num + "\" ..;" + cmd_build = "make -j; make -j install;" + cmdexc = cmdcd + cmd_cmake + cmd_build + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, runlogdump, None) + runlogdump.close() + else: + print("Kokkos already built..") + + return True + + def runtest(self, testnum): + print("Running Quicksilver Test..") + cmdcd = "cd " + self.thistestpath + "/build;" + if testnum == 0: + cmdrun = "ctest -R;" + elif testnum == 1: + cmdrun = "./core/perf_test/KokkosCore_PerfTestExec;" + cmdexc = cmdcd + cmdrun + self.runlog = execshellcmd(cmdexc, self.logFile, None) + + def clean(self): + print("Cleaning Quicksilver..") + + def parse_result(self, testnum): + return KokkosParser(self.runlog).parse(testnum) diff --git a/src/testsuite/hpc_apps/kokkos/kokkos_parser_common.py b/src/testsuite/hpc_apps/kokkos/kokkos_parser_common.py new file mode 100644 index 0000000..ec10152 --- /dev/null +++ b/src/testsuite/hpc_apps/kokkos/kokkos_parser_common.py @@ -0,0 +1,36 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.common.hip_shell import execshellcmd +import re + +class KokkosParser(): + def __init__(self, results): + self.results = results + + def parse(self, testnum): + testpassed = False + if testnum == 0: + if re.search("100% tests passed, 0 tests failed out of \d+", self.results): + testpassed = True + elif testnum == 1: + if re.search("[\s*PASSED\s*]\s*\d+\s*tests", self.results): + testpassed = True + return testpassed diff --git a/src/testsuite/hpc_apps/laghos/__init__.py b/src/testsuite/hpc_apps/laghos/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/testsuite/hpc_apps/laghos/laghos.py b/src/testsuite/hpc_apps/laghos/laghos.py new file mode 100644 index 0000000..c3d756a --- /dev/null +++ b/src/testsuite/hpc_apps/laghos/laghos.py @@ -0,0 +1,226 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.TesterRepository import Tester, Test, TestData +from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from testsuite.test_classifier import TestClassifier +from testsuite.hpc_apps.laghos.laghos_build_amd import BuildRunAmd +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import execshellcmd + +import os +import re +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, cwd): + self.cwdAbs = cwd + self.app_path = os.path.join(self.cwdAbs,\ + "src/testsuite/hpc_apps/laghos/") + self.thistestpath = self.app_path + self.prepareobj = None + + self.laghos_repo = "" # Default + self.laghos_branch = "" + self.laghos_commitId = "" + + self.openmpi_repo = "" # Default + self.openmpi_branch = "" + self.openmpi_commitId = "" + + self.mfem_repo = "" # Default + self.mfem_branch = "" + self.mfem_commitId = "" + + def set_laghos_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["mfem"].repo_url != None: + self.mfem_repo = test_data.repos["mfem"].repo_url + else: + validrepconfig &= False + if test_data.repos["mfem"].branch != None: + self.mfem_branch = test_data.repos["mfem"].branch + if test_data.repos["mfem"].commit_id != None: + self.mfem_commitId = test_data.repos["mfem"].commit_id + + if test_data.repos["Laghos"].repo_url != None: + self.laghos_repo = test_data.repos["Laghos"].repo_url + else: + validrepconfig &= False + if test_data.repos["Laghos"].branch != None: + self.laghos_branch = test_data.repos["Laghos"].branch + if test_data.repos["Laghos"].commit_id != None: + self.laghos_commitId = test_data.repos["Laghos"].commit_id + + if test_data.repos["openmpi"].repo_url != None: + self.openmpi_repo = test_data.repos["openmpi"].repo_url + else: + validrepconfig &= False + if test_data.repos["openmpi"].branch != None: + self.openmpi_branch = test_data.repos["openmpi"].branch + if test_data.repos["openmpi"].commit_id != None: + self.openmpi_commitId = test_data.repos["openmpi"].commit_id + + return validrepconfig + + def downloadtest(self, logFile, test_data: HIPTestData): + ret = True + ret = ret & HipPackages().pull_repo(logFile, self.mfem_repo,\ + self.mfem_branch, self.mfem_commitId, "mfem") + ret = ret & HipPackages().pull_repo(logFile, self.laghos_repo,\ + self.laghos_branch, self.laghos_commitId, "Laghos") + ret = ret & HipPackages().pull_repo(logFile, self.openmpi_repo,\ + self.openmpi_branch, self.openmpi_commitId, "openmpi") + return ret + + def buildtest(self, logFile, test_data: HIPTestData, platform): + if platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.thistestpath, logFile) + else: + print("Invalid Platform") + return False + buildstatus = self.prepareobj.buildtest(test_data) + if buildstatus == False: + return False + + return True + + def clean(self): + if self.prepareobj != None: + self.prepareobj.clean() + + def runtest(self, testnum): + if self.prepareobj != None: + self.prepareobj.runtest(testnum) + + def parse_result(self, testnum): + if self.prepareobj != None: + return self.prepareobj.parse_result(testnum) + return False + +class LAGHOS(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"laghos": matched_with_names}) + +class PERFORMANCE(LAGHOS): + def __init__(self): + LAGHOS.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + LAGHOS.add_matched_with_names(self, {"performance": matched_with_names}) + +# cube01_hex test +class LAGHOS_CUBE01_HEX(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = PERFORMANCE() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Laghos cube01_hex test ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + print("Laghos test is not supported on NVIDIA") + test_data.test_result = TestResult.SKIP + return + # Set repo info + isrepocfgvalid = self.set_laghos_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cube01_hex.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(0) + # Parse the test result + if True == self.parse_result(0): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + +# cube_12_hex test +class LAGHOS_CUBE_12_HEX(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = PERFORMANCE() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Laghos cube_12_hex test ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + print("Laghos test is not supported on NVIDIA") + test_data.test_result = TestResult.SKIP + return + # Set repo info + isrepocfgvalid = self.set_laghos_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cube_12_hex.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(1) + # Parse the test result + if True == self.parse_result(1): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL diff --git a/src/testsuite/hpc_apps/laghos/laghos_build_amd.py b/src/testsuite/hpc_apps/laghos/laghos_build_amd.py new file mode 100644 index 0000000..43c1008 --- /dev/null +++ b/src/testsuite/hpc_apps/laghos/laghos_build_amd.py @@ -0,0 +1,225 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +import re +import tempfile +from testsuite.Test import HIPTestData +from testsuite.common.hip_shell import * +from testsuite.hpc_apps.laghos.laghos_parser_common import LaghosParser + +class BuildRunAmd(): + def __init__(self, thistestpath, logFile): + self.thistestpath = thistestpath + self.logFile = logFile + self.runlog = "" + + def set_env(self): + cmd = "export MPI_PATH=/usr/local/openmpi;" + cmd += "export ROCM_PATH=/opt/rocm;" + cmd += "export PATH=${MPI_PATH}/bin:$PATH;" + cmd += "export PATH=${MPI_PATH}/bin:$PATH;" + return cmd + + def configure_build_openmpi(self, test_data: HIPTestData): + # Check if OpenMPI is already installed. Then return. + if os.path.exists("/usr/local/openmpi/bin") and os.path.exists("/usr/local/openmpi/etc")\ + and os.path.exists("/usr/local/openmpi/lib") and os.path.exists("/usr/local/openmpi/include")\ + and os.path.exists("/usr/local/openmpi/share"): + print("Openmpi already installed. Returning ..") + return True + openmpi_rootdir = os.path.join(self.thistestpath, "openmpi") + cmd = self.set_env() + cmd += "cd " + openmpi_rootdir + ";" + cmd += "./autogen.pl;" + print("Open MPI autogen in progress ..") + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, self.logFile, runlogdump, None) + autogensuccess = False + for line in runlogdump: + if re.search("Open\s+MPI\s+autogen:\s+completed\s+successfully.\s+w00t!", line): + autogensuccess = True + runlogdump.close() + if not autogensuccess: + print("Open MPI autogen in failed! Check if prerequisites (autoconf, automake, libtool and flex) are installed.") + return False + cmd = self.set_env() + cmd += "cd " + openmpi_rootdir + ";" + cmd += "./configure --enable-mpi-cxx --enable-mpi1-compatibility --prefix=/usr/local/openmpi;" + cmd += "make -j;" + cmd += "echo " + test_data.user_password + " | sudo -S make -j install;" + print("Open MPI Configuration+Build in progress ..") + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, self.logFile, runlogdump, None) + runlogdump.close() + # Check if Openmpi is installed + count = 0 + lib32_present = False + lib64_present = False + if os.path.exists("/usr/local/openmpi"): + if os.path.exists("/usr/local/openmpi/bin"): + count = count + 1 + if os.path.exists("/usr/local/openmpi/etc"): + count = count + 1 + if os.path.exists("/usr/local/openmpi/lib"): + count = count + 1 + else: + if os.path.exists("/usr/local/openmpi/lib32"): + count = count + 1 + lib32_present = True + elif os.path.exists("/usr/local/openmpi/lib64"): + count = count + 1 + lib64_present = True + if os.path.exists("/usr/local/openmpi/include"): + count = count + 1 + if os.path.exists("/usr/local/openmpi/share"): + count = count + 1 + else: + print("Openmpi not installed. Build failed!") + return False + if count != 5: + print("Openmpi not installed. Build failed!") + return False + # This step might be necessary on SLES machines + if lib32_present: + print("lib softlink to lib32 needs to be created") + cmd = "cd /usr/local/openmpi/;" + cmd += "echo " + test_data.user_password + "| sudo -S ln -s lib32 lib;" + execshellcmd(cmd, self.logFile, None) + elif lib64_present: + print("lib softlink to lib64 needs to be created") + cmd = "cd /usr/local/openmpi/;" + cmd += "echo " + test_data.user_password + "| sudo -S ln -s lib64 lib;" + execshellcmd(cmd, self.logFile, None) + + return True + + def configure_build_hypre(self): + if os.path.exists(os.path.join(self.thistestpath, "hypre/src/lib/libHYPRE.a")): + print("Hypre already built") + return True + print("Hypre build in progress ..") + cmd = self.set_env() + cmd += "cd " + self.thistestpath + ";" + cmd += "wget https://github.com/hypre-space/hypre/archive/v2.16.0.tar.gz;" + cmd += "mv v2.16.0.tar.gz hypre-2.16.0.tar.gz;tar -zxvf hypre-2.16.0.tar.gz;rm hypre-2.16.0.tar.gz;" + cmd += "cd hypre-2.16.0/src/;" + cmd += "./configure --disable-fortran --enable-bigint --with-MPI --with-MPI-include=${MPI_PATH}/include --with-MPI-lib-dirs=${MPI_PATH}/lib;" + cmd += "make -j; cd ../..; ln -s hypre-2.16.0 hypre;" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, self.logFile, runlogdump, None) + runlogdump.close() + if not os.path.exists(os.path.join(self.thistestpath, "hypre/src/lib/libHYPRE.a")): + print("Hypre Build Failed") + return False + return True + + def configure_build_metis(self): + if os.path.exists(os.path.join(self.thistestpath, "metis-4.0/libmetis.a")): + print("Metis already built") + return True + print("Metis build in progress ..") + cmd = self.set_env() + cmd += "cd " + self.thistestpath + ";" + cmd += "wget http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/OLD/metis-4.0.3.tar.gz;" + cmd += "tar -zxvf metis-4.0.3.tar.gz;rm metis-4.0.3.tar.gz;" + cmd += "cd metis-4.0.3;make -j;cd ..;" + cmd += "ln -s metis-4.0.3 metis-4.0;" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, self.logFile, runlogdump, None) + runlogdump.close() + if not os.path.exists(os.path.join(self.thistestpath, "metis-4.0/libmetis.a")): + print("Metis Build Failed") + return False + return True + + def configure_build_mfem(self, test_data: HIPTestData): + if os.path.exists(os.path.join(self.thistestpath, "mfem/libmfem.a")): + print("Mfem already built") + return True + print("Mfem build in progress ..") + cmd = self.set_env() + cmd += "cd " + self.thistestpath + "; cd mfem;" + cmd += "make config;" + cmd += "make phip -j CXXFLAGS=\"-O3 -std=c++11 --gpu-max-threads-per-block=256\" \ + MFEM_TPLFLAGS=\"-I./../hypre/src/hypre/include -I${MPI_PATH}/include\" MFEM_EXT_LIBS=\"-L./../hypre/src/hypre/lib \ + -lHYPRE -L./../metis-4.0 -lmetis -lrt -L${MPI_PATH}/lib -lmpi\";" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, self.logFile, runlogdump, None) + runlogdump.close() + if not os.path.exists(os.path.join(self.thistestpath, "mfem/libmfem.a")): + print("Mfem Build Failed") + return False + return True + + def configure_build_laghos(self, test_data: HIPTestData): + if os.path.exists(os.path.join(self.thistestpath, "Laghos/laghos")): + print("Laghos already built") + return True + print("Laghos build in progress ..") + cmd = self.set_env() + cmd += "cd " + self.thistestpath + "; cd Laghos;" + cmd += "make -j;" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, self.logFile, runlogdump, None) + runlogdump.close() + if not os.path.exists(os.path.join(self.thistestpath, "mfem/libmfem.a")): + print("Laghos Build Failed") + return False + return True + + def buildtest(self, test_data: HIPTestData): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + # print("Password is = ", test_data.user_password) + if not self.configure_build_openmpi(test_data): + print("Openmpi configuration and build failed ..") + return False + if not self.configure_build_hypre(): + print("Hypre configuration and build failed ..") + return False + if not self.configure_build_metis(): + print("Metis configuration and build failed ..") + return False + if not self.configure_build_mfem(test_data): + print("Mfem configuration and build failed ..") + return False + if not self.configure_build_laghos(test_data): + print("Laghos configuration and build failed ..") + return False + + return True + + def runtest(self, testnum): + print("Running Laghos Test" + str(testnum)) + cmd = self.set_env() + cmd += "cd " + self.thistestpath + "; cd Laghos;" + cmd += "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/openmpi/lib;" + if testnum == 0: + cmd += "mpirun -np 1 laghos -pa -p 1 -tf 0.6 -no-vis -m data/cube01_hex.mesh --cg-tol 0 --cg-max-steps 50 --max-steps 2 -ok 2 -ot 1 -rs 5 -d hip;" + elif testnum == 1: + cmd += "mpirun -np 1 laghos -pa -p 1 -tf 0.6 -no-vis -m data/cube_12_hex.mesh --cg-tol 0 --cg-max-steps 50 --max-steps 2 -ok 3 -ot 2 -rs 4 -d hip;" + self.runlog = execshellcmd(cmd, self.logFile, None) + + def clean(self): + print("Cleaning Laghos..") + + def parse_result(self, testnum): + return LaghosParser(self.runlog).parse(testnum) diff --git a/src/testsuite/hpc_apps/laghos/laghos_parser_common.py b/src/testsuite/hpc_apps/laghos/laghos_parser_common.py new file mode 100644 index 0000000..0d4b9a2 --- /dev/null +++ b/src/testsuite/hpc_apps/laghos/laghos_parser_common.py @@ -0,0 +1,39 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.common.hip_shell import execshellcmd +import re + +class LaghosParser(): + def __init__(self, results): + self.results = results + + def parse(self, testnum): + count = 0 + if testnum == 0 or testnum == 1: + if re.search("Major\s*kernels\s*total\s*time\s*\(seconds\):\s*\d+\.\d+", self.results): + count = count + 1 + if re.search("Major\s*kernels\s*total\s*rate\s*\(megadofs\s*x\s*time\s*steps\s*/\s*second\):\s*\d+\.\d+", self.results): + count = count + 1 + if re.search("Energy\s+diff:\s*\d+\.\d+", self.results): + count = count + 1 + if count != 3: + return False + return True diff --git a/src/testsuite/hpc_apps/quicksilver/__init__.py b/src/testsuite/hpc_apps/quicksilver/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/testsuite/hpc_apps/quicksilver/quicksilver.py b/src/testsuite/hpc_apps/quicksilver/quicksilver.py new file mode 100644 index 0000000..980ff0b --- /dev/null +++ b/src/testsuite/hpc_apps/quicksilver/quicksilver.py @@ -0,0 +1,154 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.TesterRepository import Tester, Test, TestData +from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from testsuite.test_classifier import TestClassifier +from testsuite.hpc_apps.quicksilver.quicksilver_build_amd import BuildRunAmd +from testsuite.common.hip_get_packages import HipPackages +from testsuite.common.hip_shell import execshellcmd + +import os +import re +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, cwd, binary): + self.cwdAbs = cwd + self.binary = binary + self.app_path = os.path.join(self.cwdAbs,\ + "src/testsuite/hpc_apps/quicksilver/") + self.app_root = os.path.join(self.app_path, "Quicksilver/") + self.thistestpath = self.app_root + self.prepareobj = None + self.apprepo = "" # Default + self.appbranch = "" + self.appcommitId = "" + + def set_quicksilver_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["quicksilver"].repo_url != None: + self.apprepo = test_data.repos["quicksilver"].repo_url + else: + validrepconfig &= False + if test_data.repos["quicksilver"].branch != None: + self.appbranch = test_data.repos["quicksilver"].branch + if test_data.repos["quicksilver"].commit_id != None: + self.appcommitId = test_data.repos["quicksilver"].commit_id + return validrepconfig + + def downloadtest(self, logFile, test_data: HIPTestData): + ret = True + ret = ret & HipPackages().pull_repo(logFile, self.apprepo,\ + self.appbranch, self.appcommitId, "quicksilver") + return ret + + def buildtest(self, logFile, platform): + isBinaryPresent = True + if platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.thistestpath, logFile) + else: + print("Invalid Platform") + return False + buildstatus = self.prepareobj.buildtest() + if buildstatus == False: + return False + # Check if test binary is created + binarypath = os.path.join(self.thistestpath, "src") + if not os.path.isfile(\ + os.path.join(binarypath, self.binary)): + isBinaryPresent &= False + return isBinaryPresent + + def clean(self): + if self.prepareobj != None: + self.prepareobj.clean() + + def runtest(self): + if self.prepareobj != None: + self.prepareobj.runtest() + + def parse_result(self): + if self.prepareobj != None: + return self.prepareobj.parse_result() + return False + +class QUICKSILVER(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"quicksilver": matched_with_names}) + +class PERFORMANCE(QUICKSILVER): + def __init__(self): + QUICKSILVER.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + QUICKSILVER.add_matched_with_names(self, {"performance": matched_with_names}) + + +# Quicksilver test +class QUICKSILVERTEST(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd, "qs") + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = PERFORMANCE() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== Quicksilver test ===============") + if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: + print("Quicksilver test is not supported on NVIDIA") + test_data.test_result = TestResult.SKIP + return + # Set repo info + isrepocfgvalid = self.set_quicksilver_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/qs.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest() + # Parse the test result + if True == self.parse_result(): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL diff --git a/src/testsuite/hpc_apps/quicksilver/quicksilver_build_amd.py b/src/testsuite/hpc_apps/quicksilver/quicksilver_build_amd.py new file mode 100644 index 0000000..a0ec0db --- /dev/null +++ b/src/testsuite/hpc_apps/quicksilver/quicksilver_build_amd.py @@ -0,0 +1,60 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +from testsuite.common.hip_shell import execshellcmd +from testsuite.hpc_apps.quicksilver.quicksilver_parser_common import QuicksilverParser + +class BuildRunAmd(): + def __init__(self, thistestpath, logFile): + self.thistestpath = thistestpath + self.logFile = logFile + self.runlog = "" + + def buildtest(self): + # In this function put the build steps for test cases + # which differ across platforms (amd/nvidia/intel) + print("Building Quicksilver..") + env = "export ROCM_PATH=/opt/rocm;export PATH=${ROCM_PATH}/bin:$PATH;" + cmdcd = "cd " + self.thistestpath + ";" + cmd_modify = "" + if not os.path.isfile(os.path.join(self.thistestpath, "patched")): + cmd_modify = "patch -p0 < ../quicksilver_diff_patch; touch patched;" + cmd_build = "cd src; make -j;" + cmdexc = env + cmdcd + cmd_modify + cmd_build + execshellcmd(cmdexc, self.logFile, None) + return True + + def runtest(self): + print("Running Quicksilver Test..") + cmdcd = "cd " + self.thistestpath + ";" + "cd src;" + cmdrun = "./qs -i ../Examples/CORAL2_Benchmark/Problem1/Coral2_P1.inp -X 16 -Y 16 -Z 16 -x 16 -y 16 -z 16 -I 1 -J 1 -K 1 -b 2 -n 2621440;" + cmdexc = cmdcd + cmdrun + self.runlog = execshellcmd(cmdexc, self.logFile, None) + + def clean(self): + print("Cleaning Quicksilver..") + cmdcd = "cd " + self.thistestpath + ";" + "cd src;" + cmdrm = "make clean;" + cmdexc = cmdcd + cmdrm + execshellcmd(cmdexc, None, None) + + def parse_result(self): + return QuicksilverParser(self.runlog).parse() diff --git a/src/testsuite/hpc_apps/quicksilver/quicksilver_diff_patch b/src/testsuite/hpc_apps/quicksilver/quicksilver_diff_patch new file mode 100644 index 0000000..c758f4a --- /dev/null +++ b/src/testsuite/hpc_apps/quicksilver/quicksilver_diff_patch @@ -0,0 +1,46 @@ +diff -Naur ../Quicksilver_orig/src/AtomicMacro.hh src/AtomicMacro.hh +--- ../Quicksilver_orig/src/AtomicMacro.hh 2021-09-24 16:48:20.970111488 +0530 ++++ src/AtomicMacro.hh 2021-09-24 17:09:18.177032332 +0530 +@@ -25,7 +25,7 @@ + #include + #include + #ifdef HAVE_HIP +- #include ++ #include + #endif + + +diff -Naur ../Quicksilver_orig/src/Makefile src/Makefile +--- ../Quicksilver_orig/src/Makefile 2021-09-24 16:48:20.974111483 +0530 ++++ src/Makefile 2021-09-24 17:10:19.804984064 +0530 +@@ -114,20 +114,20 @@ + ##################################################################################### + #hip, no MPI + ##################################################################################### +-#CXX = $(ROCM_PATH)/bin/hipcc +-#CXXFLAGS = -I$(ROCM_PATH)/include/ +-#CPPFLAGS = -DHAVE_HIP=1 -DMaxIt=15 +-#LDFLAGS = -L$(ROCM_PATH)/lib -L$(ROCM_PATH)/lib ++CXX = $(ROCM_PATH)/bin/hipcc ++CXXFLAGS = -I$(ROCM_PATH)/include/ ++CPPFLAGS = -DHAVE_HIP=1 -DMaxIt=15 ++LDFLAGS = -L$(ROCM_PATH)/lib -L$(ROCM_PATH)/lib + + ############################################################################ + #hip + mpi + ############################################################################# +-CXX = $(HIP)/bin/hipcc +-CXXFLAGS1 = -I$(HIP)/include/ +-CXXFLAGS2 = $(CXXFLAGS1) -I$(MPIPATH)/include +-CXXFLAGS = $(CXXFLAGS2) -pthread +-CPPFLAGS = -DHAVE_HIP=1 -DHAVE_MPI -DMaxIt=15 +-LDFLAGS = -L$(HIP)/lib -L$(MPIPATH)/lib -lmpi ++#CXX = $(HIP)/bin/hipcc ++#CXXFLAGS1 = -I$(HIP)/include/ ++#CXXFLAGS2 = $(CXXFLAGS1) -I$(MPIPATH)/include ++#CXXFLAGS = $(CXXFLAGS2) -pthread ++#CPPFLAGS = -DHAVE_HIP=1 -DHAVE_MPI -DMaxIt=15 ++#LDFLAGS = -L$(HIP)/lib -L$(MPIPATH)/lib -lmpi + ######################################################################## + #hip + nvcc + ###################################################################### diff --git a/src/testsuite/hpc_apps/quicksilver/quicksilver_parser_common.py b/src/testsuite/hpc_apps/quicksilver/quicksilver_parser_common.py new file mode 100644 index 0000000..5b3e06b --- /dev/null +++ b/src/testsuite/hpc_apps/quicksilver/quicksilver_parser_common.py @@ -0,0 +1,42 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from testsuite.common.hip_shell import execshellcmd +import re + +class QuicksilverParser(): + def __init__(self, results): + self.results = results + + def parse(self): + passed1 = False + passed2 = False + passed3 = False + passed4 = False + if re.search("PASS:: Absorption / Fission / Scatter Ratios maintained with \d+% tolerance", self.results): + passed1 = True + if re.search("PASS:: Collision to Facet Crossing Ratio maintained even balanced within \d+% tolerance", self.results): + passed2 = True + if re.search("PASS:: No Particles Lost During Run", self.results): + passed3 = True + if re.search("PASS:: Fluence is homogenous across cells with \d+% tolerance", self.results): + passed4 = True + + return (passed1 & passed2 & passed3 & passed4) diff --git a/src/amd/list_tests.py b/src/testsuite/list_tests.py similarity index 97% rename from src/amd/list_tests.py rename to src/testsuite/list_tests.py index 270326f..fe09c1e 100644 --- a/src/amd/list_tests.py +++ b/src/testsuite/list_tests.py @@ -22,9 +22,9 @@ import typing from typing import Union, List -from amd.TesterRepository import TesterRepository, GetTests -from amd.Test import Test, Quick -from amd.test_classifier import TestClassifier +from testsuite.TesterRepository import TesterRepository, GetTests +from testsuite.Test import Test, Quick +from testsuite.test_classifier import TestClassifier def list_tests(quick: bool, cfg, tester_repository=None): diff --git a/src/amd/match_fun_args_call.py b/src/testsuite/match_fun_args_call.py similarity index 100% rename from src/amd/match_fun_args_call.py rename to src/testsuite/match_fun_args_call.py diff --git a/src/testsuite/stress/__init__.py b/src/testsuite/stress/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/targets.py b/src/testsuite/targets.py similarity index 99% rename from src/amd/targets.py rename to src/testsuite/targets.py index b4be04a..070b52f 100644 --- a/src/amd/targets.py +++ b/src/testsuite/targets.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.AMD import AMDObject +from testsuite.AMD import AMDObject class Target: diff --git a/src/amd/test_classifier.py b/src/testsuite/test_classifier.py similarity index 97% rename from src/amd/test_classifier.py rename to src/testsuite/test_classifier.py index f985175..a99f0a0 100644 --- a/src/amd/test_classifier.py +++ b/src/testsuite/test_classifier.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.AMD import AMDObject +from testsuite.AMD import AMDObject from typing import Union diff --git a/src/amd/test_selector.py b/src/testsuite/test_selector.py similarity index 74% rename from src/amd/test_selector.py rename to src/testsuite/test_selector.py index 58e4d17..f1740f2 100644 --- a/src/amd/test_selector.py +++ b/src/testsuite/test_selector.py @@ -18,8 +18,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.AMD import AMDObject -from amd.TesterRepository import TesterRepository, Test, GetTests +from testsuite.AMD import AMDObject +from testsuite.TesterRepository import TesterRepository, Test, GetTests from typing import Union, List, Dict import re @@ -69,6 +69,45 @@ def to_select_this_test(self, test: Test, test_name_regexes): select_this_test = True return select_this_test + def check_quicktestlist(self, test_to_find, quicktestlist): + testfound = False + for test in quicktestlist: + if re.search(test_to_find, test.test_name): + testfound = True + return testfound + + def check_classifierlist(self, test_to_find, classifierlist): + testfound = False + for thisclassifier in classifierlist: + if re.search(test_to_find, thisclassifier): + testfound = True + return testfound + + def check_quicktestlist_sufficient(self, usertestlist, quicktestlist, classifierlist): + testcount = 0 + for test in usertestlist: + if self.check_classifierlist(test, classifierlist): + # We know this test is non conformance + testcount = testcount + 1 + elif self.check_quicktestlist(test, quicktestlist): + testcount = testcount + 1 + isSufficient = True + if len(usertestlist) != testcount: + # quicktestlist is not sufficient + isSufficient = False + return isSufficient + + def get_all_classifierkeys(self, testclassifierdict, classifierlist): + for key in testclassifierdict: + classifierlist.append(key) + if testclassifierdict[key] != None: + if isinstance(testclassifierdict[key], dict): + self.get_all_classifierkeys(testclassifierdict[key], classifierlist) + + def get_all_classifiers(self, testclassifier, classifierlist): + for myclassifier in testclassifier: + self.get_all_classifierkeys(myclassifier.matched_with_names, classifierlist) + def select_tests(self, log_location: str) -> List[Test]: config = self.config tests = list() @@ -91,6 +130,17 @@ def select_tests(self, log_location: str) -> List[Test]: if select_this_test: tests.append(test_of_tester) + classifierlist = list() + for thistest in tests: + tmpclassifierlist = list() + self.get_all_classifiers(thistest.classifiers, tmpclassifierlist) + thistestclassification = "" + for elem in tmpclassifierlist: + thistestclassification = thistestclassification + elem + ":" + classifierlist.append(thistestclassification) + # Check at this point if quick search is sufficient + if False == self.check_quicktestlist_sufficient(test_name_regexes, tests, classifierlist): + tests.clear() if not tests: for test_of_tester in self.get_tests(log_location=log_location, quick=False): select_this_test = self.to_select_this_test(test=test_of_tester, test_name_regexes=test_name_regexes) diff --git a/src/testsuite/thirdparty/__init__.py b/src/testsuite/thirdparty/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/amd/version.py b/src/testsuite/version.py similarity index 100% rename from src/amd/version.py rename to src/testsuite/version.py From e0b88627f28a46e3ad188af741944762648b8a91 Mon Sep 17 00:00:00 2001 From: Mohan Kumar Mithur Date: Mon, 4 Oct 2021 13:47:54 +0530 Subject: [PATCH 10/18] Moved hpc_apps under src/testsuite/applications Change-Id: Id1c9b6309a1018e090bd518c3ee8a7b7e675a7e8 --- .../{ => applications}/hpc_apps/__init__.py | 0 .../hpc_apps/gridtools/__init__.py | 0 .../hpc_apps/gridtools/gridtools.py | 12 ++++++------ .../hpc_apps/gridtools/gridtools_build_amd.py | 4 ++-- .../hpc_apps/gridtools/gridtools_parser_common.py | 0 .../hpc_apps/gridtools/gtbench.patch | 0 .../{ => applications}/hpc_apps/kokkos/__init__.py | 0 .../{ => applications}/hpc_apps/kokkos/kokkos.py | 4 ++-- .../hpc_apps/kokkos/kokkos_build_amd.py | 2 +- .../hpc_apps/kokkos/kokkos_parser_common.py | 0 .../{ => applications}/hpc_apps/laghos/__init__.py | 0 .../{ => applications}/hpc_apps/laghos/laghos.py | 4 ++-- .../hpc_apps/laghos/laghos_build_amd.py | 2 +- .../hpc_apps/laghos/laghos_parser_common.py | 0 .../hpc_apps/quicksilver/__init__.py | 0 .../hpc_apps/quicksilver/quicksilver.py | 4 ++-- .../hpc_apps/quicksilver/quicksilver_build_amd.py | 2 +- .../hpc_apps/quicksilver/quicksilver_diff_patch | 0 .../quicksilver/quicksilver_parser_common.py | 0 src/testsuite/common/hip_get_packages.py | 14 +++++++------- 20 files changed, 24 insertions(+), 24 deletions(-) rename src/testsuite/{ => applications}/hpc_apps/__init__.py (100%) rename src/testsuite/{ => applications}/hpc_apps/gridtools/__init__.py (100%) rename src/testsuite/{ => applications}/hpc_apps/gridtools/gridtools.py (96%) rename src/testsuite/{ => applications}/hpc_apps/gridtools/gridtools_build_amd.py (97%) rename src/testsuite/{ => applications}/hpc_apps/gridtools/gridtools_parser_common.py (100%) rename src/testsuite/{ => applications}/hpc_apps/gridtools/gtbench.patch (100%) rename src/testsuite/{ => applications}/hpc_apps/kokkos/__init__.py (100%) rename src/testsuite/{ => applications}/hpc_apps/kokkos/kokkos.py (98%) rename src/testsuite/{ => applications}/hpc_apps/kokkos/kokkos_build_amd.py (97%) rename src/testsuite/{ => applications}/hpc_apps/kokkos/kokkos_parser_common.py (100%) rename src/testsuite/{ => applications}/hpc_apps/laghos/__init__.py (100%) rename src/testsuite/{ => applications}/hpc_apps/laghos/laghos.py (98%) rename src/testsuite/{ => applications}/hpc_apps/laghos/laghos_build_amd.py (99%) rename src/testsuite/{ => applications}/hpc_apps/laghos/laghos_parser_common.py (100%) rename src/testsuite/{ => applications}/hpc_apps/quicksilver/__init__.py (100%) rename src/testsuite/{ => applications}/hpc_apps/quicksilver/quicksilver.py (97%) rename src/testsuite/{ => applications}/hpc_apps/quicksilver/quicksilver_build_amd.py (96%) rename src/testsuite/{ => applications}/hpc_apps/quicksilver/quicksilver_diff_patch (100%) rename src/testsuite/{ => applications}/hpc_apps/quicksilver/quicksilver_parser_common.py (100%) diff --git a/src/testsuite/hpc_apps/__init__.py b/src/testsuite/applications/hpc_apps/__init__.py similarity index 100% rename from src/testsuite/hpc_apps/__init__.py rename to src/testsuite/applications/hpc_apps/__init__.py diff --git a/src/testsuite/hpc_apps/gridtools/__init__.py b/src/testsuite/applications/hpc_apps/gridtools/__init__.py similarity index 100% rename from src/testsuite/hpc_apps/gridtools/__init__.py rename to src/testsuite/applications/hpc_apps/gridtools/__init__.py diff --git a/src/testsuite/hpc_apps/gridtools/gridtools.py b/src/testsuite/applications/hpc_apps/gridtools/gridtools.py similarity index 96% rename from src/testsuite/hpc_apps/gridtools/gridtools.py rename to src/testsuite/applications/hpc_apps/gridtools/gridtools.py index de62ac0..c4bf490 100644 --- a/src/testsuite/hpc_apps/gridtools/gridtools.py +++ b/src/testsuite/applications/hpc_apps/gridtools/gridtools.py @@ -22,7 +22,7 @@ from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List from testsuite.test_classifier import TestClassifier -from testsuite.hpc_apps.gridtools.gridtools_build_amd import BuildRunAmd +from testsuite.applications.hpc_apps.gridtools.gridtools_build_amd import BuildRunAmd from testsuite.common.hip_get_packages import HipPackages from testsuite.common.hip_shell import execshellcmd @@ -33,7 +33,7 @@ class PrepareTest(): def __init__(self, cwd): self.cwdAbs = cwd self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/hpc_apps/gridtools") + "src/testsuite/applications/hpc_apps/gridtools") self.thistestpath = self.app_path self.prepareobj = None self.gridtoolsrepo = "" # Default @@ -140,8 +140,8 @@ def test(self, test_data: HIPTestData): # Check if Boost package exists if not os.path.isfile(\ os.path.join(self.thistestpath, "boost_1_72_0.tar.bz2")): - print("Boost Package boost_1_72_0.tar.bz2 not available under src/testsuite/hpc_apps/gridtools/.") - print("Please download and copy Boost package in src/testsuite/hpc_apps/gridtools folder.") + print("Boost Package boost_1_72_0.tar.bz2 not available under src/testsuite/applications/hpc_apps/gridtools/.") + print("Please download and copy Boost package in src/testsuite/applications/hpc_apps/gridtools folder.") test_data.test_result = TestResult.ERROR return # Create GT_TREE_DIR @@ -203,8 +203,8 @@ def test(self, test_data: HIPTestData): # Check if Boost package exists if not os.path.isfile(\ os.path.join(self.thistestpath, "boost_1_72_0.tar.bz2")): - print("Boost Package boost_1_72_0.tar.bz2 not available under src/testsuite/hpc_apps/gridtools/.") - print("Please download and copy Boost package in src/testsuite/hpc_apps/gridtools folder.") + print("Boost Package boost_1_72_0.tar.bz2 not available under src/testsuite/applications/hpc_apps/gridtools/.") + print("Please download and copy Boost package in src/testsuite/applications/hpc_apps/gridtools folder.") test_data.test_result = TestResult.ERROR return diff --git a/src/testsuite/hpc_apps/gridtools/gridtools_build_amd.py b/src/testsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py similarity index 97% rename from src/testsuite/hpc_apps/gridtools/gridtools_build_amd.py rename to src/testsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py index 9bd68e5..11b4b27 100644 --- a/src/testsuite/hpc_apps/gridtools/gridtools_build_amd.py +++ b/src/testsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py @@ -21,7 +21,7 @@ import os import tempfile from testsuite.common.hip_shell import * -from testsuite.hpc_apps.gridtools.gridtools_parser_common import GridtoolsParser +from testsuite.applications.hpc_apps.gridtools.gridtools_parser_common import GridtoolsParser class BuildRunAmd(): def __init__(self, thistestpath, logFile): @@ -37,7 +37,7 @@ def setenv(self, gpu_arch): env += "export ROCMHOME=/opt/rocm;" env += "export PATH=$ROCMHOME/bin:$ROCMHOME/llvm/bin:$ROCMHOME/hip/bin:$ROCMHOME/opencl/bin:$ROCMHOME/rocprofiler/bin:$PATH;" env += "export LD_LIBRARY_PATH=/$ROCMHOME/lib:$ROCMHOME/llvm/lib:$LD_LIBRARY_PATH;" - env += "export GT_ALL_DIR=$PWD/src/testsuite/hpc_apps/gridtools;" + env += "export GT_ALL_DIR=$PWD/src/testsuite/applications/hpc_apps/gridtools;" env += "export GT_TREE_DIR=$GT_ALL_DIR/GridTools;" env += "export BOOST_TREE_DIR=$GT_TREE_DIR/boost_1_72_0;" env += "export BOOST_INSTALL_DIR=$GT_ALL_DIR/boost_1_72_0;" diff --git a/src/testsuite/hpc_apps/gridtools/gridtools_parser_common.py b/src/testsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py similarity index 100% rename from src/testsuite/hpc_apps/gridtools/gridtools_parser_common.py rename to src/testsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py diff --git a/src/testsuite/hpc_apps/gridtools/gtbench.patch b/src/testsuite/applications/hpc_apps/gridtools/gtbench.patch similarity index 100% rename from src/testsuite/hpc_apps/gridtools/gtbench.patch rename to src/testsuite/applications/hpc_apps/gridtools/gtbench.patch diff --git a/src/testsuite/hpc_apps/kokkos/__init__.py b/src/testsuite/applications/hpc_apps/kokkos/__init__.py similarity index 100% rename from src/testsuite/hpc_apps/kokkos/__init__.py rename to src/testsuite/applications/hpc_apps/kokkos/__init__.py diff --git a/src/testsuite/hpc_apps/kokkos/kokkos.py b/src/testsuite/applications/hpc_apps/kokkos/kokkos.py similarity index 98% rename from src/testsuite/hpc_apps/kokkos/kokkos.py rename to src/testsuite/applications/hpc_apps/kokkos/kokkos.py index f5dcf88..d8720e8 100644 --- a/src/testsuite/hpc_apps/kokkos/kokkos.py +++ b/src/testsuite/applications/hpc_apps/kokkos/kokkos.py @@ -22,7 +22,7 @@ from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List from testsuite.test_classifier import TestClassifier -from testsuite.hpc_apps.kokkos.kokkos_build_amd import BuildRunAmd +from testsuite.applications.hpc_apps.kokkos.kokkos_build_amd import BuildRunAmd from testsuite.common.hip_get_packages import HipPackages from testsuite.common.hip_shell import execshellcmd @@ -33,7 +33,7 @@ class PrepareTest(): def __init__(self, cwd): self.cwdAbs = cwd self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/hpc_apps/kokkos/") + "src/testsuite/applications/hpc_apps/kokkos/") self.app_root = os.path.join(self.app_path, "kokkos/") self.thistestpath = self.app_root self.prepareobj = None diff --git a/src/testsuite/hpc_apps/kokkos/kokkos_build_amd.py b/src/testsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py similarity index 97% rename from src/testsuite/hpc_apps/kokkos/kokkos_build_amd.py rename to src/testsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py index a021c2f..8bd81c4 100644 --- a/src/testsuite/hpc_apps/kokkos/kokkos_build_amd.py +++ b/src/testsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py @@ -22,7 +22,7 @@ import re import tempfile from testsuite.common.hip_shell import * -from testsuite.hpc_apps.kokkos.kokkos_parser_common import KokkosParser +from testsuite.applications.hpc_apps.kokkos.kokkos_parser_common import KokkosParser class BuildRunAmd(): def __init__(self, thistestpath, logFile): diff --git a/src/testsuite/hpc_apps/kokkos/kokkos_parser_common.py b/src/testsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py similarity index 100% rename from src/testsuite/hpc_apps/kokkos/kokkos_parser_common.py rename to src/testsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py diff --git a/src/testsuite/hpc_apps/laghos/__init__.py b/src/testsuite/applications/hpc_apps/laghos/__init__.py similarity index 100% rename from src/testsuite/hpc_apps/laghos/__init__.py rename to src/testsuite/applications/hpc_apps/laghos/__init__.py diff --git a/src/testsuite/hpc_apps/laghos/laghos.py b/src/testsuite/applications/hpc_apps/laghos/laghos.py similarity index 98% rename from src/testsuite/hpc_apps/laghos/laghos.py rename to src/testsuite/applications/hpc_apps/laghos/laghos.py index c3d756a..a092c2d 100644 --- a/src/testsuite/hpc_apps/laghos/laghos.py +++ b/src/testsuite/applications/hpc_apps/laghos/laghos.py @@ -22,7 +22,7 @@ from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List from testsuite.test_classifier import TestClassifier -from testsuite.hpc_apps.laghos.laghos_build_amd import BuildRunAmd +from testsuite.applications.hpc_apps.laghos.laghos_build_amd import BuildRunAmd from testsuite.common.hip_get_packages import HipPackages from testsuite.common.hip_shell import execshellcmd @@ -33,7 +33,7 @@ class PrepareTest(): def __init__(self, cwd): self.cwdAbs = cwd self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/hpc_apps/laghos/") + "src/testsuite/applications/hpc_apps/laghos/") self.thistestpath = self.app_path self.prepareobj = None diff --git a/src/testsuite/hpc_apps/laghos/laghos_build_amd.py b/src/testsuite/applications/hpc_apps/laghos/laghos_build_amd.py similarity index 99% rename from src/testsuite/hpc_apps/laghos/laghos_build_amd.py rename to src/testsuite/applications/hpc_apps/laghos/laghos_build_amd.py index 43c1008..d90993f 100644 --- a/src/testsuite/hpc_apps/laghos/laghos_build_amd.py +++ b/src/testsuite/applications/hpc_apps/laghos/laghos_build_amd.py @@ -23,7 +23,7 @@ import tempfile from testsuite.Test import HIPTestData from testsuite.common.hip_shell import * -from testsuite.hpc_apps.laghos.laghos_parser_common import LaghosParser +from testsuite.applications.hpc_apps.laghos.laghos_parser_common import LaghosParser class BuildRunAmd(): def __init__(self, thistestpath, logFile): diff --git a/src/testsuite/hpc_apps/laghos/laghos_parser_common.py b/src/testsuite/applications/hpc_apps/laghos/laghos_parser_common.py similarity index 100% rename from src/testsuite/hpc_apps/laghos/laghos_parser_common.py rename to src/testsuite/applications/hpc_apps/laghos/laghos_parser_common.py diff --git a/src/testsuite/hpc_apps/quicksilver/__init__.py b/src/testsuite/applications/hpc_apps/quicksilver/__init__.py similarity index 100% rename from src/testsuite/hpc_apps/quicksilver/__init__.py rename to src/testsuite/applications/hpc_apps/quicksilver/__init__.py diff --git a/src/testsuite/hpc_apps/quicksilver/quicksilver.py b/src/testsuite/applications/hpc_apps/quicksilver/quicksilver.py similarity index 97% rename from src/testsuite/hpc_apps/quicksilver/quicksilver.py rename to src/testsuite/applications/hpc_apps/quicksilver/quicksilver.py index 980ff0b..1080094 100644 --- a/src/testsuite/hpc_apps/quicksilver/quicksilver.py +++ b/src/testsuite/applications/hpc_apps/quicksilver/quicksilver.py @@ -22,7 +22,7 @@ from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List from testsuite.test_classifier import TestClassifier -from testsuite.hpc_apps.quicksilver.quicksilver_build_amd import BuildRunAmd +from testsuite.applications.hpc_apps.quicksilver.quicksilver_build_amd import BuildRunAmd from testsuite.common.hip_get_packages import HipPackages from testsuite.common.hip_shell import execshellcmd @@ -34,7 +34,7 @@ def __init__(self, cwd, binary): self.cwdAbs = cwd self.binary = binary self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/hpc_apps/quicksilver/") + "src/testsuite/applications/hpc_apps/quicksilver/") self.app_root = os.path.join(self.app_path, "Quicksilver/") self.thistestpath = self.app_root self.prepareobj = None diff --git a/src/testsuite/hpc_apps/quicksilver/quicksilver_build_amd.py b/src/testsuite/applications/hpc_apps/quicksilver/quicksilver_build_amd.py similarity index 96% rename from src/testsuite/hpc_apps/quicksilver/quicksilver_build_amd.py rename to src/testsuite/applications/hpc_apps/quicksilver/quicksilver_build_amd.py index a0ec0db..25acea0 100644 --- a/src/testsuite/hpc_apps/quicksilver/quicksilver_build_amd.py +++ b/src/testsuite/applications/hpc_apps/quicksilver/quicksilver_build_amd.py @@ -20,7 +20,7 @@ import os from testsuite.common.hip_shell import execshellcmd -from testsuite.hpc_apps.quicksilver.quicksilver_parser_common import QuicksilverParser +from testsuite.applications.hpc_apps.quicksilver.quicksilver_parser_common import QuicksilverParser class BuildRunAmd(): def __init__(self, thistestpath, logFile): diff --git a/src/testsuite/hpc_apps/quicksilver/quicksilver_diff_patch b/src/testsuite/applications/hpc_apps/quicksilver/quicksilver_diff_patch similarity index 100% rename from src/testsuite/hpc_apps/quicksilver/quicksilver_diff_patch rename to src/testsuite/applications/hpc_apps/quicksilver/quicksilver_diff_patch diff --git a/src/testsuite/hpc_apps/quicksilver/quicksilver_parser_common.py b/src/testsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py similarity index 100% rename from src/testsuite/hpc_apps/quicksilver/quicksilver_parser_common.py rename to src/testsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py diff --git a/src/testsuite/common/hip_get_packages.py b/src/testsuite/common/hip_get_packages.py index ec9d275..f625153 100644 --- a/src/testsuite/common/hip_get_packages.py +++ b/src/testsuite/common/hip_get_packages.py @@ -48,22 +48,22 @@ def __init__(self): self.cudamemrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/cuda_memtest/") self.cudamemapppath = os.path.join(self.cudamemrootpath, "cuda_memtest/") # Quicksilver - self.qsrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/quicksilver/") + self.qsrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/quicksilver/") self.qsapppath = os.path.join(self.qsrootpath, "Quicksilver/") # Gridtools - self.gridtoolsrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/gridtools/GridTools/") + self.gridtoolsrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/gridtools/GridTools/") self.gridtoolsapppath = os.path.join(self.gridtoolsrootpath, "gridtools/") - self.gtbenchrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/gridtools/GridTools/") + self.gtbenchrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/gridtools/GridTools/") self.gtbenchapppath = os.path.join(self.gtbenchrootpath, "gtbench/") # Kokkos - self.kokkosrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/kokkos/") + self.kokkosrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/kokkos/") self.kokkosapppath = os.path.join(self.kokkosrootpath, "kokkos/") # Laghos - self.mfemrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/laghos/") + self.mfemrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/laghos/") self.mfemapppath = os.path.join(self.mfemrootpath, "mfem/") - self.laghosrootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/laghos/") + self.laghosrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/laghos/") self.laghosapppath = os.path.join(self.laghosrootpath, "Laghos/") - self.mpirootpath = os.path.join(self.cwdAbs, "src/testsuite/hpc_apps/laghos/") + self.mpirootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/laghos/") self.mpiapppath = os.path.join(self.mpirootpath, "openmpi/") def pull_repo(self, logFile, repo, branch, commitId, reponame): From 48fe4744ff83a9d6582b3a32d573a934885451b8 Mon Sep 17 00:00:00 2001 From: Rupam Chetia Date: Mon, 4 Oct 2021 19:09:54 +0530 Subject: [PATCH 11/18] Changes and bug fixes 1. Changed testsuite to hiptesuite 2. Update messages during clone 3. Take make target for samples and use target for hipInfo 4. Fixed wrong string output in Kokkos 5. Removed openmpi cloning and building for laghos. Change-Id: I462d1c288dd2e39a572544eb6feeeac87b2e3420 --- cfg.py | 7 +- run.py | 4 +- src/{testsuite => hiptestsuite}/AMD.py | 0 src/{testsuite => hiptestsuite}/Test.py | 10 +- .../TesterRepository.py | 14 +-- .../TestersExecutor.py | 8 +- src/{testsuite => hiptestsuite}/__init__.py | 0 .../applications/__init__.py | 0 .../application_test_classifier.py | 2 +- .../applications/cuda_grep/__init__.py | 0 .../applications/cuda_grep/cuda_grep.py | 16 +-- .../cuda_grep/cuda_grep_build_amd.py | 4 +- .../cuda_grep/cuda_grep_build_nvidia.py | 4 +- .../cuda_grep/cuda_grep_parser_common.py | 2 +- .../applications/cuda_memtest/__init__.py | 0 .../applications/cuda_memtest/cuda_memtest.py | 16 +-- .../cuda_memtest/cuda_memtest_build_amd.py | 4 +- .../cuda_memtest/cuda_memtest_build_nvidia.py | 4 +- .../cuda_memtest_parser_common.py | 2 +- .../cuda_memtest/cuda_memtest_patch | 0 .../applications/hip_examples/__init__.py | 0 .../applications/hip_examples/hip_examples.py | 16 +-- .../hip_examples/hip_examples_build_amd.py | 6 +- .../hip_examples/hip_examples_build_common.py | 4 +- .../hip_examples/hip_examples_build_nvidia.py | 8 +- .../hip_examples/hip_examples_parser.py | 0 .../hip_samples/Samples_Patch_4.2.x | 0 .../applications/hip_samples/__init__.py | 0 .../applications/hip_samples/hip_samples.py | 22 ++-- .../hip_samples/hip_samples_build_amd.py | 8 +- .../hip_samples/hip_samples_build_common.py | 9 +- .../hip_samples/hip_samples_build_nvidia.py | 10 +- .../applications/hpc_apps/__init__.py | 0 .../hpc_apps/gridtools/__init__.py | 0 .../hpc_apps/gridtools/gridtools.py | 22 ++-- .../hpc_apps/gridtools/gridtools_build_amd.py | 6 +- .../gridtools/gridtools_parser_common.py | 2 +- .../hpc_apps/gridtools/gtbench.patch | 0 .../applications/hpc_apps/kokkos/__init__.py | 0 .../applications/hpc_apps/kokkos/kokkos.py | 14 +-- .../hpc_apps/kokkos/kokkos_build_amd.py | 8 +- .../hpc_apps/kokkos/kokkos_parser_common.py | 2 +- .../applications/hpc_apps/laghos/__init__.py | 0 .../applications/hpc_apps/laghos/laghos.py | 38 ++----- .../hpc_apps/laghos/laghos_build_amd.py | 99 +++--------------- .../hpc_apps/laghos/laghos_parser_common.py | 2 +- .../hpc_apps/quicksilver/__init__.py | 0 .../hpc_apps/quicksilver/quicksilver.py | 14 +-- .../quicksilver/quicksilver_build_amd.py | 4 +- .../quicksilver/quicksilver_diff_patch | 0 .../quicksilver/quicksilver_parser_common.py | 2 +- .../keccaktreegpu/KeccakTreeGpu/KeccakF.c | 0 .../keccaktreegpu/KeccakTreeGpu/KeccakF.h | 0 .../keccaktreegpu/KeccakTreeGpu/KeccakTree.h | 0 .../KeccakTreeGpu/KeccakTreeCPU.c | 0 .../KeccakTreeGpu/KeccakTreeCPU.h | 0 .../KeccakTreeGpu/KeccakTreeGPU.cu | 0 .../KeccakTreeGpu/KeccakTreeGPU.h | 0 .../keccaktreegpu/KeccakTreeGpu/KeccakTypes.h | 0 .../keccaktreegpu/KeccakTreeGpu/Makefile | 0 .../keccaktreegpu/KeccakTreeGpu/README.txt | 0 .../keccaktreegpu/KeccakTreeGpu/Test.c | 0 .../keccaktreegpu/KeccakTreeGpu/Test.h | 0 .../VCKeccakTree/Release/BuildLog.htm | Bin .../VCKeccakTree/Release/KeccakF.obj | Bin .../VCKeccakTree/Release/KeccakTreeCPU.obj | Bin .../VCKeccakTree/Release/KeccakTreeGPU.cu.obj | Bin .../VCKeccakTree/Release/Test.obj | Bin .../Release/VCKeccakTree.ex.renameit | Bin .../VCKeccakTree.exe.intermediate.manifest | 0 .../VCKeccakTree/Release/VCKeccakTree.pdb | Bin .../VCKeccakTree/Release/cudart32_30_14.dll | Bin .../VCKeccakTree/Release/main.obj | Bin .../KeccakTreeGpu/VCKeccakTree/Release/mt.dep | 0 .../VCKeccakTree/Release/vc90.idb | Bin .../VCKeccakTree/Release/vc90.pdb | Bin .../VCKeccakTree/VCKeccakTree.ncb | Bin .../VCKeccakTree/VCKeccakTree.sln | 0 .../VCKeccakTree/VCKeccakTree.suo | Bin .../VCKeccakTree/VCKeccakTree.vcproj | 0 .../VCKeccakTree.vcproj.DELLM24.guigui.user | 0 .../KeccakTreeGpu/VCKeccakTree/vc90.pdb | Bin .../keccaktreegpu/KeccakTreeGpu/main.c | 0 .../applications/keccaktreegpu/__init__.py | 0 .../keccaktreegpu/keccaktreegpu.py | 16 +-- .../keccaktreegpu/keccaktreegpu_build_amd.py | 4 +- .../keccaktreegpu_build_nvidia.py | 4 +- .../keccaktreegpu_parser_common.py | 2 +- .../applications/mgbench/__init__.py | 0 .../applications/mgbench/mgbench.py | 16 +-- .../applications/mgbench/mgbench_build_amd.py | 4 +- .../mgbench/mgbench_build_nvidia.py | 4 +- .../mgbench/mgbench_parser_common.py | 2 +- .../common/__init__.py | 0 .../common/hip_get_packages.py | 34 +++--- .../common/hip_shell.py | 0 .../config_processor.py | 2 +- .../conformance/CMakePatch | 0 .../conformance/__init__.py | 0 .../conformance_test_classifier.py | 2 +- .../conformance/hip_dtest.py | 16 +-- .../conformance/hip_dtest_build_amd.py | 4 +- .../conformance/hip_dtest_build_common.py | 4 +- .../conformance/hip_dtest_build_nvidia.py | 4 +- src/{testsuite => hiptestsuite}/list_tests.py | 6 +- .../match_fun_args_call.py | 0 .../stress/__init__.py | 0 src/{testsuite => hiptestsuite}/targets.py | 2 +- .../test_classifier.py | 2 +- .../test_selector.py | 4 +- .../thirdparty/__init__.py | 0 src/{testsuite => hiptestsuite}/version.py | 0 112 files changed, 217 insertions(+), 308 deletions(-) rename src/{testsuite => hiptestsuite}/AMD.py (100%) rename src/{testsuite => hiptestsuite}/Test.py (96%) rename src/{testsuite => hiptestsuite}/TesterRepository.py (93%) rename src/{testsuite => hiptestsuite}/TestersExecutor.py (98%) rename src/{testsuite => hiptestsuite}/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/application_test_classifier.py (96%) rename src/{testsuite => hiptestsuite}/applications/cuda_grep/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/cuda_grep/cuda_grep.py (91%) rename src/{testsuite => hiptestsuite}/applications/cuda_grep/cuda_grep_build_amd.py (95%) rename src/{testsuite => hiptestsuite}/applications/cuda_grep/cuda_grep_build_nvidia.py (95%) rename src/{testsuite => hiptestsuite}/applications/cuda_grep/cuda_grep_parser_common.py (96%) rename src/{testsuite => hiptestsuite}/applications/cuda_memtest/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/cuda_memtest/cuda_memtest.py (97%) rename src/{testsuite => hiptestsuite}/applications/cuda_memtest/cuda_memtest_build_amd.py (95%) rename src/{testsuite => hiptestsuite}/applications/cuda_memtest/cuda_memtest_build_nvidia.py (95%) rename src/{testsuite => hiptestsuite}/applications/cuda_memtest/cuda_memtest_parser_common.py (96%) rename src/{testsuite => hiptestsuite}/applications/cuda_memtest/cuda_memtest_patch (100%) rename src/{testsuite => hiptestsuite}/applications/hip_examples/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/hip_examples/hip_examples.py (99%) rename src/{testsuite => hiptestsuite}/applications/hip_examples/hip_examples_build_amd.py (90%) rename src/{testsuite => hiptestsuite}/applications/hip_examples/hip_examples_build_common.py (99%) rename src/{testsuite => hiptestsuite}/applications/hip_examples/hip_examples_build_nvidia.py (88%) rename src/{testsuite => hiptestsuite}/applications/hip_examples/hip_examples_parser.py (100%) rename src/{testsuite => hiptestsuite}/applications/hip_samples/Samples_Patch_4.2.x (100%) rename src/{testsuite => hiptestsuite}/applications/hip_samples/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/hip_samples/hip_samples.py (98%) rename src/{testsuite => hiptestsuite}/applications/hip_samples/hip_samples_build_amd.py (87%) rename src/{testsuite => hiptestsuite}/applications/hip_samples/hip_samples_build_common.py (89%) rename src/{testsuite => hiptestsuite}/applications/hip_samples/hip_samples_build_nvidia.py (87%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/gridtools/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/gridtools/gridtools.py (93%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/gridtools/gridtools_build_amd.py (96%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/gridtools/gridtools_parser_common.py (97%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/gridtools/gtbench.patch (100%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/kokkos/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/kokkos/kokkos.py (94%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/kokkos/kokkos_build_amd.py (93%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/kokkos/kokkos_parser_common.py (96%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/laghos/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/laghos/laghos.py (84%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/laghos/laghos_build_amd.py (64%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/laghos/laghos_parser_common.py (97%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/quicksilver/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/quicksilver/quicksilver.py (92%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/quicksilver/quicksilver_build_amd.py (94%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/quicksilver/quicksilver_diff_patch (100%) rename src/{testsuite => hiptestsuite}/applications/hpc_apps/quicksilver/quicksilver_parser_common.py (97%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.c (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.h (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/KeccakTree.h (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.c (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.h (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.cu (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.h (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/KeccakTypes.h (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/Makefile (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/README.txt (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/Test.c (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/Test.h (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/BuildLog.htm (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakF.obj (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeCPU.obj (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeGPU.cu.obj (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/Test.obj (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.ex.renameit (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.exe.intermediate.manifest (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.pdb (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/cudart32_30_14.dll (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/main.obj (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/mt.dep (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.idb (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.pdb (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.ncb (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.sln (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.suo (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj.DELLM24.guigui.user (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/vc90.pdb (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/KeccakTreeGpu/main.c (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/keccaktreegpu.py (88%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/keccaktreegpu_build_amd.py (94%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py (94%) rename src/{testsuite => hiptestsuite}/applications/keccaktreegpu/keccaktreegpu_parser_common.py (97%) rename src/{testsuite => hiptestsuite}/applications/mgbench/__init__.py (100%) rename src/{testsuite => hiptestsuite}/applications/mgbench/mgbench.py (94%) rename src/{testsuite => hiptestsuite}/applications/mgbench/mgbench_build_amd.py (95%) rename src/{testsuite => hiptestsuite}/applications/mgbench/mgbench_build_nvidia.py (96%) rename src/{testsuite => hiptestsuite}/applications/mgbench/mgbench_parser_common.py (97%) rename src/{testsuite => hiptestsuite}/common/__init__.py (100%) rename src/{testsuite => hiptestsuite}/common/hip_get_packages.py (81%) rename src/{testsuite => hiptestsuite}/common/hip_shell.py (100%) rename src/{testsuite => hiptestsuite}/config_processor.py (97%) rename src/{testsuite => hiptestsuite}/conformance/CMakePatch (100%) rename src/{testsuite => hiptestsuite}/conformance/__init__.py (100%) rename src/{testsuite => hiptestsuite}/conformance/conformance_test_classifier.py (96%) rename src/{testsuite => hiptestsuite}/conformance/hip_dtest.py (89%) rename src/{testsuite => hiptestsuite}/conformance/hip_dtest_build_amd.py (94%) rename src/{testsuite => hiptestsuite}/conformance/hip_dtest_build_common.py (95%) rename src/{testsuite => hiptestsuite}/conformance/hip_dtest_build_nvidia.py (95%) rename src/{testsuite => hiptestsuite}/list_tests.py (96%) rename src/{testsuite => hiptestsuite}/match_fun_args_call.py (100%) rename src/{testsuite => hiptestsuite}/stress/__init__.py (100%) rename src/{testsuite => hiptestsuite}/targets.py (98%) rename src/{testsuite => hiptestsuite}/test_classifier.py (97%) rename src/{testsuite => hiptestsuite}/test_selector.py (98%) rename src/{testsuite => hiptestsuite}/thirdparty/__init__.py (100%) rename src/{testsuite => hiptestsuite}/version.py (100%) diff --git a/cfg.py b/cfg.py index 62774ba..ab3c13c 100644 --- a/cfg.py +++ b/cfg.py @@ -20,7 +20,7 @@ version = "1.0.0" -user_password = "AH64_uh1" +user_password = None log_location = None # None/amd/nvidia @@ -130,10 +130,5 @@ "repo_url": "https://github.com/CEED/Laghos.git", "branch": None, "commit_id": "a7f6123d42847f6bdbdb614f5af876541f49cd16" - }, - "openmpi": { - "repo_url": "http://github.com/open-mpi/ompi.git openmpi", - "branch": "v4.0.x", - "commit_id": "4dc196d8c60aadeb34b0df9d226ab4c341004704" } } diff --git a/run.py b/run.py index 1484323..24afbe4 100644 --- a/run.py +++ b/run.py @@ -26,8 +26,8 @@ sys.path.append(os.path.join(os.path.dirname(__file__), "src")) -from testsuite.TestersExecutor import TestersExecutor -from testsuite.list_tests import list_tests +from hiptestsuite.TestersExecutor import TestersExecutor +from hiptestsuite.list_tests import list_tests import cfg diff --git a/src/testsuite/AMD.py b/src/hiptestsuite/AMD.py similarity index 100% rename from src/testsuite/AMD.py rename to src/hiptestsuite/AMD.py diff --git a/src/testsuite/Test.py b/src/hiptestsuite/Test.py similarity index 96% rename from src/testsuite/Test.py rename to src/hiptestsuite/Test.py index eed9beb..b3840fd 100644 --- a/src/testsuite/Test.py +++ b/src/hiptestsuite/Test.py @@ -19,17 +19,17 @@ # THE SOFTWARE. from typing import List, Union, Set, Dict -from testsuite.AMD import AMDObject -from testsuite.targets import Target +from hiptestsuite.AMD import AMDObject +from hiptestsuite.targets import Target from enum import Enum, auto -from testsuite.test_classifier import TestClassifier -from testsuite.config_processor import ConfigProcessor +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.config_processor import ConfigProcessor class Test(AMDObject): def __init__(self): # Cycle Import - from testsuite.TesterRepository import Tester + from hiptestsuite.TesterRepository import Tester AMDObject.__init__(self) self.test_name: Union[None, str] = None diff --git a/src/testsuite/TesterRepository.py b/src/hiptestsuite/TesterRepository.py similarity index 93% rename from src/testsuite/TesterRepository.py rename to src/hiptestsuite/TesterRepository.py index 2081302..5f2248e 100644 --- a/src/testsuite/TesterRepository.py +++ b/src/hiptestsuite/TesterRepository.py @@ -18,12 +18,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.Test import Test, TestData, GetTestsData, LogLocation, Quick -from testsuite.test_classifier import TestClassifier -from testsuite.AMD import AMDObject -from testsuite.config_processor import ConfigProcessor -from testsuite.match_fun_args_call import match_fun_args_call -import testsuite +from hiptestsuite.Test import Test, TestData, GetTestsData, LogLocation, Quick +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.AMD import AMDObject +from hiptestsuite.config_processor import ConfigProcessor +from hiptestsuite.match_fun_args_call import match_fun_args_call +import hiptestsuite from typing import List, Union import pkgutil @@ -54,7 +54,7 @@ def clean(self): class TesterRepository(AMDObject): def __init__(self): AMDObject.__init__(self) - self.getTestersFrom = [testsuite] + self.getTestersFrom = [hiptestsuite] self.testers: Union[None, List[Tester]] = None def getTesters(self) -> List[Tester]: diff --git a/src/testsuite/TestersExecutor.py b/src/hiptestsuite/TestersExecutor.py similarity index 98% rename from src/testsuite/TestersExecutor.py rename to src/hiptestsuite/TestersExecutor.py index a33f368..1b33003 100644 --- a/src/testsuite/TestersExecutor.py +++ b/src/hiptestsuite/TestersExecutor.py @@ -18,10 +18,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import TesterRepository, Tester, Test -from testsuite.test_selector import TestSelector -from testsuite.config_processor import ConfigProcessor -from testsuite.Test import TestResult +from hiptestsuite.TesterRepository import TesterRepository, Tester, Test +from hiptestsuite.test_selector import TestSelector +from hiptestsuite.config_processor import ConfigProcessor +from hiptestsuite.Test import TestResult import os import traceback diff --git a/src/testsuite/__init__.py b/src/hiptestsuite/__init__.py similarity index 100% rename from src/testsuite/__init__.py rename to src/hiptestsuite/__init__.py diff --git a/src/testsuite/applications/__init__.py b/src/hiptestsuite/applications/__init__.py similarity index 100% rename from src/testsuite/applications/__init__.py rename to src/hiptestsuite/applications/__init__.py diff --git a/src/testsuite/applications/application_test_classifier.py b/src/hiptestsuite/applications/application_test_classifier.py similarity index 96% rename from src/testsuite/applications/application_test_classifier.py rename to src/hiptestsuite/applications/application_test_classifier.py index 21c0ea4..757d30c 100644 --- a/src/testsuite/applications/application_test_classifier.py +++ b/src/hiptestsuite/applications/application_test_classifier.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.test_classifier import TestClassifier +from hiptestsuite.test_classifier import TestClassifier from typing import Union diff --git a/src/testsuite/applications/cuda_grep/__init__.py b/src/hiptestsuite/applications/cuda_grep/__init__.py similarity index 100% rename from src/testsuite/applications/cuda_grep/__init__.py rename to src/hiptestsuite/applications/cuda_grep/__init__.py diff --git a/src/testsuite/applications/cuda_grep/cuda_grep.py b/src/hiptestsuite/applications/cuda_grep/cuda_grep.py similarity index 91% rename from src/testsuite/applications/cuda_grep/cuda_grep.py rename to src/hiptestsuite/applications/cuda_grep/cuda_grep.py index f43cc27..2b19bbc 100644 --- a/src/testsuite/applications/cuda_grep/cuda_grep.py +++ b/src/hiptestsuite/applications/cuda_grep/cuda_grep.py @@ -18,14 +18,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester, Test, TestData -from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from testsuite.test_classifier import TestClassifier -from testsuite.applications.cuda_grep.cuda_grep_build_amd import BuildRunAmd -from testsuite.applications.cuda_grep.cuda_grep_build_nvidia import BuildRunNvidia -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.cuda_grep.cuda_grep_build_amd import BuildRunAmd +from hiptestsuite.applications.cuda_grep.cuda_grep_build_nvidia import BuildRunNvidia +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import execshellcmd import os import re @@ -35,7 +35,7 @@ def __init__(self, cwd, binary): self.cwdAbs = cwd self.binary = binary self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/applications/cuda_grep/") + "src/hiptestsuite/applications/cuda_grep/") self.app_root = os.path.join(self.app_path, "CUDA-grep/") self.thistestpath = self.app_root self.prepareobj = None diff --git a/src/testsuite/applications/cuda_grep/cuda_grep_build_amd.py b/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_amd.py similarity index 95% rename from src/testsuite/applications/cuda_grep/cuda_grep_build_amd.py rename to src/hiptestsuite/applications/cuda_grep/cuda_grep_build_amd.py index 528384e..7f2e879 100644 --- a/src/testsuite/applications/cuda_grep/cuda_grep_build_amd.py +++ b/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_amd.py @@ -19,8 +19,8 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.cuda_grep.cuda_grep_parser_common import CudaGrepParser +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.cuda_grep.cuda_grep_parser_common import CudaGrepParser class BuildRunAmd(): def __init__(self, thistestpath, logFile, binary): diff --git a/src/testsuite/applications/cuda_grep/cuda_grep_build_nvidia.py b/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_nvidia.py similarity index 95% rename from src/testsuite/applications/cuda_grep/cuda_grep_build_nvidia.py rename to src/hiptestsuite/applications/cuda_grep/cuda_grep_build_nvidia.py index 897dfd5..91ecb4c 100644 --- a/src/testsuite/applications/cuda_grep/cuda_grep_build_nvidia.py +++ b/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_nvidia.py @@ -19,8 +19,8 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.cuda_grep.cuda_grep_parser_common import CudaGrepParser +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.cuda_grep.cuda_grep_parser_common import CudaGrepParser class BuildRunNvidia(): def __init__(self, thistestpath, logFile, binary): diff --git a/src/testsuite/applications/cuda_grep/cuda_grep_parser_common.py b/src/hiptestsuite/applications/cuda_grep/cuda_grep_parser_common.py similarity index 96% rename from src/testsuite/applications/cuda_grep/cuda_grep_parser_common.py rename to src/hiptestsuite/applications/cuda_grep/cuda_grep_parser_common.py index 4ff0a75..ca183f8 100644 --- a/src/testsuite/applications/cuda_grep/cuda_grep_parser_common.py +++ b/src/hiptestsuite/applications/cuda_grep/cuda_grep_parser_common.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd class CudaGrepParser(): def __init__(self, results): diff --git a/src/testsuite/applications/cuda_memtest/__init__.py b/src/hiptestsuite/applications/cuda_memtest/__init__.py similarity index 100% rename from src/testsuite/applications/cuda_memtest/__init__.py rename to src/hiptestsuite/applications/cuda_memtest/__init__.py diff --git a/src/testsuite/applications/cuda_memtest/cuda_memtest.py b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest.py similarity index 97% rename from src/testsuite/applications/cuda_memtest/cuda_memtest.py rename to src/hiptestsuite/applications/cuda_memtest/cuda_memtest.py index 6d73f9b..1b5a556 100644 --- a/src/testsuite/applications/cuda_memtest/cuda_memtest.py +++ b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest.py @@ -18,14 +18,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester, Test, TestData -from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from testsuite.test_classifier import TestClassifier -from testsuite.applications.cuda_memtest.cuda_memtest_build_amd import BuildRunAmd -from testsuite.applications.cuda_memtest.cuda_memtest_build_nvidia import BuildRunNvidia -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.cuda_memtest.cuda_memtest_build_amd import BuildRunAmd +from hiptestsuite.applications.cuda_memtest.cuda_memtest_build_nvidia import BuildRunNvidia +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import execshellcmd import os import re @@ -35,7 +35,7 @@ def __init__(self, cwd, binary): self.cwdAbs = cwd self.binary = binary self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/applications/cuda_memtest/") + "src/hiptestsuite/applications/cuda_memtest/") self.app_root = os.path.join(self.app_path, "cuda_memtest/") self.thistestpath = self.app_root self.prepareobj = None diff --git a/src/testsuite/applications/cuda_memtest/cuda_memtest_build_amd.py b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_amd.py similarity index 95% rename from src/testsuite/applications/cuda_memtest/cuda_memtest_build_amd.py rename to src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_amd.py index 78d6e55..05bb539 100644 --- a/src/testsuite/applications/cuda_memtest/cuda_memtest_build_amd.py +++ b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_amd.py @@ -19,8 +19,8 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.cuda_memtest.cuda_memtest_parser_common import CudaMemtestParser +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.cuda_memtest.cuda_memtest_parser_common import CudaMemtestParser class BuildRunAmd(): def __init__(self, thistestpath, logFile, binary): diff --git a/src/testsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py similarity index 95% rename from src/testsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py rename to src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py index 408839e..0070976 100644 --- a/src/testsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py +++ b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py @@ -19,8 +19,8 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.cuda_memtest.cuda_memtest_parser_common import CudaMemtestParser +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.cuda_memtest.cuda_memtest_parser_common import CudaMemtestParser class BuildRunNvidia(): def __init__(self, thistestpath, logFile, binary): diff --git a/src/testsuite/applications/cuda_memtest/cuda_memtest_parser_common.py b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_parser_common.py similarity index 96% rename from src/testsuite/applications/cuda_memtest/cuda_memtest_parser_common.py rename to src/hiptestsuite/applications/cuda_memtest/cuda_memtest_parser_common.py index d3eea70..701cf38 100644 --- a/src/testsuite/applications/cuda_memtest/cuda_memtest_parser_common.py +++ b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_parser_common.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd import re class CudaMemtestParser(): diff --git a/src/testsuite/applications/cuda_memtest/cuda_memtest_patch b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_patch similarity index 100% rename from src/testsuite/applications/cuda_memtest/cuda_memtest_patch rename to src/hiptestsuite/applications/cuda_memtest/cuda_memtest_patch diff --git a/src/testsuite/applications/hip_examples/__init__.py b/src/hiptestsuite/applications/hip_examples/__init__.py similarity index 100% rename from src/testsuite/applications/hip_examples/__init__.py rename to src/hiptestsuite/applications/hip_examples/__init__.py diff --git a/src/testsuite/applications/hip_examples/hip_examples.py b/src/hiptestsuite/applications/hip_examples/hip_examples.py similarity index 99% rename from src/testsuite/applications/hip_examples/hip_examples.py rename to src/hiptestsuite/applications/hip_examples/hip_examples.py index 04a8351..38ff571 100644 --- a/src/testsuite/applications/hip_examples/hip_examples.py +++ b/src/hiptestsuite/applications/hip_examples/hip_examples.py @@ -18,14 +18,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester, Test, TestData -from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from testsuite.test_classifier import TestClassifier -from testsuite.applications.hip_examples.hip_examples_build_amd import BuildRunAmd -from testsuite.applications.hip_examples.hip_examples_build_nvidia import BuildRunNvidia -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import * +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.hip_examples.hip_examples_build_amd import BuildRunAmd +from hiptestsuite.applications.hip_examples.hip_examples_build_nvidia import BuildRunNvidia +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import * import os @@ -34,7 +34,7 @@ class PrepareTest(): def __init__(self, path, cwd): self.cwdAbs = cwd self.appPath = os.path.join(self.cwdAbs,\ - "src/testsuite/applications/hip_examples/") + "src/hiptestsuite/applications/hip_examples/") self.examplepath = os.path.join(self.appPath, "HIP-Examples/") self.thistestpath = os.path.join(self.examplepath, path) self.prepareobj = None diff --git a/src/testsuite/applications/hip_examples/hip_examples_build_amd.py b/src/hiptestsuite/applications/hip_examples/hip_examples_build_amd.py similarity index 90% rename from src/testsuite/applications/hip_examples/hip_examples_build_amd.py rename to src/hiptestsuite/applications/hip_examples/hip_examples_build_amd.py index 4ad70ec..3fe9155 100644 --- a/src/testsuite/applications/hip_examples/hip_examples_build_amd.py +++ b/src/hiptestsuite/applications/hip_examples/hip_examples_build_amd.py @@ -19,9 +19,9 @@ # THE SOFTWARE. import os -from testsuite.applications.hip_examples.hip_examples_parser import Hip_examples_parser -from testsuite.applications.hip_examples.hip_examples_build_common import BuildRunCommon -from testsuite.common.hip_shell import * +from hiptestsuite.applications.hip_examples.hip_examples_parser import Hip_examples_parser +from hiptestsuite.applications.hip_examples.hip_examples_build_common import BuildRunCommon +from hiptestsuite.common.hip_shell import * class BuildRunAmd(BuildRunCommon): def __init__(self, path): diff --git a/src/testsuite/applications/hip_examples/hip_examples_build_common.py b/src/hiptestsuite/applications/hip_examples/hip_examples_build_common.py similarity index 99% rename from src/testsuite/applications/hip_examples/hip_examples_build_common.py rename to src/hiptestsuite/applications/hip_examples/hip_examples_build_common.py index 5952beb..6292a28 100644 --- a/src/testsuite/applications/hip_examples/hip_examples_build_common.py +++ b/src/hiptestsuite/applications/hip_examples/hip_examples_build_common.py @@ -21,8 +21,8 @@ import os import tempfile -from testsuite.applications.hip_examples.hip_examples_parser import Hip_examples_parser -from testsuite.common.hip_shell import * +from hiptestsuite.applications.hip_examples.hip_examples_parser import Hip_examples_parser +from hiptestsuite.common.hip_shell import * class BuildRunCommon(): ''' diff --git a/src/testsuite/applications/hip_examples/hip_examples_build_nvidia.py b/src/hiptestsuite/applications/hip_examples/hip_examples_build_nvidia.py similarity index 88% rename from src/testsuite/applications/hip_examples/hip_examples_build_nvidia.py rename to src/hiptestsuite/applications/hip_examples/hip_examples_build_nvidia.py index fa15172..a1f85c1 100644 --- a/src/testsuite/applications/hip_examples/hip_examples_build_nvidia.py +++ b/src/hiptestsuite/applications/hip_examples/hip_examples_build_nvidia.py @@ -20,13 +20,13 @@ import os -from testsuite.applications.hip_examples.hip_examples_parser import Hip_examples_parser -from testsuite.applications.hip_examples.hip_examples_build_common import BuildRunCommon -from testsuite.common.hip_shell import * +from hiptestsuite.applications.hip_examples.hip_examples_parser import Hip_examples_parser +from hiptestsuite.applications.hip_examples.hip_examples_build_common import BuildRunCommon +from hiptestsuite.common.hip_shell import * class BuildRunNvidia(BuildRunCommon): def __init__(self, path): - self.hippath = os.path.join(os.getcwd(),"src/testsuite/conformance/HIP/") + self.hippath = os.path.join(os.getcwd(),"src/hiptestsuite/conformance/HIP/") BuildRunCommon.__init__(self, path) def getenvironmentvariables(self): diff --git a/src/testsuite/applications/hip_examples/hip_examples_parser.py b/src/hiptestsuite/applications/hip_examples/hip_examples_parser.py similarity index 100% rename from src/testsuite/applications/hip_examples/hip_examples_parser.py rename to src/hiptestsuite/applications/hip_examples/hip_examples_parser.py diff --git a/src/testsuite/applications/hip_samples/Samples_Patch_4.2.x b/src/hiptestsuite/applications/hip_samples/Samples_Patch_4.2.x similarity index 100% rename from src/testsuite/applications/hip_samples/Samples_Patch_4.2.x rename to src/hiptestsuite/applications/hip_samples/Samples_Patch_4.2.x diff --git a/src/testsuite/applications/hip_samples/__init__.py b/src/hiptestsuite/applications/hip_samples/__init__.py similarity index 100% rename from src/testsuite/applications/hip_samples/__init__.py rename to src/hiptestsuite/applications/hip_samples/__init__.py diff --git a/src/testsuite/applications/hip_samples/hip_samples.py b/src/hiptestsuite/applications/hip_samples/hip_samples.py similarity index 98% rename from src/testsuite/applications/hip_samples/hip_samples.py rename to src/hiptestsuite/applications/hip_samples/hip_samples.py index 8325b0e..e24042c 100644 --- a/src/testsuite/applications/hip_samples/hip_samples.py +++ b/src/hiptestsuite/applications/hip_samples/hip_samples.py @@ -18,14 +18,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester, Test, TestData -from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from testsuite.test_classifier import TestClassifier -from testsuite.applications.hip_samples.hip_samples_build_amd import BuildRunAmd -from testsuite.applications.hip_samples.hip_samples_build_nvidia import BuildRunNvidia -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.hip_samples.hip_samples_build_amd import BuildRunAmd +from hiptestsuite.applications.hip_samples.hip_samples_build_nvidia import BuildRunNvidia +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import execshellcmd import os import re @@ -33,7 +33,7 @@ class PrepareTest(): def __init__(self, path, binary, cwd): self.cwdAbs = cwd - self.conformancePath = os.path.join(self.cwdAbs, "src/testsuite/conformance/") + self.conformancePath = os.path.join(self.cwdAbs, "src/hiptestsuite/conformance/") self.hippath = os.path.join(self.conformancePath, "HIP/") self.thistestpath = os.path.join(self.hippath, path) self.binary = binary @@ -61,7 +61,7 @@ def downloadtest(self, logFile): self.hipcommitId, "HIP") return ret - def buildtest(self, logFile, platform): + def buildtest(self, logFile, platform, target=None): isBinaryPresent = True if platform == HIP_PLATFORM.nvidia: self.prepareobj = BuildRunNvidia(self.thistestpath, logFile) @@ -70,7 +70,7 @@ def buildtest(self, logFile, platform): else: print("Invalid Platform") return False - self.prepareobj.buildtest() + self.prepareobj.buildtest(target) if not os.path.isfile(\ os.path.join(self.thistestpath, self.binary)): @@ -1275,7 +1275,7 @@ def test(self, test_data: HIPTestData): if not res: test_data.test_result = TestResult.FAIL return - res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, "hipInfo") if not res: test_data.test_result = TestResult.FAIL return diff --git a/src/testsuite/applications/hip_samples/hip_samples_build_amd.py b/src/hiptestsuite/applications/hip_samples/hip_samples_build_amd.py similarity index 87% rename from src/testsuite/applications/hip_samples/hip_samples_build_amd.py rename to src/hiptestsuite/applications/hip_samples/hip_samples_build_amd.py index c72fc71..badcd88 100644 --- a/src/testsuite/applications/hip_samples/hip_samples_build_amd.py +++ b/src/hiptestsuite/applications/hip_samples/hip_samples_build_amd.py @@ -19,19 +19,19 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.hip_samples.hip_samples_build_common import BuildRunCommon +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.hip_samples.hip_samples_build_common import BuildRunCommon class BuildRunAmd(BuildRunCommon): def __init__(self, path, logfile): BuildRunCommon.__init__(self, path, logfile) - def buildtest(self): + def buildtest(self, target): # In this function put the build steps for test cases # which differ across platforms (amd/nvidia/intel) else # invoke BuildRunCommon.buildtest if not os.path.exists("/opt/rocm"): print("ROCm not installed. Exiting!") return False - ret = BuildRunCommon.buildtest(self) + ret = BuildRunCommon.buildtest(self, target) return ret diff --git a/src/testsuite/applications/hip_samples/hip_samples_build_common.py b/src/hiptestsuite/applications/hip_samples/hip_samples_build_common.py similarity index 89% rename from src/testsuite/applications/hip_samples/hip_samples_build_common.py rename to src/hiptestsuite/applications/hip_samples/hip_samples_build_common.py index deb7855..cc3f668 100644 --- a/src/testsuite/applications/hip_samples/hip_samples_build_common.py +++ b/src/hiptestsuite/applications/hip_samples/hip_samples_build_common.py @@ -19,7 +19,7 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd class BuildRunCommon(): ''' @@ -30,10 +30,13 @@ def __init__(self, path, logfile): self.thistestpath = path self.logfile = logfile - def buildtest(self, env = None): + def buildtest(self, target, env = None): cmdcd = "cd " + self.thistestpath + ";" cmd_clean = "make clean;" - cmd_build = "make" + if target != None: + cmd_build = "make " + target + ";" + else: + cmd_build = "make;" cmdexc = cmdcd + cmd_clean + cmd_build execshellcmd(cmdexc, self.logfile, env) diff --git a/src/testsuite/applications/hip_samples/hip_samples_build_nvidia.py b/src/hiptestsuite/applications/hip_samples/hip_samples_build_nvidia.py similarity index 87% rename from src/testsuite/applications/hip_samples/hip_samples_build_nvidia.py rename to src/hiptestsuite/applications/hip_samples/hip_samples_build_nvidia.py index d456b69..62a97af 100644 --- a/src/testsuite/applications/hip_samples/hip_samples_build_nvidia.py +++ b/src/hiptestsuite/applications/hip_samples/hip_samples_build_nvidia.py @@ -19,12 +19,12 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.hip_samples.hip_samples_build_common import BuildRunCommon +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.hip_samples.hip_samples_build_common import BuildRunCommon class BuildRunNvidia(BuildRunCommon): def __init__(self, path, logfile): - self.hippath = os.path.join(os.getcwd(), "src/testsuite/conformance/HIP/") + self.hippath = os.path.join(os.getcwd(), "src/hiptestsuite/conformance/HIP/") BuildRunCommon.__init__(self, path, logfile) def getenvironmentvariables(self): @@ -42,7 +42,7 @@ def applypatch(self): # To be deleted cmd += "patch -p0 < ../../applications/hip_samples/Samples_Patch_4.2.x; touch patched;" execshellcmd(cmd, self.logfile, None) - def buildtest(self): + def buildtest(self, target): # In this function put the build steps for test cases # which differ across platforms (testsuite/nvidia/intel) else # invoke BuildRunCommon.buildtest @@ -53,5 +53,5 @@ def buildtest(self): # are available in HIP public repository. envtoset = self.getenvironmentvariables() self.applypatch() - ret = BuildRunCommon.buildtest(self, envtoset) + ret = BuildRunCommon.buildtest(self, target, envtoset) return ret diff --git a/src/testsuite/applications/hpc_apps/__init__.py b/src/hiptestsuite/applications/hpc_apps/__init__.py similarity index 100% rename from src/testsuite/applications/hpc_apps/__init__.py rename to src/hiptestsuite/applications/hpc_apps/__init__.py diff --git a/src/testsuite/applications/hpc_apps/gridtools/__init__.py b/src/hiptestsuite/applications/hpc_apps/gridtools/__init__.py similarity index 100% rename from src/testsuite/applications/hpc_apps/gridtools/__init__.py rename to src/hiptestsuite/applications/hpc_apps/gridtools/__init__.py diff --git a/src/testsuite/applications/hpc_apps/gridtools/gridtools.py b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.py similarity index 93% rename from src/testsuite/applications/hpc_apps/gridtools/gridtools.py rename to src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.py index c4bf490..f5c128b 100644 --- a/src/testsuite/applications/hpc_apps/gridtools/gridtools.py +++ b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.py @@ -18,13 +18,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester, Test, TestData -from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from testsuite.test_classifier import TestClassifier -from testsuite.applications.hpc_apps.gridtools.gridtools_build_amd import BuildRunAmd -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.hpc_apps.gridtools.gridtools_build_amd import BuildRunAmd +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import execshellcmd import os import re @@ -33,7 +33,7 @@ class PrepareTest(): def __init__(self, cwd): self.cwdAbs = cwd self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/applications/hpc_apps/gridtools") + "src/hiptestsuite/applications/hpc_apps/gridtools") self.thistestpath = self.app_path self.prepareobj = None self.gridtoolsrepo = "" # Default @@ -140,8 +140,8 @@ def test(self, test_data: HIPTestData): # Check if Boost package exists if not os.path.isfile(\ os.path.join(self.thistestpath, "boost_1_72_0.tar.bz2")): - print("Boost Package boost_1_72_0.tar.bz2 not available under src/testsuite/applications/hpc_apps/gridtools/.") - print("Please download and copy Boost package in src/testsuite/applications/hpc_apps/gridtools folder.") + print("Boost Package boost_1_72_0.tar.bz2 not available under src/hiptestsuite/applications/hpc_apps/gridtools/.") + print("Please download and copy Boost package in src/hiptestsuite/applications/hpc_apps/gridtools folder.") test_data.test_result = TestResult.ERROR return # Create GT_TREE_DIR @@ -203,8 +203,8 @@ def test(self, test_data: HIPTestData): # Check if Boost package exists if not os.path.isfile(\ os.path.join(self.thistestpath, "boost_1_72_0.tar.bz2")): - print("Boost Package boost_1_72_0.tar.bz2 not available under src/testsuite/applications/hpc_apps/gridtools/.") - print("Please download and copy Boost package in src/testsuite/applications/hpc_apps/gridtools folder.") + print("Boost Package boost_1_72_0.tar.bz2 not available under src/hiptestsuite/applications/hpc_apps/gridtools/.") + print("Please download and copy Boost package in src/hiptestsuite/applications/hpc_apps/gridtools folder.") test_data.test_result = TestResult.ERROR return diff --git a/src/testsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py similarity index 96% rename from src/testsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py rename to src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py index 11b4b27..ce80af5 100644 --- a/src/testsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py +++ b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py @@ -20,8 +20,8 @@ import os import tempfile -from testsuite.common.hip_shell import * -from testsuite.applications.hpc_apps.gridtools.gridtools_parser_common import GridtoolsParser +from hiptestsuite.common.hip_shell import * +from hiptestsuite.applications.hpc_apps.gridtools.gridtools_parser_common import GridtoolsParser class BuildRunAmd(): def __init__(self, thistestpath, logFile): @@ -37,7 +37,7 @@ def setenv(self, gpu_arch): env += "export ROCMHOME=/opt/rocm;" env += "export PATH=$ROCMHOME/bin:$ROCMHOME/llvm/bin:$ROCMHOME/hip/bin:$ROCMHOME/opencl/bin:$ROCMHOME/rocprofiler/bin:$PATH;" env += "export LD_LIBRARY_PATH=/$ROCMHOME/lib:$ROCMHOME/llvm/lib:$LD_LIBRARY_PATH;" - env += "export GT_ALL_DIR=$PWD/src/testsuite/applications/hpc_apps/gridtools;" + env += "export GT_ALL_DIR=$PWD/src/hiptestsuite/applications/hpc_apps/gridtools;" env += "export GT_TREE_DIR=$GT_ALL_DIR/GridTools;" env += "export BOOST_TREE_DIR=$GT_TREE_DIR/boost_1_72_0;" env += "export BOOST_INSTALL_DIR=$GT_ALL_DIR/boost_1_72_0;" diff --git a/src/testsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py similarity index 97% rename from src/testsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py rename to src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py index 099003e..356fe25 100644 --- a/src/testsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py +++ b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd import re class GridtoolsParser(): diff --git a/src/testsuite/applications/hpc_apps/gridtools/gtbench.patch b/src/hiptestsuite/applications/hpc_apps/gridtools/gtbench.patch similarity index 100% rename from src/testsuite/applications/hpc_apps/gridtools/gtbench.patch rename to src/hiptestsuite/applications/hpc_apps/gridtools/gtbench.patch diff --git a/src/testsuite/applications/hpc_apps/kokkos/__init__.py b/src/hiptestsuite/applications/hpc_apps/kokkos/__init__.py similarity index 100% rename from src/testsuite/applications/hpc_apps/kokkos/__init__.py rename to src/hiptestsuite/applications/hpc_apps/kokkos/__init__.py diff --git a/src/testsuite/applications/hpc_apps/kokkos/kokkos.py b/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos.py similarity index 94% rename from src/testsuite/applications/hpc_apps/kokkos/kokkos.py rename to src/hiptestsuite/applications/hpc_apps/kokkos/kokkos.py index d8720e8..b8fe373 100644 --- a/src/testsuite/applications/hpc_apps/kokkos/kokkos.py +++ b/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos.py @@ -18,13 +18,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester, Test, TestData -from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from testsuite.test_classifier import TestClassifier -from testsuite.applications.hpc_apps.kokkos.kokkos_build_amd import BuildRunAmd -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.hpc_apps.kokkos.kokkos_build_amd import BuildRunAmd +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import execshellcmd import os import re @@ -33,7 +33,7 @@ class PrepareTest(): def __init__(self, cwd): self.cwdAbs = cwd self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/applications/hpc_apps/kokkos/") + "src/hiptestsuite/applications/hpc_apps/kokkos/") self.app_root = os.path.join(self.app_path, "kokkos/") self.thistestpath = self.app_root self.prepareobj = None diff --git a/src/testsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py b/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py similarity index 93% rename from src/testsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py rename to src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py index 8bd81c4..95e1a35 100644 --- a/src/testsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py +++ b/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py @@ -21,8 +21,8 @@ import os import re import tempfile -from testsuite.common.hip_shell import * -from testsuite.applications.hpc_apps.kokkos.kokkos_parser_common import KokkosParser +from hiptestsuite.common.hip_shell import * +from hiptestsuite.applications.hpc_apps.kokkos.kokkos_parser_common import KokkosParser class BuildRunAmd(): def __init__(self, thistestpath, logFile): @@ -56,7 +56,7 @@ def buildtest(self): return True def runtest(self, testnum): - print("Running Quicksilver Test..") + print("Running Kokkos Test..") cmdcd = "cd " + self.thistestpath + "/build;" if testnum == 0: cmdrun = "ctest -R;" @@ -66,7 +66,7 @@ def runtest(self, testnum): self.runlog = execshellcmd(cmdexc, self.logFile, None) def clean(self): - print("Cleaning Quicksilver..") + print("Cleaning Kokkos..") def parse_result(self, testnum): return KokkosParser(self.runlog).parse(testnum) diff --git a/src/testsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py b/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py similarity index 96% rename from src/testsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py rename to src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py index ec10152..919e0bf 100644 --- a/src/testsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py +++ b/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd import re class KokkosParser(): diff --git a/src/testsuite/applications/hpc_apps/laghos/__init__.py b/src/hiptestsuite/applications/hpc_apps/laghos/__init__.py similarity index 100% rename from src/testsuite/applications/hpc_apps/laghos/__init__.py rename to src/hiptestsuite/applications/hpc_apps/laghos/__init__.py diff --git a/src/testsuite/applications/hpc_apps/laghos/laghos.py b/src/hiptestsuite/applications/hpc_apps/laghos/laghos.py similarity index 84% rename from src/testsuite/applications/hpc_apps/laghos/laghos.py rename to src/hiptestsuite/applications/hpc_apps/laghos/laghos.py index a092c2d..8af0ee7 100644 --- a/src/testsuite/applications/hpc_apps/laghos/laghos.py +++ b/src/hiptestsuite/applications/hpc_apps/laghos/laghos.py @@ -18,13 +18,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester, Test, TestData -from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from testsuite.test_classifier import TestClassifier -from testsuite.applications.hpc_apps.laghos.laghos_build_amd import BuildRunAmd -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.hpc_apps.laghos.laghos_build_amd import BuildRunAmd +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import execshellcmd import os import re @@ -33,7 +33,7 @@ class PrepareTest(): def __init__(self, cwd): self.cwdAbs = cwd self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/applications/hpc_apps/laghos/") + "src/hiptestsuite/applications/hpc_apps/laghos/") self.thistestpath = self.app_path self.prepareobj = None @@ -41,10 +41,6 @@ def __init__(self, cwd): self.laghos_branch = "" self.laghos_commitId = "" - self.openmpi_repo = "" # Default - self.openmpi_branch = "" - self.openmpi_commitId = "" - self.mfem_repo = "" # Default self.mfem_branch = "" self.mfem_commitId = "" @@ -68,16 +64,6 @@ def set_laghos_repoinfo(self, test_data: HIPTestData): self.laghos_branch = test_data.repos["Laghos"].branch if test_data.repos["Laghos"].commit_id != None: self.laghos_commitId = test_data.repos["Laghos"].commit_id - - if test_data.repos["openmpi"].repo_url != None: - self.openmpi_repo = test_data.repos["openmpi"].repo_url - else: - validrepconfig &= False - if test_data.repos["openmpi"].branch != None: - self.openmpi_branch = test_data.repos["openmpi"].branch - if test_data.repos["openmpi"].commit_id != None: - self.openmpi_commitId = test_data.repos["openmpi"].commit_id - return validrepconfig def downloadtest(self, logFile, test_data: HIPTestData): @@ -86,17 +72,15 @@ def downloadtest(self, logFile, test_data: HIPTestData): self.mfem_branch, self.mfem_commitId, "mfem") ret = ret & HipPackages().pull_repo(logFile, self.laghos_repo,\ self.laghos_branch, self.laghos_commitId, "Laghos") - ret = ret & HipPackages().pull_repo(logFile, self.openmpi_repo,\ - self.openmpi_branch, self.openmpi_commitId, "openmpi") return ret - def buildtest(self, logFile, test_data: HIPTestData, platform): + def buildtest(self, logFile, platform): if platform == HIP_PLATFORM.amd: self.prepareobj = BuildRunAmd(self.thistestpath, logFile) else: print("Invalid Platform") return False - buildstatus = self.prepareobj.buildtest(test_data) + buildstatus = self.prepareobj.buildtest() if buildstatus == False: return False @@ -166,7 +150,7 @@ def test(self, test_data: HIPTestData): if not res: test_data.test_result = TestResult.FAIL return - res = self.buildtest(testLogger, test_data, test_data.HIP_PLATFORM) + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) if not res: test_data.test_result = TestResult.FAIL return @@ -214,7 +198,7 @@ def test(self, test_data: HIPTestData): if not res: test_data.test_result = TestResult.FAIL return - res = self.buildtest(testLogger, test_data, test_data.HIP_PLATFORM) + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) if not res: test_data.test_result = TestResult.FAIL return diff --git a/src/testsuite/applications/hpc_apps/laghos/laghos_build_amd.py b/src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_amd.py similarity index 64% rename from src/testsuite/applications/hpc_apps/laghos/laghos_build_amd.py rename to src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_amd.py index d90993f..2e81a5e 100644 --- a/src/testsuite/applications/hpc_apps/laghos/laghos_build_amd.py +++ b/src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_amd.py @@ -21,9 +21,8 @@ import os import re import tempfile -from testsuite.Test import HIPTestData -from testsuite.common.hip_shell import * -from testsuite.applications.hpc_apps.laghos.laghos_parser_common import LaghosParser +from hiptestsuite.common.hip_shell import * +from hiptestsuite.applications.hpc_apps.laghos.laghos_parser_common import LaghosParser class BuildRunAmd(): def __init__(self, thistestpath, logFile): @@ -38,79 +37,6 @@ def set_env(self): cmd += "export PATH=${MPI_PATH}/bin:$PATH;" return cmd - def configure_build_openmpi(self, test_data: HIPTestData): - # Check if OpenMPI is already installed. Then return. - if os.path.exists("/usr/local/openmpi/bin") and os.path.exists("/usr/local/openmpi/etc")\ - and os.path.exists("/usr/local/openmpi/lib") and os.path.exists("/usr/local/openmpi/include")\ - and os.path.exists("/usr/local/openmpi/share"): - print("Openmpi already installed. Returning ..") - return True - openmpi_rootdir = os.path.join(self.thistestpath, "openmpi") - cmd = self.set_env() - cmd += "cd " + openmpi_rootdir + ";" - cmd += "./autogen.pl;" - print("Open MPI autogen in progress ..") - runlogdump = tempfile.TemporaryFile("w+") - execshellcmd_largedump(cmd, self.logFile, runlogdump, None) - autogensuccess = False - for line in runlogdump: - if re.search("Open\s+MPI\s+autogen:\s+completed\s+successfully.\s+w00t!", line): - autogensuccess = True - runlogdump.close() - if not autogensuccess: - print("Open MPI autogen in failed! Check if prerequisites (autoconf, automake, libtool and flex) are installed.") - return False - cmd = self.set_env() - cmd += "cd " + openmpi_rootdir + ";" - cmd += "./configure --enable-mpi-cxx --enable-mpi1-compatibility --prefix=/usr/local/openmpi;" - cmd += "make -j;" - cmd += "echo " + test_data.user_password + " | sudo -S make -j install;" - print("Open MPI Configuration+Build in progress ..") - runlogdump = tempfile.TemporaryFile("w+") - execshellcmd_largedump(cmd, self.logFile, runlogdump, None) - runlogdump.close() - # Check if Openmpi is installed - count = 0 - lib32_present = False - lib64_present = False - if os.path.exists("/usr/local/openmpi"): - if os.path.exists("/usr/local/openmpi/bin"): - count = count + 1 - if os.path.exists("/usr/local/openmpi/etc"): - count = count + 1 - if os.path.exists("/usr/local/openmpi/lib"): - count = count + 1 - else: - if os.path.exists("/usr/local/openmpi/lib32"): - count = count + 1 - lib32_present = True - elif os.path.exists("/usr/local/openmpi/lib64"): - count = count + 1 - lib64_present = True - if os.path.exists("/usr/local/openmpi/include"): - count = count + 1 - if os.path.exists("/usr/local/openmpi/share"): - count = count + 1 - else: - print("Openmpi not installed. Build failed!") - return False - if count != 5: - print("Openmpi not installed. Build failed!") - return False - # This step might be necessary on SLES machines - if lib32_present: - print("lib softlink to lib32 needs to be created") - cmd = "cd /usr/local/openmpi/;" - cmd += "echo " + test_data.user_password + "| sudo -S ln -s lib32 lib;" - execshellcmd(cmd, self.logFile, None) - elif lib64_present: - print("lib softlink to lib64 needs to be created") - cmd = "cd /usr/local/openmpi/;" - cmd += "echo " + test_data.user_password + "| sudo -S ln -s lib64 lib;" - execshellcmd(cmd, self.logFile, None) - - return True - def configure_build_hypre(self): if os.path.exists(os.path.join(self.thistestpath, "hypre/src/lib/libHYPRE.a")): print("Hypre already built") @@ -150,7 +76,7 @@ def configure_build_metis(self): return False return True - def configure_build_mfem(self, test_data: HIPTestData): + def configure_build_mfem(self): if os.path.exists(os.path.join(self.thistestpath, "mfem/libmfem.a")): print("Mfem already built") return True @@ -169,7 +95,7 @@ def configure_build_mfem(self, test_data: HIPTestData): return False return True - def configure_build_laghos(self, test_data: HIPTestData): + def configure_build_laghos(self): if os.path.exists(os.path.join(self.thistestpath, "Laghos/laghos")): print("Laghos already built") return True @@ -185,23 +111,28 @@ def configure_build_laghos(self, test_data: HIPTestData): return False return True - def buildtest(self, test_data: HIPTestData): + def buildtest(self): # In this function put the build steps for test cases # which differ across platforms (amd/nvidia/intel) - # print("Password is = ", test_data.user_password) - if not self.configure_build_openmpi(test_data): - print("Openmpi configuration and build failed ..") + # Check if OpenMPI is already installed. Else return. + if os.path.exists("/usr/local/openmpi/bin") and os.path.exists("/usr/local/openmpi/etc")\ + and os.path.exists("/usr/local/openmpi/lib") and os.path.exists("/usr/local/openmpi/include")\ + and os.path.exists("/usr/local/openmpi/share"): + print("Openmpi installed.") + else: + print("Openmpi not installed. Exiting test!") return False + if not self.configure_build_hypre(): print("Hypre configuration and build failed ..") return False if not self.configure_build_metis(): print("Metis configuration and build failed ..") return False - if not self.configure_build_mfem(test_data): + if not self.configure_build_mfem(): print("Mfem configuration and build failed ..") return False - if not self.configure_build_laghos(test_data): + if not self.configure_build_laghos(): print("Laghos configuration and build failed ..") return False diff --git a/src/testsuite/applications/hpc_apps/laghos/laghos_parser_common.py b/src/hiptestsuite/applications/hpc_apps/laghos/laghos_parser_common.py similarity index 97% rename from src/testsuite/applications/hpc_apps/laghos/laghos_parser_common.py rename to src/hiptestsuite/applications/hpc_apps/laghos/laghos_parser_common.py index 0d4b9a2..35c3d9a 100644 --- a/src/testsuite/applications/hpc_apps/laghos/laghos_parser_common.py +++ b/src/hiptestsuite/applications/hpc_apps/laghos/laghos_parser_common.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd import re class LaghosParser(): diff --git a/src/testsuite/applications/hpc_apps/quicksilver/__init__.py b/src/hiptestsuite/applications/hpc_apps/quicksilver/__init__.py similarity index 100% rename from src/testsuite/applications/hpc_apps/quicksilver/__init__.py rename to src/hiptestsuite/applications/hpc_apps/quicksilver/__init__.py diff --git a/src/testsuite/applications/hpc_apps/quicksilver/quicksilver.py b/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver.py similarity index 92% rename from src/testsuite/applications/hpc_apps/quicksilver/quicksilver.py rename to src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver.py index 1080094..147e52b 100644 --- a/src/testsuite/applications/hpc_apps/quicksilver/quicksilver.py +++ b/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver.py @@ -18,13 +18,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester, Test, TestData -from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from testsuite.test_classifier import TestClassifier -from testsuite.applications.hpc_apps.quicksilver.quicksilver_build_amd import BuildRunAmd -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.hpc_apps.quicksilver.quicksilver_build_amd import BuildRunAmd +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import execshellcmd import os import re @@ -34,7 +34,7 @@ def __init__(self, cwd, binary): self.cwdAbs = cwd self.binary = binary self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/applications/hpc_apps/quicksilver/") + "src/hiptestsuite/applications/hpc_apps/quicksilver/") self.app_root = os.path.join(self.app_path, "Quicksilver/") self.thistestpath = self.app_root self.prepareobj = None diff --git a/src/testsuite/applications/hpc_apps/quicksilver/quicksilver_build_amd.py b/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_build_amd.py similarity index 94% rename from src/testsuite/applications/hpc_apps/quicksilver/quicksilver_build_amd.py rename to src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_build_amd.py index 25acea0..8afc662 100644 --- a/src/testsuite/applications/hpc_apps/quicksilver/quicksilver_build_amd.py +++ b/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_build_amd.py @@ -19,8 +19,8 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.hpc_apps.quicksilver.quicksilver_parser_common import QuicksilverParser +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.hpc_apps.quicksilver.quicksilver_parser_common import QuicksilverParser class BuildRunAmd(): def __init__(self, thistestpath, logFile): diff --git a/src/testsuite/applications/hpc_apps/quicksilver/quicksilver_diff_patch b/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_diff_patch similarity index 100% rename from src/testsuite/applications/hpc_apps/quicksilver/quicksilver_diff_patch rename to src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_diff_patch diff --git a/src/testsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py b/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py similarity index 97% rename from src/testsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py rename to src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py index 5b3e06b..b5c9289 100644 --- a/src/testsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py +++ b/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd import re class QuicksilverParser(): diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.c b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.c similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.c rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.c diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.h b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.h similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.h rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakF.h diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTree.h b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTree.h similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTree.h rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTree.h diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.c b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.c similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.c rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.c diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.h b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.h similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.h rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeCPU.h diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.cu b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.cu similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.cu rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.cu diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.h b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.h similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.h rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTreeGPU.h diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTypes.h b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTypes.h similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTypes.h rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/KeccakTypes.h diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Makefile b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/Makefile similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Makefile rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/Makefile diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/README.txt b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/README.txt similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/README.txt rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/README.txt diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.c b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.c similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.c rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.c diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.h b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.h similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.h rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/Test.h diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/BuildLog.htm b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/BuildLog.htm similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/BuildLog.htm rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/BuildLog.htm diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakF.obj b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakF.obj similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakF.obj rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakF.obj diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeCPU.obj b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeCPU.obj similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeCPU.obj rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeCPU.obj diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeGPU.cu.obj b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeGPU.cu.obj similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeGPU.cu.obj rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/KeccakTreeGPU.cu.obj diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/Test.obj b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/Test.obj similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/Test.obj rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/Test.obj diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.ex.renameit b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.ex.renameit similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.ex.renameit rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.ex.renameit diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.exe.intermediate.manifest b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.exe.intermediate.manifest similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.exe.intermediate.manifest rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.exe.intermediate.manifest diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.pdb b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.pdb similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.pdb rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/VCKeccakTree.pdb diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/cudart32_30_14.dll b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/cudart32_30_14.dll similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/cudart32_30_14.dll rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/cudart32_30_14.dll diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/main.obj b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/main.obj similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/main.obj rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/main.obj diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/mt.dep b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/mt.dep similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/mt.dep rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/mt.dep diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.idb b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.idb similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.idb rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.idb diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.pdb b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.pdb similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.pdb rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/Release/vc90.pdb diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.ncb b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.ncb similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.ncb rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.ncb diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.sln b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.sln similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.sln rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.sln diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.suo b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.suo similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.suo rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.suo diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj.DELLM24.guigui.user b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj.DELLM24.guigui.user similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj.DELLM24.guigui.user rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/VCKeccakTree.vcproj.DELLM24.guigui.user diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/vc90.pdb b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/vc90.pdb similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/vc90.pdb rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/VCKeccakTree/vc90.pdb diff --git a/src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/main.c b/src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/main.c similarity index 100% rename from src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/main.c rename to src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/main.c diff --git a/src/testsuite/applications/keccaktreegpu/__init__.py b/src/hiptestsuite/applications/keccaktreegpu/__init__.py similarity index 100% rename from src/testsuite/applications/keccaktreegpu/__init__.py rename to src/hiptestsuite/applications/keccaktreegpu/__init__.py diff --git a/src/testsuite/applications/keccaktreegpu/keccaktreegpu.py b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu.py similarity index 88% rename from src/testsuite/applications/keccaktreegpu/keccaktreegpu.py rename to src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu.py index 919a3b0..66f00ca 100644 --- a/src/testsuite/applications/keccaktreegpu/keccaktreegpu.py +++ b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu.py @@ -18,14 +18,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester, Test, TestData -from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from testsuite.test_classifier import TestClassifier -from testsuite.applications.keccaktreegpu.keccaktreegpu_build_amd import BuildRunAmd -from testsuite.applications.keccaktreegpu.keccaktreegpu_build_nvidia import BuildRunNvidia -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.keccaktreegpu.keccaktreegpu_build_amd import BuildRunAmd +from hiptestsuite.applications.keccaktreegpu.keccaktreegpu_build_nvidia import BuildRunNvidia +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import execshellcmd import os import re @@ -35,7 +35,7 @@ def __init__(self, cwd, binary): self.cwdAbs = cwd self.binary = binary self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/applications/keccaktreegpu/KeccakTreeGpu/") + "src/hiptestsuite/applications/keccaktreegpu/KeccakTreeGpu/") self.thistestpath = self.app_path self.prepareobj = None diff --git a/src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py similarity index 94% rename from src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py rename to src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py index 5cc5777..2825856 100644 --- a/src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py +++ b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py @@ -19,8 +19,8 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.keccaktreegpu.keccaktreegpu_parser_common import KeccakTreeParser +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.keccaktreegpu.keccaktreegpu_parser_common import KeccakTreeParser class BuildRunAmd(): def __init__(self, thistestpath, logFile, binary): diff --git a/src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py similarity index 94% rename from src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py rename to src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py index c015ffa..47e35c8 100644 --- a/src/testsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py +++ b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py @@ -19,8 +19,8 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.keccaktreegpu.keccaktreegpu_parser_common import KeccakTreeParser +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.keccaktreegpu.keccaktreegpu_parser_common import KeccakTreeParser class BuildRunNvidia(): def __init__(self, thistestpath, logFile, binary): diff --git a/src/testsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py similarity index 97% rename from src/testsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py rename to src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py index 86aab4f..2774344 100644 --- a/src/testsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py +++ b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py @@ -19,7 +19,7 @@ # THE SOFTWARE. import re -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd class KeccakTreeParser(): def __init__(self, results): diff --git a/src/testsuite/applications/mgbench/__init__.py b/src/hiptestsuite/applications/mgbench/__init__.py similarity index 100% rename from src/testsuite/applications/mgbench/__init__.py rename to src/hiptestsuite/applications/mgbench/__init__.py diff --git a/src/testsuite/applications/mgbench/mgbench.py b/src/hiptestsuite/applications/mgbench/mgbench.py similarity index 94% rename from src/testsuite/applications/mgbench/mgbench.py rename to src/hiptestsuite/applications/mgbench/mgbench.py index 1560842..bf34da8 100644 --- a/src/testsuite/applications/mgbench/mgbench.py +++ b/src/hiptestsuite/applications/mgbench/mgbench.py @@ -18,14 +18,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester, Test, TestData -from testsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM from typing import Union, List -from testsuite.test_classifier import TestClassifier -from testsuite.applications.mgbench.mgbench_build_amd import BuildRunAmd -from testsuite.applications.mgbench.mgbench_build_nvidia import BuildRunNvidia -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.mgbench.mgbench_build_amd import BuildRunAmd +from hiptestsuite.applications.mgbench.mgbench_build_nvidia import BuildRunNvidia +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import execshellcmd import os import re @@ -36,7 +36,7 @@ def __init__(self, cwd, testpath, mgtestfile, binary): self.mgtestfile = mgtestfile self.binary = binary self.app_path = os.path.join(self.cwdAbs,\ - "src/testsuite/applications/mgbench/") + "src/hiptestsuite/applications/mgbench/") self.app_root = os.path.join(self.app_path, "mgbench/") self.thistestpath = os.path.join(self.app_root, testpath) self.prepareobj = None diff --git a/src/testsuite/applications/mgbench/mgbench_build_amd.py b/src/hiptestsuite/applications/mgbench/mgbench_build_amd.py similarity index 95% rename from src/testsuite/applications/mgbench/mgbench_build_amd.py rename to src/hiptestsuite/applications/mgbench/mgbench_build_amd.py index 6f7d038..bb8f9ab 100644 --- a/src/testsuite/applications/mgbench/mgbench_build_amd.py +++ b/src/hiptestsuite/applications/mgbench/mgbench_build_amd.py @@ -19,8 +19,8 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.mgbench.mgbench_parser_common import MgbenchParser +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.mgbench.mgbench_parser_common import MgbenchParser class BuildRunAmd(): def __init__(self, app_root, thistestpath, logFile, mgtestfile, binary): diff --git a/src/testsuite/applications/mgbench/mgbench_build_nvidia.py b/src/hiptestsuite/applications/mgbench/mgbench_build_nvidia.py similarity index 96% rename from src/testsuite/applications/mgbench/mgbench_build_nvidia.py rename to src/hiptestsuite/applications/mgbench/mgbench_build_nvidia.py index 7f474ab..0230705 100644 --- a/src/testsuite/applications/mgbench/mgbench_build_nvidia.py +++ b/src/hiptestsuite/applications/mgbench/mgbench_build_nvidia.py @@ -19,8 +19,8 @@ # THE SOFTWARE. import os -from testsuite.common.hip_shell import execshellcmd -from testsuite.applications.mgbench.mgbench_parser_common import MgbenchParser +from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.applications.mgbench.mgbench_parser_common import MgbenchParser class BuildRunNvidia(): def __init__(self, app_root, thistestpath, logFile, mgtestfile, binary): diff --git a/src/testsuite/applications/mgbench/mgbench_parser_common.py b/src/hiptestsuite/applications/mgbench/mgbench_parser_common.py similarity index 97% rename from src/testsuite/applications/mgbench/mgbench_parser_common.py rename to src/hiptestsuite/applications/mgbench/mgbench_parser_common.py index 6d6fd8d..2c0c3e2 100644 --- a/src/testsuite/applications/mgbench/mgbench_parser_common.py +++ b/src/hiptestsuite/applications/mgbench/mgbench_parser_common.py @@ -20,7 +20,7 @@ import os import re -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd class MgbenchParser(): def __init__(self, results): diff --git a/src/testsuite/common/__init__.py b/src/hiptestsuite/common/__init__.py similarity index 100% rename from src/testsuite/common/__init__.py rename to src/hiptestsuite/common/__init__.py diff --git a/src/testsuite/common/hip_get_packages.py b/src/hiptestsuite/common/hip_get_packages.py similarity index 81% rename from src/testsuite/common/hip_get_packages.py rename to src/hiptestsuite/common/hip_get_packages.py index f625153..99e310b 100644 --- a/src/testsuite/common/hip_get_packages.py +++ b/src/hiptestsuite/common/hip_get_packages.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd import os @@ -26,12 +26,12 @@ class HipPackages(): def __init__(self): self.cwdAbs = os.getcwd() - self.conformancePath = os.path.join(self.cwdAbs, "src/testsuite/conformance/") + self.conformancePath = os.path.join(self.cwdAbs, "src/hiptestsuite/conformance/") # HIP & HIPAMD self.hippath = os.path.join(self.conformancePath, "HIP/") self.hipamdpath = os.path.join(self.conformancePath, "HIPAMD/") # HIP-Examples - self.appPath = os.path.join(self.cwdAbs,"src/testsuite/applications/hip_examples/") + self.appPath = os.path.join(self.cwdAbs,"src/hiptestsuite/applications/hip_examples/") self.examplepath = os.path.join(self.appPath, "HIP-Examples/") self.mixbenchpath = os.path.join(self.appPath, "mixbench/") self.gpustrmpath = os.path.join(self.appPath, "GPU-STREAM/") @@ -39,32 +39,30 @@ def __init__(self): self.rocclrpath = os.path.join(self.conformancePath, "ROCclr/") self.openclpath = os.path.join(self.conformancePath, "ROCm-OpenCL-Runtime/") # mgbench - self.mgbenchrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/mgbench/") + self.mgbenchrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/mgbench/") self.mgbenchapppath = os.path.join(self.mgbenchrootpath, "mgbench/") # CUDA-grep - self.cudagreprootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/cuda_grep/") + self.cudagreprootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/cuda_grep/") self.cudagrepapppath = os.path.join(self.cudagreprootpath, "CUDA-grep/") # cuda_memtest - self.cudamemrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/cuda_memtest/") + self.cudamemrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/cuda_memtest/") self.cudamemapppath = os.path.join(self.cudamemrootpath, "cuda_memtest/") # Quicksilver - self.qsrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/quicksilver/") + self.qsrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/quicksilver/") self.qsapppath = os.path.join(self.qsrootpath, "Quicksilver/") # Gridtools - self.gridtoolsrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/gridtools/GridTools/") + self.gridtoolsrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/gridtools/GridTools/") self.gridtoolsapppath = os.path.join(self.gridtoolsrootpath, "gridtools/") - self.gtbenchrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/gridtools/GridTools/") + self.gtbenchrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/gridtools/GridTools/") self.gtbenchapppath = os.path.join(self.gtbenchrootpath, "gtbench/") # Kokkos - self.kokkosrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/kokkos/") + self.kokkosrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/kokkos/") self.kokkosapppath = os.path.join(self.kokkosrootpath, "kokkos/") # Laghos - self.mfemrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/laghos/") + self.mfemrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/laghos/") self.mfemapppath = os.path.join(self.mfemrootpath, "mfem/") - self.laghosrootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/laghos/") + self.laghosrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/laghos/") self.laghosapppath = os.path.join(self.laghosrootpath, "Laghos/") - self.mpirootpath = os.path.join(self.cwdAbs, "src/testsuite/applications/hpc_apps/laghos/") - self.mpiapppath = os.path.join(self.mpirootpath, "openmpi/") def pull_repo(self, logFile, repo, branch, commitId, reponame): repo_root_path = "" @@ -134,10 +132,6 @@ def pull_repo(self, logFile, repo, branch, commitId, reponame): repo_root_path = self.laghosapppath repo_location = self.laghosrootpath repo_dir = "Laghos" - elif reponame == "openmpi": - repo_root_path = self.mpiapppath - repo_location = self.mpirootpath - repo_dir = "openmpi" if os.path.isdir(repo_root_path) and os.path.isdir(repo_root_path + "/.git"): print(reponame + " already exist") @@ -163,11 +157,12 @@ def pull_repo(self, logFile, repo, branch, commitId, reponame): cmdcd = "cd " + repo_location + ";" cmdcd += "rm -Rf " + repo_dir + "/;" cmdPull = "" + print("Cloning " + reponame + " to " + repo_location) if branch == "": #No branch name provided. Clone from main branch. cmdPull = "git clone " + repo else: + print("From branch: " + branch) cmdPull = "git clone -b " + branch + " " + repo - print("Cloning the latest repo from branch = " + branch + "...") cmdexc = cmdcd + cmdPull # Clone the latest version from repo execshellcmd(cmdexc, logFile, None) @@ -178,6 +173,7 @@ def pull_repo(self, logFile, repo, branch, commitId, reponame): print(reponame + " cloned successfully") # At this point check ensure git head is pointing to commitId if commitId != "": + print("Resetting to commit: " + commitId) cmdcd = "cd " + repo_root_path + ";" cmdexc = cmdcd + "git reset --hard " + commitId + ";" execshellcmd(cmdexc, logFile, None) diff --git a/src/testsuite/common/hip_shell.py b/src/hiptestsuite/common/hip_shell.py similarity index 100% rename from src/testsuite/common/hip_shell.py rename to src/hiptestsuite/common/hip_shell.py diff --git a/src/testsuite/config_processor.py b/src/hiptestsuite/config_processor.py similarity index 97% rename from src/testsuite/config_processor.py rename to src/hiptestsuite/config_processor.py index 0cd99e4..46a22de 100644 --- a/src/testsuite/config_processor.py +++ b/src/hiptestsuite/config_processor.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.AMD import AMDObject +from hiptestsuite.AMD import AMDObject class ConfigProcessor(AMDObject): diff --git a/src/testsuite/conformance/CMakePatch b/src/hiptestsuite/conformance/CMakePatch similarity index 100% rename from src/testsuite/conformance/CMakePatch rename to src/hiptestsuite/conformance/CMakePatch diff --git a/src/testsuite/conformance/__init__.py b/src/hiptestsuite/conformance/__init__.py similarity index 100% rename from src/testsuite/conformance/__init__.py rename to src/hiptestsuite/conformance/__init__.py diff --git a/src/testsuite/conformance/conformance_test_classifier.py b/src/hiptestsuite/conformance/conformance_test_classifier.py similarity index 96% rename from src/testsuite/conformance/conformance_test_classifier.py rename to src/hiptestsuite/conformance/conformance_test_classifier.py index 54edc0f..8b805e4 100644 --- a/src/testsuite/conformance/conformance_test_classifier.py +++ b/src/hiptestsuite/conformance/conformance_test_classifier.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.test_classifier import TestClassifier +from hiptestsuite.test_classifier import TestClassifier from typing import Union diff --git a/src/testsuite/conformance/hip_dtest.py b/src/hiptestsuite/conformance/hip_dtest.py similarity index 89% rename from src/testsuite/conformance/hip_dtest.py rename to src/hiptestsuite/conformance/hip_dtest.py index e86b267..c62b701 100644 --- a/src/testsuite/conformance/hip_dtest.py +++ b/src/hiptestsuite/conformance/hip_dtest.py @@ -18,15 +18,15 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.TesterRepository import Tester -from testsuite.Test import HIPTestData, TestResult, Test, HIPBuildData, HIP_PLATFORM +from hiptestsuite.TesterRepository import Tester +from hiptestsuite.Test import HIPTestData, TestResult, Test, HIPBuildData, HIP_PLATFORM from typing import Union, List -from testsuite.conformance.conformance_test_classifier import CONFORMANCE -from testsuite.test_classifier import TestClassifier -from testsuite.conformance.hip_dtest_build_amd import BuildRunAmd -from testsuite.conformance.hip_dtest_build_nvidia import BuildRunNvidia -from testsuite.common.hip_get_packages import HipPackages -from testsuite.common.hip_shell import * +from hiptestsuite.conformance.conformance_test_classifier import CONFORMANCE +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.conformance.hip_dtest_build_amd import BuildRunAmd +from hiptestsuite.conformance.hip_dtest_build_nvidia import BuildRunNvidia +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import * import os diff --git a/src/testsuite/conformance/hip_dtest_build_amd.py b/src/hiptestsuite/conformance/hip_dtest_build_amd.py similarity index 94% rename from src/testsuite/conformance/hip_dtest_build_amd.py rename to src/hiptestsuite/conformance/hip_dtest_build_amd.py index bd355af..fce86cb 100644 --- a/src/testsuite/conformance/hip_dtest_build_amd.py +++ b/src/hiptestsuite/conformance/hip_dtest_build_amd.py @@ -22,8 +22,8 @@ import tempfile import json import re -from testsuite.common.hip_shell import execshellcmd_largedump, execshellcmd -from testsuite.conformance.hip_dtest_build_common import BuildRunCommon +from hiptestsuite.common.hip_shell import execshellcmd_largedump, execshellcmd +from hiptestsuite.conformance.hip_dtest_build_common import BuildRunCommon class BuildRunAmd(BuildRunCommon): ''' diff --git a/src/testsuite/conformance/hip_dtest_build_common.py b/src/hiptestsuite/conformance/hip_dtest_build_common.py similarity index 95% rename from src/testsuite/conformance/hip_dtest_build_common.py rename to src/hiptestsuite/conformance/hip_dtest_build_common.py index 5b758f7..e88ff39 100644 --- a/src/testsuite/conformance/hip_dtest_build_common.py +++ b/src/hiptestsuite/conformance/hip_dtest_build_common.py @@ -21,7 +21,7 @@ import os import tempfile import re -from testsuite.common.hip_shell import execshellcmd_largedump, execshellcmd +from hiptestsuite.common.hip_shell import execshellcmd_largedump, execshellcmd class BuildRunCommon(): ''' @@ -30,7 +30,7 @@ class BuildRunCommon(): ''' def __init__(self, logfile): self.logfile = logfile - self.hippath = os.path.join(os.getcwd(), "src/testsuite/conformance/HIP") + self.hippath = os.path.join(os.getcwd(), "src/hiptestsuite/conformance/HIP") self.ctest_file = os.path.join(self.hippath, "build/ctest.txt") self.builddir = os.path.join(self.hippath, "build") self.expected_catch_binaries = ["ABMTests","MultiProcTests","UnitTests"] diff --git a/src/testsuite/conformance/hip_dtest_build_nvidia.py b/src/hiptestsuite/conformance/hip_dtest_build_nvidia.py similarity index 95% rename from src/testsuite/conformance/hip_dtest_build_nvidia.py rename to src/hiptestsuite/conformance/hip_dtest_build_nvidia.py index 2ed3ea6..334e058 100644 --- a/src/testsuite/conformance/hip_dtest_build_nvidia.py +++ b/src/hiptestsuite/conformance/hip_dtest_build_nvidia.py @@ -22,8 +22,8 @@ import tempfile import json import re -from testsuite.common.hip_shell import execshellcmd_largedump, execshellcmd -from testsuite.conformance.hip_dtest_build_common import BuildRunCommon +from hiptestsuite.common.hip_shell import execshellcmd_largedump, execshellcmd +from hiptestsuite.conformance.hip_dtest_build_common import BuildRunCommon class BuildRunNvidia(BuildRunCommon): ''' diff --git a/src/testsuite/list_tests.py b/src/hiptestsuite/list_tests.py similarity index 96% rename from src/testsuite/list_tests.py rename to src/hiptestsuite/list_tests.py index fe09c1e..0c09f15 100644 --- a/src/testsuite/list_tests.py +++ b/src/hiptestsuite/list_tests.py @@ -22,9 +22,9 @@ import typing from typing import Union, List -from testsuite.TesterRepository import TesterRepository, GetTests -from testsuite.Test import Test, Quick -from testsuite.test_classifier import TestClassifier +from hiptestsuite.TesterRepository import TesterRepository, GetTests +from hiptestsuite.Test import Test, Quick +from hiptestsuite.test_classifier import TestClassifier def list_tests(quick: bool, cfg, tester_repository=None): diff --git a/src/testsuite/match_fun_args_call.py b/src/hiptestsuite/match_fun_args_call.py similarity index 100% rename from src/testsuite/match_fun_args_call.py rename to src/hiptestsuite/match_fun_args_call.py diff --git a/src/testsuite/stress/__init__.py b/src/hiptestsuite/stress/__init__.py similarity index 100% rename from src/testsuite/stress/__init__.py rename to src/hiptestsuite/stress/__init__.py diff --git a/src/testsuite/targets.py b/src/hiptestsuite/targets.py similarity index 98% rename from src/testsuite/targets.py rename to src/hiptestsuite/targets.py index 070b52f..16fc502 100644 --- a/src/testsuite/targets.py +++ b/src/hiptestsuite/targets.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.AMD import AMDObject +from hiptestsuite.AMD import AMDObject class Target: diff --git a/src/testsuite/test_classifier.py b/src/hiptestsuite/test_classifier.py similarity index 97% rename from src/testsuite/test_classifier.py rename to src/hiptestsuite/test_classifier.py index a99f0a0..4998766 100644 --- a/src/testsuite/test_classifier.py +++ b/src/hiptestsuite/test_classifier.py @@ -18,7 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.AMD import AMDObject +from hiptestsuite.AMD import AMDObject from typing import Union diff --git a/src/testsuite/test_selector.py b/src/hiptestsuite/test_selector.py similarity index 98% rename from src/testsuite/test_selector.py rename to src/hiptestsuite/test_selector.py index f1740f2..5438c39 100644 --- a/src/testsuite/test_selector.py +++ b/src/hiptestsuite/test_selector.py @@ -18,8 +18,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from testsuite.AMD import AMDObject -from testsuite.TesterRepository import TesterRepository, Test, GetTests +from hiptestsuite.AMD import AMDObject +from hiptestsuite.TesterRepository import TesterRepository, Test, GetTests from typing import Union, List, Dict import re diff --git a/src/testsuite/thirdparty/__init__.py b/src/hiptestsuite/thirdparty/__init__.py similarity index 100% rename from src/testsuite/thirdparty/__init__.py rename to src/hiptestsuite/thirdparty/__init__.py diff --git a/src/testsuite/version.py b/src/hiptestsuite/version.py similarity index 100% rename from src/testsuite/version.py rename to src/hiptestsuite/version.py From c0e88a7882e71f434843ca5085b848bcc6698532 Mon Sep 17 00:00:00 2001 From: Rupam Chetia Date: Tue, 5 Oct 2021 14:58:02 +0530 Subject: [PATCH 12/18] Adding following fixes for MS4 1. A sample third party app. 2. Excluding 3rd party filters from default filter. 3. Added a script to clean the entire project. 4. Displaying Error for build fail in case of HIP Catch2. 5. Changing from amd -> hiptestsuite in examples/ Change-Id: Ifd9bbbbb16d1118d9cae1a5238188edea2f70890 --- cleanPrj.sh | 22 ++ examples/examples/0.py | 4 +- examples/examples/1.py | 6 +- examples/examples/2.py | 6 +- examples/examples/3.py | 6 +- examples/examples/4.py | 4 +- examples/run.py | 6 +- run.py | 5 +- src/hiptestsuite/TestersExecutor.py | 8 +- src/hiptestsuite/common/hip_get_packages.py | 362 +++++++++--------- src/hiptestsuite/conformance/hip_dtest.py | 337 ++++++++-------- src/hiptestsuite/test_selector.py | 14 +- .../thirdparty/sampleapp/__init__.py | 0 .../sampleapp/sampleapp_thirdparty.py | 32 ++ .../thirdparty/thirdparty_classifier.py | 31 ++ 15 files changed, 474 insertions(+), 369 deletions(-) create mode 100755 cleanPrj.sh create mode 100644 src/hiptestsuite/thirdparty/sampleapp/__init__.py create mode 100644 src/hiptestsuite/thirdparty/sampleapp/sampleapp_thirdparty.py create mode 100644 src/hiptestsuite/thirdparty/thirdparty_classifier.py diff --git a/cleanPrj.sh b/cleanPrj.sh new file mode 100755 index 0000000..59f48a8 --- /dev/null +++ b/cleanPrj.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# This shell script removes all generated files/folders and cloned repositories +echo "Cleaning hip-testsuite project.." +find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf +rm -Rf report +rm -Rf src/hiptestsuite/applications/cuda_grep/CUDA-grep +rm -Rf src/hiptestsuite/applications/cuda_memtest/cuda_memtest +rm -Rf src/hiptestsuite/applications/hip_examples/GPU-STREAM +rm -Rf src/hiptestsuite/applications/hip_examples/HIP-Examples +rm -Rf src/hiptestsuite/applications/hip_examples/mixbench +rm -Rf src/hiptestsuite/applications/mgbench/mgbench +rm -Rf src/hiptestsuite/applications/hpc_apps/gridtools/boost_1_72_0 +rm -Rf src/hiptestsuite/applications/hpc_apps/gridtools/GridTools +rm -Rf src/hiptestsuite/applications/hpc_apps/gridtools/gridtools +rm -Rf src/hiptestsuite/applications/hpc_apps/kokkos/kokkos +rm -Rf src/hiptestsuite/applications/hpc_apps/laghos/hypre* +rm -Rf src/hiptestsuite/applications/hpc_apps/laghos/Laghos +rm -Rf src/hiptestsuite/applications/hpc_apps/laghos/metis* +rm -Rf src/hiptestsuite/applications/hpc_apps/laghos/mfem +rm -Rf src/hiptestsuite/applications/hpc_apps/quicksilver/Quicksilver +rm -Rf src/hiptestsuite/conformance/HIP diff --git a/examples/examples/0.py b/examples/examples/0.py index 95ccb72..4f29896 100644 --- a/examples/examples/0.py +++ b/examples/examples/0.py @@ -20,8 +20,8 @@ from pathlib import Path -from amd.TesterRepository import Tester -from amd.Test import TestData, TestResult +from hiptestsuite.TesterRepository import Tester +from hiptestsuite.Test import TestData, TestResult class Test0(Tester): diff --git a/examples/examples/1.py b/examples/examples/1.py index 7ec2abb..ae5ef7e 100644 --- a/examples/examples/1.py +++ b/examples/examples/1.py @@ -18,9 +18,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.TesterRepository import Tester -from amd.Test import TestData, TestResult, Test -from amd.test_classifier import TestClassifier +from hiptestsuite.TesterRepository import Tester +from hiptestsuite.Test import TestData, TestResult, Test +from hiptestsuite.test_classifier import TestClassifier from typing import List, Union diff --git a/examples/examples/2.py b/examples/examples/2.py index 9014de8..095fc66 100644 --- a/examples/examples/2.py +++ b/examples/examples/2.py @@ -18,9 +18,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.TesterRepository import Tester -from amd.Test import TestData, TestResult, Test -from amd.test_classifier import TestClassifier +from hiptestsuite.TesterRepository import Tester +from hiptestsuite.Test import TestData, TestResult, Test +from hiptestsuite.test_classifier import TestClassifier from typing import List, Union diff --git a/examples/examples/3.py b/examples/examples/3.py index c9d8ba3..851f429 100644 --- a/examples/examples/3.py +++ b/examples/examples/3.py @@ -18,9 +18,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.TesterRepository import Tester -from amd.Test import HIPTestData, TestResult, Test -from amd.test_classifier import TestClassifier +from hiptestsuite.TesterRepository import Tester +from hiptestsuite.Test import HIPTestData, TestResult, Test +from hiptestsuite.test_classifier import TestClassifier from typing import List, Union diff --git a/examples/examples/4.py b/examples/examples/4.py index 98106e7..bc9c587 100644 --- a/examples/examples/4.py +++ b/examples/examples/4.py @@ -18,8 +18,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from amd.TesterRepository import Tester -from amd.Test import TestResult, TestData, LogLocation, UserAccess +from hiptestsuite.TesterRepository import Tester +from hiptestsuite.Test import TestResult, TestData, LogLocation, UserAccess class MyTestData(TestData, LogLocation, UserAccess): diff --git a/examples/run.py b/examples/run.py index 668d8be..1ff802c 100644 --- a/examples/run.py +++ b/examples/run.py @@ -26,9 +26,9 @@ sys.path.append(os.path.join(os.path.dirname(__file__), "..", "src")) -from amd.TestersExecutor import TestersExecutor -from amd.TesterRepository import TesterRepository -from amd.list_tests import list_tests +from hiptestsuite.TestersExecutor import TestersExecutor +from hiptestsuite.TesterRepository import TesterRepository +from hiptestsuite.list_tests import list_tests import cfg import examples diff --git a/run.py b/run.py index 24afbe4..9edd23e 100644 --- a/run.py +++ b/run.py @@ -61,10 +61,13 @@ def parse_args(): def main(): + # Exclude tests from thirdparty folder + exclude_module_paths = ["hiptestsuite/thirdparty"] + if parse_args(): tester_executor: TestersExecutor = TestersExecutor() tester_executor.config = cfg - tester_executor.executeTests() + tester_executor.executeTests(exclude_module_paths=exclude_module_paths) if __name__ == "__main__": diff --git a/src/hiptestsuite/TestersExecutor.py b/src/hiptestsuite/TestersExecutor.py index 1b33003..8bfaf69 100644 --- a/src/hiptestsuite/TestersExecutor.py +++ b/src/hiptestsuite/TestersExecutor.py @@ -40,7 +40,7 @@ class TestersExecutor(ConfigProcessor): def __init__(self): ConfigProcessor.__init__(self) - def executeTests(self, tester_repository: TesterRepository=None): + def executeTests(self, tester_repository: TesterRepository=None, exclude_module_paths=None): start_datetime = datetime.datetime.now() config = self.config log_location = config.log_location @@ -76,7 +76,7 @@ def executeTests(self, tester_repository: TesterRepository=None): test_selector: TestSelector = TestSelector(tester_repository=tester_repository) test_selector.config = config - tests: List[Test] = test_selector.select_tests(log_location=timestamped_log_location) + tests: List[Test] = test_selector.select_tests(log_location=timestamped_log_location, exclude_module_paths=exclude_module_paths) tests: List[Test] = sorted(tests, key=lambda x: x.test_name) tests_status = dict() tests_logs = dict() @@ -240,6 +240,10 @@ def executeTests(self, tester_repository: TesterRepository=None): with open(os.path.join(timestamped_log_location, 'report.json'), 'w+', encoding='utf-8') as f: json.dump(json_root, f, ensure_ascii=False, indent=4) + print("") + print("Test Complete: Log file directory is " + relative_timestamped_log_location) + + def get_os_name() -> str: cmd = "awk -F= '/^NAME=/{print $2}' /etc/os-release" diff --git a/src/hiptestsuite/common/hip_get_packages.py b/src/hiptestsuite/common/hip_get_packages.py index 99e310b..57ea7d1 100644 --- a/src/hiptestsuite/common/hip_get_packages.py +++ b/src/hiptestsuite/common/hip_get_packages.py @@ -1,181 +1,181 @@ -# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from hiptestsuite.common.hip_shell import execshellcmd - -import os - -# Common class to clone/pull dependent Packages -class HipPackages(): - def __init__(self): - self.cwdAbs = os.getcwd() - self.conformancePath = os.path.join(self.cwdAbs, "src/hiptestsuite/conformance/") - # HIP & HIPAMD - self.hippath = os.path.join(self.conformancePath, "HIP/") - self.hipamdpath = os.path.join(self.conformancePath, "HIPAMD/") - # HIP-Examples - self.appPath = os.path.join(self.cwdAbs,"src/hiptestsuite/applications/hip_examples/") - self.examplepath = os.path.join(self.appPath, "HIP-Examples/") - self.mixbenchpath = os.path.join(self.appPath, "mixbench/") - self.gpustrmpath = os.path.join(self.appPath, "GPU-STREAM/") - # Vdi and OpenCL - self.rocclrpath = os.path.join(self.conformancePath, "ROCclr/") - self.openclpath = os.path.join(self.conformancePath, "ROCm-OpenCL-Runtime/") - # mgbench - self.mgbenchrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/mgbench/") - self.mgbenchapppath = os.path.join(self.mgbenchrootpath, "mgbench/") - # CUDA-grep - self.cudagreprootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/cuda_grep/") - self.cudagrepapppath = os.path.join(self.cudagreprootpath, "CUDA-grep/") - # cuda_memtest - self.cudamemrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/cuda_memtest/") - self.cudamemapppath = os.path.join(self.cudamemrootpath, "cuda_memtest/") - # Quicksilver - self.qsrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/quicksilver/") - self.qsapppath = os.path.join(self.qsrootpath, "Quicksilver/") - # Gridtools - self.gridtoolsrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/gridtools/GridTools/") - self.gridtoolsapppath = os.path.join(self.gridtoolsrootpath, "gridtools/") - self.gtbenchrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/gridtools/GridTools/") - self.gtbenchapppath = os.path.join(self.gtbenchrootpath, "gtbench/") - # Kokkos - self.kokkosrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/kokkos/") - self.kokkosapppath = os.path.join(self.kokkosrootpath, "kokkos/") - # Laghos - self.mfemrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/laghos/") - self.mfemapppath = os.path.join(self.mfemrootpath, "mfem/") - self.laghosrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/laghos/") - self.laghosapppath = os.path.join(self.laghosrootpath, "Laghos/") - - def pull_repo(self, logFile, repo, branch, commitId, reponame): - repo_root_path = "" - repo_location = "" - repo_dir = "" - if reponame == "gpu-stream": - repo_root_path = self.gpustrmpath - repo_location = self.appPath - repo_dir = "GPU-STREAM" - elif reponame == "mixbench": - repo_root_path = self.mixbenchpath - repo_location = self.appPath - repo_dir = "mixbench" - elif reponame == "hip_examples": - repo_root_path = self.examplepath - repo_location = self.appPath - repo_dir = "HIP-Examples" - elif reponame == "HIP": - repo_root_path = self.hippath - repo_location = self.conformancePath - repo_dir = "HIP" - elif reponame == "hipamd": - repo_root_path = self.hipamdpath - repo_location = self.conformancePath - repo_dir = "HIPAMD" - elif reponame == "rocclr": - repo_root_path = self.rocclrpath - repo_location = self.conformancePath - repo_dir = "ROCclr" - elif reponame == "opencl": - repo_root_path = self.openclpath - repo_location = self.conformancePath - repo_dir = "ROCm-OpenCL-Runtime" - elif reponame == "mgbench": - repo_root_path = self.mgbenchapppath - repo_location = self.mgbenchrootpath - repo_dir = "mgbench" - elif reponame == "cudagrep": - repo_root_path = self.cudagrepapppath - repo_location = self.cudagreprootpath - repo_dir = "CUDA-grep" - elif reponame == "cudamemtest": - repo_root_path = self.cudamemapppath - repo_location = self.cudamemrootpath - repo_dir = "cuda_memtest" - elif reponame == "quicksilver": - repo_root_path = self.qsapppath - repo_location = self.qsrootpath - repo_dir = "Quicksilver" - elif reponame == "gridtools": - repo_root_path = self.gridtoolsapppath - repo_location = self.gridtoolsrootpath - repo_dir = "gridtools" - elif reponame == "gtbench": - repo_root_path = self.gtbenchapppath - repo_location = self.gtbenchrootpath - repo_dir = "gtbench" - elif reponame == "kokkos": - repo_root_path = self.kokkosapppath - repo_location = self.kokkosrootpath - repo_dir = "kokkos" - elif reponame == "mfem": - repo_root_path = self.mfemapppath - repo_location = self.mfemrootpath - repo_dir = "mfem" - elif reponame == "Laghos": - repo_root_path = self.laghosapppath - repo_location = self.laghosrootpath - repo_dir = "Laghos" - - if os.path.isdir(repo_root_path) and os.path.isdir(repo_root_path + "/.git"): - print(reponame + " already exist") - # Check if branch and commitId of local repo matches with input branch and commitId - # if not then update local repo - cmd = "cd " + os.path.join(repo_location, repo_dir) + ";" - cmd += "git branch" - currentbranch = execshellcmd(cmd, logFile, None) - currentbranch = currentbranch.replace("* ", "") - cmd = "cd " + os.path.join(repo_location, repo_dir) + ";" - cmd += "git rev-parse HEAD" - currentcommitid = execshellcmd(cmd, logFile, None) - # Check if the local repo is upto date with input configuration - if ((branch == "") or (branch in currentbranch)) and\ - ((commitId == "") or (commitId in currentcommitid)): - print("This repo is up to date with config") - return True - else: - print(reponame + " does not exist") - - # Update the repo - print("Updating: " + reponame) - cmdcd = "cd " + repo_location + ";" - cmdcd += "rm -Rf " + repo_dir + "/;" - cmdPull = "" - print("Cloning " + reponame + " to " + repo_location) - if branch == "": #No branch name provided. Clone from main branch. - cmdPull = "git clone " + repo - else: - print("From branch: " + branch) - cmdPull = "git clone -b " + branch + " " + repo - cmdexc = cmdcd + cmdPull - # Clone the latest version from repo - execshellcmd(cmdexc, logFile, None) - isRepoPresent = os.path.isdir(repo_root_path) - if not isRepoPresent: - return False - else: - print(reponame + " cloned successfully") - # At this point check ensure git head is pointing to commitId - if commitId != "": - print("Resetting to commit: " + commitId) - cmdcd = "cd " + repo_root_path + ";" - cmdexc = cmdcd + "git reset --hard " + commitId + ";" - execshellcmd(cmdexc, logFile, None) - print("Updating: " + reponame + " completed") - return True +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from hiptestsuite.common.hip_shell import execshellcmd + +import os + +# Common class to clone/pull dependent Packages +class HipPackages(): + def __init__(self): + self.cwdAbs = os.getcwd() + self.conformancePath = os.path.join(self.cwdAbs, "src/hiptestsuite/conformance/") + # HIP & HIPAMD + self.hippath = os.path.join(self.conformancePath, "HIP/") + self.hipamdpath = os.path.join(self.conformancePath, "HIPAMD/") + # HIP-Examples + self.appPath = os.path.join(self.cwdAbs,"src/hiptestsuite/applications/hip_examples/") + self.examplepath = os.path.join(self.appPath, "HIP-Examples/") + self.mixbenchpath = os.path.join(self.appPath, "mixbench/") + self.gpustrmpath = os.path.join(self.appPath, "GPU-STREAM/") + # Vdi and OpenCL + self.rocclrpath = os.path.join(self.conformancePath, "ROCclr/") + self.openclpath = os.path.join(self.conformancePath, "ROCm-OpenCL-Runtime/") + # mgbench + self.mgbenchrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/mgbench/") + self.mgbenchapppath = os.path.join(self.mgbenchrootpath, "mgbench/") + # CUDA-grep + self.cudagreprootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/cuda_grep/") + self.cudagrepapppath = os.path.join(self.cudagreprootpath, "CUDA-grep/") + # cuda_memtest + self.cudamemrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/cuda_memtest/") + self.cudamemapppath = os.path.join(self.cudamemrootpath, "cuda_memtest/") + # Quicksilver + self.qsrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/quicksilver/") + self.qsapppath = os.path.join(self.qsrootpath, "Quicksilver/") + # Gridtools + self.gridtoolsrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/gridtools/GridTools/") + self.gridtoolsapppath = os.path.join(self.gridtoolsrootpath, "gridtools/") + self.gtbenchrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/gridtools/GridTools/") + self.gtbenchapppath = os.path.join(self.gtbenchrootpath, "gtbench/") + # Kokkos + self.kokkosrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/kokkos/") + self.kokkosapppath = os.path.join(self.kokkosrootpath, "kokkos/") + # Laghos + self.mfemrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/laghos/") + self.mfemapppath = os.path.join(self.mfemrootpath, "mfem/") + self.laghosrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/laghos/") + self.laghosapppath = os.path.join(self.laghosrootpath, "Laghos/") + + def pull_repo(self, logFile, repo, branch, commitId, reponame): + repo_root_path = "" + repo_location = "" + repo_dir = "" + if reponame == "gpu-stream": + repo_root_path = self.gpustrmpath + repo_location = self.appPath + repo_dir = "GPU-STREAM" + elif reponame == "mixbench": + repo_root_path = self.mixbenchpath + repo_location = self.appPath + repo_dir = "mixbench" + elif reponame == "hip_examples": + repo_root_path = self.examplepath + repo_location = self.appPath + repo_dir = "HIP-Examples" + elif reponame == "HIP": + repo_root_path = self.hippath + repo_location = self.conformancePath + repo_dir = "HIP" + elif reponame == "hipamd": + repo_root_path = self.hipamdpath + repo_location = self.conformancePath + repo_dir = "HIPAMD" + elif reponame == "rocclr": + repo_root_path = self.rocclrpath + repo_location = self.conformancePath + repo_dir = "ROCclr" + elif reponame == "opencl": + repo_root_path = self.openclpath + repo_location = self.conformancePath + repo_dir = "ROCm-OpenCL-Runtime" + elif reponame == "mgbench": + repo_root_path = self.mgbenchapppath + repo_location = self.mgbenchrootpath + repo_dir = "mgbench" + elif reponame == "cudagrep": + repo_root_path = self.cudagrepapppath + repo_location = self.cudagreprootpath + repo_dir = "CUDA-grep" + elif reponame == "cudamemtest": + repo_root_path = self.cudamemapppath + repo_location = self.cudamemrootpath + repo_dir = "cuda_memtest" + elif reponame == "quicksilver": + repo_root_path = self.qsapppath + repo_location = self.qsrootpath + repo_dir = "Quicksilver" + elif reponame == "gridtools": + repo_root_path = self.gridtoolsapppath + repo_location = self.gridtoolsrootpath + repo_dir = "gridtools" + elif reponame == "gtbench": + repo_root_path = self.gtbenchapppath + repo_location = self.gtbenchrootpath + repo_dir = "gtbench" + elif reponame == "kokkos": + repo_root_path = self.kokkosapppath + repo_location = self.kokkosrootpath + repo_dir = "kokkos" + elif reponame == "mfem": + repo_root_path = self.mfemapppath + repo_location = self.mfemrootpath + repo_dir = "mfem" + elif reponame == "Laghos": + repo_root_path = self.laghosapppath + repo_location = self.laghosrootpath + repo_dir = "Laghos" + + if os.path.isdir(repo_root_path) and os.path.isdir(repo_root_path + "/.git"): + print(reponame + " already exist") + # Check if branch and commitId of local repo matches with input branch and commitId + # if not then update local repo + cmd = "cd " + os.path.join(repo_location, repo_dir) + ";" + cmd += "git branch" + currentbranch = execshellcmd(cmd, logFile, None) + currentbranch = currentbranch.replace("* ", "") + cmd = "cd " + os.path.join(repo_location, repo_dir) + ";" + cmd += "git rev-parse HEAD" + currentcommitid = execshellcmd(cmd, logFile, None) + # Check if the local repo is upto date with input configuration + if ((branch == "") or (branch in currentbranch)) and\ + ((commitId == "") or (commitId in currentcommitid)): + print("This repo is up to date with config") + return True + else: + print(reponame + " does not exist") + + # Update the repo + print("Updating: " + reponame) + cmdcd = "cd " + repo_location + ";" + cmdcd += "rm -Rf " + repo_dir + "/;" + cmdPull = "" + print("Cloning " + reponame + " to " + repo_location) + if branch == "": #No branch name provided. Clone from main branch. + cmdPull = "git clone " + repo + else: + print("From branch: " + branch) + cmdPull = "git clone -b " + branch + " " + repo + cmdexc = cmdcd + cmdPull + # Clone the latest version from repo + execshellcmd(cmdexc, logFile, None) + isRepoPresent = os.path.isdir(repo_root_path) + if not isRepoPresent: + return False + else: + print(reponame + " cloned successfully") + # At this point check ensure git head is pointing to commitId + if commitId != "": + print("Resetting to commit: " + commitId) + cmdcd = "cd " + repo_root_path + ";" + cmdexc = cmdcd + "git reset --hard " + commitId + ";" + execshellcmd(cmdexc, logFile, None) + print("Updating: " + reponame + " completed") + return True diff --git a/src/hiptestsuite/conformance/hip_dtest.py b/src/hiptestsuite/conformance/hip_dtest.py index c62b701..c8a1c0f 100644 --- a/src/hiptestsuite/conformance/hip_dtest.py +++ b/src/hiptestsuite/conformance/hip_dtest.py @@ -1,165 +1,172 @@ -# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from hiptestsuite.TesterRepository import Tester -from hiptestsuite.Test import HIPTestData, TestResult, Test, HIPBuildData, HIP_PLATFORM -from typing import Union, List -from hiptestsuite.conformance.conformance_test_classifier import CONFORMANCE -from hiptestsuite.test_classifier import TestClassifier -from hiptestsuite.conformance.hip_dtest_build_amd import BuildRunAmd -from hiptestsuite.conformance.hip_dtest_build_nvidia import BuildRunNvidia -from hiptestsuite.common.hip_get_packages import HipPackages -from hiptestsuite.common.hip_shell import * - -import os - -# Common class to clone, set up, build and run test -class PrepareTest(): - def __init__(self): - self.hiprepo = "" # Default - self.hipbranch = "" - self.hipbranch = "" - self.hipcommitId = "" - self.logfd = None - self.downloadresult = True - self.buildresult = True - self.buildobj = None - - # Get the GIT repo information from configuration input - def setrepoinfo(self, test_data: HIPBuildData): - if test_data.repos["hip"].repo_url != None: - self.hiprepo = test_data.repos["hip"].repo_url - else: - return False - if test_data.repos["hip"].branch != None: - self.hipbranch = test_data.repos["hip"].branch - if test_data.repos["hip"].commit_id != None: - self.hipcommitId = test_data.repos["hip"].commit_id - return True - - # Download the relevant packages from GIT for this test - def downloadTest(self, log, platform): - ret = HipPackages().pull_repo(log, self.hiprepo, self.hipbranch,\ - self.hipcommitId, "HIP") - return ret - - # Build Packages - def build_package(self, logFile, platform): - status = True - if platform == HIP_PLATFORM.nvidia: - self.buildobj = BuildRunNvidia(logFile) - elif platform == HIP_PLATFORM.amd: - self.buildobj = BuildRunAmd(logFile) - else: - print("Invalid Platform") - return False - - return self.buildobj.build_package() - - # Run test - def runtest(self, logFile, testcase): - status = "Failed" - if self.buildobj != None: - status = self.buildobj.runtest(logFile, testcase) - return status - - # Get ctest info - def get_ctest_list(self): - ret = None - if self.buildobj != None: - ret = self.buildobj.get_all_ctest() - return ret - - -# Test HIP Dtest/ -class Hipconformance(Tester, PrepareTest): - def __init__(self): - Tester.__init__(self) - PrepareTest.__init__(self) - - def getTests(self, get_tests_data: HIPBuildData) -> List[Test]: - # Set repo info - testlist = [] - if get_tests_data.quick: - return testlist - self.logfd = open(get_tests_data.log_location + "/Hipconformance.log", 'w+') - status = self.setrepoinfo(get_tests_data) - if not status: - self.downloadresult = False - return testlist - - # Download repos - self.downloadresult = self.downloadTest(self.logfd, get_tests_data.HIP_PLATFORM) - if not self.downloadresult: - print("Rocm packages download failed!") - return testlist - - print("Building HIP package ...") - # Build Rocclr and Hip - self.buildresult = self.build_package(self.logfd, get_tests_data.HIP_PLATFORM) - if not self.buildresult: - print("Rocm packages build failed!") - return testlist - - print("Building HIP package completed") - # Fetch all the ctests - dtestnamelist = self.get_ctest_list() - - for testname in dtestnamelist: - test = Test() - test.test_name = testname - category = CONFORMANCE() - category.add_matched_with_names() - test.classifiers = [category] - test.tester = self - testlist.append(test) - return testlist - - def get_test_classifiers(self) -> Union[None, List[TestClassifier]]: - category = CONFORMANCE() - category.add_matched_with_names() - return [category] - - def test(self, test_data: HIPTestData): - if not self.downloadresult: - test_data.test_result = TestResult.FAIL - return - - if not self.buildresult: - test_data.test_result = TestResult.FAIL - return - # Build test - print("Running test: " + test_data.test.test_name + "..........") - testcase = test_data.test.test_name - status = None - - with open(test_data.log_location + "/test.log", 'w+') as testLogger: - status = self.runtest(testLogger, testcase) - - if status == "PASSED": - test_data.test_result = TestResult.PASS - elif status == "SKIP": - test_data.test_result = TestResult.SKIP - elif status == "FAILED": - test_data.test_result = TestResult.FAIL - - def clean(self): - if self.logfd != None: - self.logfd.close() +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from hiptestsuite.TesterRepository import Tester +from hiptestsuite.Test import HIPTestData, TestResult, Test, HIPBuildData, HIP_PLATFORM +from typing import Union, List +from hiptestsuite.conformance.conformance_test_classifier import CONFORMANCE +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.conformance.hip_dtest_build_amd import BuildRunAmd +from hiptestsuite.conformance.hip_dtest_build_nvidia import BuildRunNvidia +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import * + +import os + +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self): + self.hiprepo = "" # Default + self.hipbranch = "" + self.hipbranch = "" + self.hipcommitId = "" + self.logfd = None + self.downloadresult = True + self.buildresult = True + self.buildobj = None + + # Get the GIT repo information from configuration input + def setrepoinfo(self, test_data: HIPBuildData): + if test_data.repos["hip"].repo_url != None: + self.hiprepo = test_data.repos["hip"].repo_url + else: + return False + if test_data.repos["hip"].branch != None: + self.hipbranch = test_data.repos["hip"].branch + if test_data.repos["hip"].commit_id != None: + self.hipcommitId = test_data.repos["hip"].commit_id + return True + + # Download the relevant packages from GIT for this test + def downloadTest(self, log, platform): + ret = HipPackages().pull_repo(log, self.hiprepo, self.hipbranch,\ + self.hipcommitId, "HIP") + return ret + + # Build Packages + def build_package(self, logFile, platform): + status = True + if platform == HIP_PLATFORM.nvidia: + self.buildobj = BuildRunNvidia(logFile) + elif platform == HIP_PLATFORM.amd: + self.buildobj = BuildRunAmd(logFile) + else: + print("Invalid Platform") + return False + + return self.buildobj.build_package() + + # Run test + def runtest(self, logFile, testcase): + status = "Failed" + if self.buildobj != None: + status = self.buildobj.runtest(logFile, testcase) + return status + + # Get ctest info + def get_ctest_list(self): + ret = None + if self.buildobj != None: + ret = self.buildobj.get_all_ctest() + return ret + + +# Test HIP Dtest/ +class Hipconformance(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + PrepareTest.__init__(self) + + def getTests(self, get_tests_data: HIPBuildData) -> List[Test]: + # Set repo info + testlist = [] + if get_tests_data.quick: + return testlist + self.logfd = open(get_tests_data.log_location + "/Hipconformance.log", 'w+') + status = self.setrepoinfo(get_tests_data) + if not status: + self.downloadresult = False + return testlist + + # Download repos + self.downloadresult = self.downloadTest(self.logfd, get_tests_data.HIP_PLATFORM) + if not self.downloadresult: + cmd = "echo \"Rocm packages download failed!\";" + execshellcmd(cmd, self.logfd, None) + return testlist + + print("Building HIP package ...") + # Build Rocclr and Hip + self.buildresult = self.build_package(self.logfd, get_tests_data.HIP_PLATFORM) + if not self.buildresult: + cmd = "echo \"Rocm packages build failed!!\";" + execshellcmd(cmd, self.logfd, None) + print("Rocm packages build failed!") + return testlist + + print("Building HIP package completed") + # Fetch all the ctests + dtestnamelist = self.get_ctest_list() + + for testname in dtestnamelist: + test = Test() + test.test_name = testname + category = CONFORMANCE() + category.add_matched_with_names() + test.classifiers = [category] + test.tester = self + testlist.append(test) + return testlist + + def get_test_classifiers(self) -> Union[None, List[TestClassifier]]: + category = CONFORMANCE() + category.add_matched_with_names() + return [category] + + def test(self, test_data: HIPTestData): + if not self.downloadresult: + test_data.test_result = TestResult.ERROR + cmd = "echo \"HIP Catch2 Build FAILED!\";" + execshellcmd(cmd, self.logfd, None) + return + + if not self.buildresult: + test_data.test_result = TestResult.ERROR + cmd = "echo \"HIP Catch2 Build FAILED!\";" + execshellcmd(cmd, self.logfd, None) + return + # Build test + print("Running test: " + test_data.test.test_name + "..........") + testcase = test_data.test.test_name + status = None + + with open(test_data.log_location + "/test.log", 'w+') as testLogger: + status = self.runtest(testLogger, testcase) + + if status == "PASSED": + test_data.test_result = TestResult.PASS + elif status == "SKIP": + test_data.test_result = TestResult.SKIP + elif status == "FAILED": + test_data.test_result = TestResult.FAIL + + def clean(self): + if self.logfd != None: + self.logfd.close() diff --git a/src/hiptestsuite/test_selector.py b/src/hiptestsuite/test_selector.py index 5438c39..cc7b61d 100644 --- a/src/hiptestsuite/test_selector.py +++ b/src/hiptestsuite/test_selector.py @@ -22,7 +22,7 @@ from hiptestsuite.TesterRepository import TesterRepository, Test, GetTests from typing import Union, List, Dict -import re +import re, sys, os class TestSelector(GetTests): @@ -30,16 +30,22 @@ def __init__(self, tester_repository: TesterRepository): AMDObject.__init__(self) GetTests.__init__(self, tester_repository=tester_repository) - def to_select_this_test(self, test: Test, test_name_regexes): + def to_select_this_test(self, test: Test, test_name_regexes, exclude_module_paths=None): test_name = test.test_name also_matched_with_test_names = test.also_matched_with_test_names applicable_for_target = test.applicable_for_target classifiers = test.classifiers select_this_test = False + class_abs_path = os.path.abspath(sys.modules[test.tester.__class__.__module__].__file__) if test_name_regexes is None: select_this_test = True + for exclude_module_path in exclude_module_paths: + if exclude_module_path in class_abs_path: + print("Warning: Test " + test_name + " is excluded, please run selectively") + select_this_test = False + break else: for test_name_regex in test_name_regexes: test_name_regex: Union[str, List[str]] = test_name_regex @@ -108,7 +114,7 @@ def get_all_classifiers(self, testclassifier, classifierlist): for myclassifier in testclassifier: self.get_all_classifierkeys(myclassifier.matched_with_names, classifierlist) - def select_tests(self, log_location: str) -> List[Test]: + def select_tests(self, log_location: str, exclude_module_paths) -> List[Test]: config = self.config tests = list() run_tests = config.run_tests @@ -121,7 +127,7 @@ def select_tests(self, log_location: str) -> List[Test]: if test_name_regexes is None: for test_of_tester in self.get_tests(log_location=log_location, quick=False): - select_this_test = self.to_select_this_test(test=test_of_tester, test_name_regexes=test_name_regexes) + select_this_test = self.to_select_this_test(test=test_of_tester, test_name_regexes=test_name_regexes, exclude_module_paths=exclude_module_paths) if select_this_test: tests.append(test_of_tester) else: diff --git a/src/hiptestsuite/thirdparty/sampleapp/__init__.py b/src/hiptestsuite/thirdparty/sampleapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/hiptestsuite/thirdparty/sampleapp/sampleapp_thirdparty.py b/src/hiptestsuite/thirdparty/sampleapp/sampleapp_thirdparty.py new file mode 100644 index 0000000..63ad77a --- /dev/null +++ b/src/hiptestsuite/thirdparty/sampleapp/sampleapp_thirdparty.py @@ -0,0 +1,32 @@ +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from hiptestsuite.thirdparty.thirdparty_classifier import THIRDPARTY_CLASSIFIER + +class SAMPLEAPP(THIRDPARTY_CLASSIFIER): + def __init__(self): + THIRDPARTY_CLASSIFIER.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + THIRDPARTY_CLASSIFIER.add_matched_with_names(self, {"sampleapp": matched_with_names}) + +# Sample App for 3rd party +class sampleapp_test(Tester): + def __init__(self): + Tester.__init__(self) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = SAMPLEAPP() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + pass + + def test(self, test_data: HIPTestData): + print("=============== Running Sample Third Party App ===============") + test_data.test_result = TestResult.PASS diff --git a/src/hiptestsuite/thirdparty/thirdparty_classifier.py b/src/hiptestsuite/thirdparty/thirdparty_classifier.py new file mode 100644 index 0000000..e79c6e0 --- /dev/null +++ b/src/hiptestsuite/thirdparty/thirdparty_classifier.py @@ -0,0 +1,31 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from hiptestsuite.test_classifier import TestClassifier + +from typing import Union + + +class THIRDPARTY_CLASSIFIER(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"thirdparty": matched_with_names}) From bf92ee9aac778fc373a8d1a8b1972e83e8ba9ee2 Mon Sep 17 00:00:00 2001 From: Rupam Chetia Date: Wed, 13 Oct 2021 16:57:48 +0530 Subject: [PATCH 13/18] Adding verbosity(CONFORMANCE_VERBOSE) to conformance tests. Adding support for AMD_LOG_LEVEL in conformance tests. Applied following changes for conformance tests: 1.Removed patch for conformance tests on nvidia 2.Updated HIP commit id to latest 3.Updated build command for AMD. Change-Id: Ic4a6cde70ff7577fb33a08329aca293b44c62d11 --- cfg.py | 5 ++++- src/hiptestsuite/Test.py | 19 +++++++++++++++++-- src/hiptestsuite/common/hip_shell.py | 4 ++-- src/hiptestsuite/conformance/CMakePatch | 9 --------- src/hiptestsuite/conformance/hip_dtest.py | 8 ++++---- .../conformance/hip_dtest_build_amd.py | 9 +++++---- .../conformance/hip_dtest_build_common.py | 17 ++++++++++++----- .../conformance/hip_dtest_build_nvidia.py | 9 ++++----- 8 files changed, 48 insertions(+), 32 deletions(-) delete mode 100644 src/hiptestsuite/conformance/CMakePatch diff --git a/cfg.py b/cfg.py index ab3c13c..e3c00e9 100644 --- a/cfg.py +++ b/cfg.py @@ -32,6 +32,9 @@ # None/0/1/2/4 HIPCC_VERBOSE = None +# None/0/1 +CONFORMANCE_VERBOSE = None + # None/cuda directory CUDA_PATH = None @@ -74,7 +77,7 @@ "hip": { "repo_url": "https://github.com/ROCm-Developer-Tools/HIP", "branch": None, - "commit_id": "865b40d8bd8b26a7dfe4d9719e8dcf26f4b3afc6" + "commit_id": "96ee9d1397f02ac4b4badd9243994728f6a89fe5" }, "mixbench": { "repo_url": "https://github.com/ekondis/mixbench.git", diff --git a/src/hiptestsuite/Test.py b/src/hiptestsuite/Test.py index b3840fd..d816a10 100644 --- a/src/hiptestsuite/Test.py +++ b/src/hiptestsuite/Test.py @@ -68,6 +68,20 @@ def __init__(self): self.test_result: Union[None, TestResult] = None +class ConformanceTestData(AMDObject): + def __init__(self): + AMDObject.__init__(self) + self.CONFORMANCE_VERBOSE = None + + def loadConfig(self): + if self.config.CONFORMANCE_VERBOSE == 0: + self.CONFORMANCE_VERBOSE = 0 + elif self.config.CONFORMANCE_VERBOSE == 1: + self.CONFORMANCE_VERBOSE = 1 + else: + self.CONFORMANCE_VERBOSE = 0 + + class CompileData(AMDObject): def __init__(self): AMDObject.__init__(self) @@ -106,7 +120,6 @@ class HIPCCVerbose(Enum): TWO = auto() FOUR = auto() - class Optimization_Level(Enum): ZERO = auto() ONE = auto() @@ -188,16 +201,18 @@ def loadConfig(self): # ToDo offload_target -class HIPTestData(TestData, HIPCCCompileData, AllGitData, LogLocation, UserAccess): +class HIPTestData(TestData, HIPCCCompileData, AllGitData, LogLocation, UserAccess, ConformanceTestData): def __init__(self): UserAccess.__init__(self) LogLocation.__init__(self) TestData.__init__(self) + ConformanceTestData.__init__(self) HIPCCCompileData.__init__(self) AllGitData.__init__(self) def loadConfig(self): UserAccess.loadConfig(self) + ConformanceTestData.loadConfig(self) AllGitData.loadConfig(self) HIPCCCompileData.loadConfig(self) diff --git a/src/hiptestsuite/common/hip_shell.py b/src/hiptestsuite/common/hip_shell.py index df948c7..166c515 100644 --- a/src/hiptestsuite/common/hip_shell.py +++ b/src/hiptestsuite/common/hip_shell.py @@ -26,7 +26,7 @@ def execshellcmd(cmdexc, logfile, myenv): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=0) proc.wait() - stdoutstr = proc.stdout.read().decode('utf-8') + stdoutstr = proc.stdout.read().decode('utf-8', errors='ignore') if logfile != None: logfile.write(stdoutstr) return stdoutstr @@ -35,7 +35,7 @@ def execshellcmd_largedump(cmdexc, logfile, runlog, myenv): runlog.seek(0) proc = subprocess.Popen(cmdexc, shell=True, env=myenv, stdin=subprocess.PIPE, stdout=runlog, stderr=subprocess.STDOUT, - bufsize=0) + bufsize=0, universal_newlines=False) proc.wait() runlog.seek(0) if logfile != None: diff --git a/src/hiptestsuite/conformance/CMakePatch b/src/hiptestsuite/conformance/CMakePatch deleted file mode 100644 index 2da8a78..0000000 --- a/src/hiptestsuite/conformance/CMakePatch +++ /dev/null @@ -1,9 +0,0 @@ ---- CMakeLists.txt 2021-09-30 14:11:43.980141598 +0530 -+++ CMakeListsNew.txt 2021-09-30 14:11:43.784142432 +0530 -@@ -1,3 +1,6 @@ -+cmake_minimum_required(VERSION 3.16.8) -+project(hiptests) -+ - # Set HIP Path - if(NOT DEFINED HIP_PATH) - if(DEFINED ENV{HIP_PATH}) diff --git a/src/hiptestsuite/conformance/hip_dtest.py b/src/hiptestsuite/conformance/hip_dtest.py index c8a1c0f..9b585b1 100644 --- a/src/hiptestsuite/conformance/hip_dtest.py +++ b/src/hiptestsuite/conformance/hip_dtest.py @@ -74,10 +74,10 @@ def build_package(self, logFile, platform): return self.buildobj.build_package() # Run test - def runtest(self, logFile, testcase): + def runtest(self, logFile, verbosity, testcase): status = "Failed" if self.buildobj != None: - status = self.buildobj.runtest(logFile, testcase) + status = self.buildobj.runtest(logFile, verbosity, testcase) return status # Get ctest info @@ -157,8 +157,8 @@ def test(self, test_data: HIPTestData): testcase = test_data.test.test_name status = None - with open(test_data.log_location + "/test.log", 'w+') as testLogger: - status = self.runtest(testLogger, testcase) + with open(test_data.log_location + "/test.log", 'wb+') as testLogger: + status = self.runtest(testLogger, test_data.CONFORMANCE_VERBOSE, testcase) if status == "PASSED": test_data.test_result = TestResult.PASS diff --git a/src/hiptestsuite/conformance/hip_dtest_build_amd.py b/src/hiptestsuite/conformance/hip_dtest_build_amd.py index fce86cb..680b7a6 100644 --- a/src/hiptestsuite/conformance/hip_dtest_build_amd.py +++ b/src/hiptestsuite/conformance/hip_dtest_build_amd.py @@ -32,6 +32,7 @@ class BuildRunAmd(BuildRunCommon): ''' def __init__(self, logfile): BuildRunCommon.__init__(self, logfile) + self.envtoset = os.environ.copy() # Build HIP Catch2 for AMD platform def build_package(self): @@ -45,11 +46,11 @@ def build_package(self): print("Catch2 test not built. Building Catch2 ..") cmd = "cd " + self.hippath + ";" cmd += "mkdir build; cd build;" - cmd += "cmake -DHIP_PATH=/opt/rocm/hip ../tests/catch;" + cmd += "cmake -DHIP_PATH=/opt/rocm/hip -DHIP_PLATFORM=amd ../tests/catch;" cmd += "make -j build_tests;" cmdexc = cmd runlogdump = tempfile.TemporaryFile("w+") - execshellcmd_largedump(cmdexc, self.logfile, runlogdump, None) + execshellcmd_largedump(cmdexc, self.logfile, runlogdump, self.envtoset) runlogdump.close() # Validate if HIP build is successful @@ -62,6 +63,6 @@ def build_package(self): return True # Execute test cases - def runtest(self, log, testcase): - return BuildRunCommon.runtest(self, log, testcase, None) + def runtest(self, log, verbosity, testcase): + return BuildRunCommon.runtest(self, log, verbosity, testcase, self.envtoset) diff --git a/src/hiptestsuite/conformance/hip_dtest_build_common.py b/src/hiptestsuite/conformance/hip_dtest_build_common.py index e88ff39..94a1af8 100644 --- a/src/hiptestsuite/conformance/hip_dtest_build_common.py +++ b/src/hiptestsuite/conformance/hip_dtest_build_common.py @@ -68,22 +68,29 @@ def get_all_ctest(self): # Parse the test result def parsetest(self, log): log.seek(0) - text = log.read() + logbytes = log.read() + logutf = logbytes.decode('utf-8', errors='ignore') status = None - if re.search("100% tests passed", text) != None: + if re.search("100% tests passed", logutf) != None: status = "PASSED" else: status = "FAILED" return status # Execute the test case - def runtest(self, log, testcase, envtoset): - cmdtest = "ctest -R " + "\"" + testcase + "\"" + def runtest(self, log, verbosity, testcase, envtoset): + if verbosity == 0: + cmdtest = "ctest -R " + "\"" + testcase + "\"" + elif verbosity == 1: + cmdtest = "ctest -R " + "\"" + testcase + "\"" + " --verbose" + else: + cmdtest = "ctest -R " + "\"" + testcase + "\"" + print("Executing command = " + cmdtest) # run test cmd = "cd " + self.builddir + ";" cmd += cmdtest + ";" - runlogdump = tempfile.TemporaryFile("w+") + runlogdump = tempfile.TemporaryFile("wb+") execshellcmd_largedump(cmd, log, runlogdump, envtoset) status = self.parsetest(runlogdump) runlogdump.close() diff --git a/src/hiptestsuite/conformance/hip_dtest_build_nvidia.py b/src/hiptestsuite/conformance/hip_dtest_build_nvidia.py index 334e058..3b4711f 100644 --- a/src/hiptestsuite/conformance/hip_dtest_build_nvidia.py +++ b/src/hiptestsuite/conformance/hip_dtest_build_nvidia.py @@ -32,6 +32,7 @@ class BuildRunNvidia(BuildRunCommon): ''' def __init__(self, logfile): BuildRunCommon.__init__(self, logfile) + self.envtoset = os.environ.copy() def setenv(self): env = "export HIP_PLATFORM=nvidia;" @@ -50,15 +51,13 @@ def build_package(self): # Build HIP print("Catch2 test not built. Building Catch2 ..") cmd = self.setenv() - cmd += "cd " + self.hippath + "/tests/catch;" - cmd += "patch -p0 < ../../../CMakePatch;" cmd += "cd " + self.hippath + ";" cmd += "mkdir build; cd build;" cmd += "cmake -DHIP_COMPILER=nvcc -DHIP_PLATFORM=nvidia -DHIP_RUNTIME=cuda -DHIP_PATH=/opt/rocm/hip ../tests/catch;" cmd += "make -j build_tests;" cmdexc = cmd runlogdump = tempfile.TemporaryFile("w+") - execshellcmd_largedump(cmdexc, self.logfile, runlogdump, None) + execshellcmd_largedump(cmdexc, self.logfile, runlogdump, self.envtoset) runlogdump.close() # Validate if HIP build is successful @@ -71,5 +70,5 @@ def build_package(self): return True # Execute test cases - def runtest(self, log, testcase): - return BuildRunCommon.runtest(self, log, testcase, None) + def runtest(self, log, verbosity, testcase): + return BuildRunCommon.runtest(self, log, verbosity, testcase, self.envtoset) From a886b7c3b9a7e0c84d593a0247403751c32d0b94 Mon Sep 17 00:00:00 2001 From: Rupam Chetia Date: Thu, 21 Oct 2021 22:38:39 +0530 Subject: [PATCH 14/18] Updating cuda_memtest hipified diff patch. Change-Id: I75dead20bc222ba14cf5ba2a9f04f7efe4d5771a --- .../applications/cuda_memtest/cuda_memtest_patch | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_patch b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_patch index 5a09723..7f6f823 100644 --- a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_patch +++ b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_patch @@ -1,9 +1,9 @@ ---- cuda_memtest.cu 2021-09-21 20:23:48.117207109 +0530 -+++ cuda_memtest_modified.cu 2021-09-21 20:20:52.553149253 +0530 +--- cuda_memtest.cu 2021-10-21 22:29:34.974957959 +0530 ++++ cuda_memtest_mod.cu 2021-10-21 22:30:48.198958856 +0530 @@ -168,7 +168,7 @@ { //create cuda mapped memory - hipHostMalloc((void**)&mappedHostPtr,tot_num_blocks* BLOCKSIZE,hipHostMallocMapped); + hipHostAlloc((void**)&mappedHostPtr,tot_num_blocks* BLOCKSIZE,hipHostMallocMapped); - hipHostGetDevicePointer(&ptr,mappedHostPtr,0); + hipHostGetDevicePointer((void **)&ptr,mappedHostPtr,0); } From d3438dd22155ef0e6be24c59be858b2cbe53f8c2 Mon Sep 17 00:00:00 2001 From: Rupam Chetia Date: Sun, 14 Nov 2021 23:08:01 +0530 Subject: [PATCH 15/18] MS5 activities: 1. Supporting Quicksilver Test on NVIDIA with HIP backend. 2. Supporting Laghos Test on NVIDIA with HIP backend. 3. Supporting Gridtools Test on NVIDIA with HIP backend. Change-Id: I91090c9e2a3a2d0121c1e542e7dbb1424d30183b --- cfg.py | 11 +- src/hiptestsuite/Test.py | 8 +- .../hpc_apps/gridtools/gridtools.patch | 22 +++ .../hpc_apps/gridtools/gridtools.py | 17 +- .../hpc_apps/gridtools/gridtools_build_amd.py | 5 +- .../gridtools/gridtools_build_nvidia.py | 110 ++++++++++++ .../hpc_apps/laghos/hip_on_nvcc.patch | 44 +++++ .../hpc_apps/laghos/laghos-multinode.patch | 27 +++ .../applications/hpc_apps/laghos/laghos.py | 17 +- .../hpc_apps/laghos/laghos_build_amd.py | 9 +- .../hpc_apps/laghos/laghos_build_nvidia.py | 164 ++++++++++++++++++ .../hpc_apps/quicksilver/qs_diff_patch_nvidia | 32 ++++ .../hpc_apps/quicksilver/quicksilver.py | 12 +- .../quicksilver/quicksilver_build_amd.py | 28 ++- .../quicksilver/quicksilver_build_nvidia.py | 82 +++++++++ .../quicksilver/quicksilver_diff_patch | 46 ----- .../quicksilver/quicksilver_parser_common.py | 4 +- 17 files changed, 542 insertions(+), 96 deletions(-) create mode 100644 src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.patch create mode 100644 src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_nvidia.py create mode 100644 src/hiptestsuite/applications/hpc_apps/laghos/hip_on_nvcc.patch create mode 100644 src/hiptestsuite/applications/hpc_apps/laghos/laghos-multinode.patch create mode 100644 src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_nvidia.py create mode 100644 src/hiptestsuite/applications/hpc_apps/quicksilver/qs_diff_patch_nvidia create mode 100644 src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_build_nvidia.py delete mode 100644 src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_diff_patch diff --git a/cfg.py b/cfg.py index e3c00e9..8f2315d 100644 --- a/cfg.py +++ b/cfg.py @@ -42,7 +42,8 @@ ROCM_PATH = None # None/offload target -build_for_target = None +build_for_gfx_target = None +build_for_cuda_target = "compute_70" # -I. # None/List of paths @@ -107,11 +108,11 @@ "quicksilver": { "repo_url": "https://github.com/LLNL/Quicksilver.git", "branch": "AMD-HIP", - "commit_id": "bf073887bf73ef34de8025adaba51c6ad7fb15be" + "commit_id": "3eddfc36003de27ee404499923b18e04efef8dad" }, "gridtools": { "repo_url": "https://github.com/GridTools/gridtools.git", - "branch": None, + "branch": "v1.1.3", "commit_id": "d33fa6fecee0a7bd9e080212c1038f0dbd31fe97" }, "gtbench": { @@ -126,8 +127,8 @@ }, "mfem": { "repo_url": "https://github.com/mfem/mfem.git ./mfem", - "branch": None, - "commit_id": "a3f0a5bb7ca874ec260d6f85afa3693cd6542497" + "branch": "amd", + "commit_id": "1ded8554ea470e2018284a881594b888b938ed0b" }, "Laghos": { "repo_url": "https://github.com/CEED/Laghos.git", diff --git a/src/hiptestsuite/Test.py b/src/hiptestsuite/Test.py index d816a10..a580665 100644 --- a/src/hiptestsuite/Test.py +++ b/src/hiptestsuite/Test.py @@ -85,7 +85,8 @@ def loadConfig(self): class CompileData(AMDObject): def __init__(self): AMDObject.__init__(self) - self.build_for_target: Union[None, Set[Target]] = None + self.build_for_gfx_target: Union[None, Set[Target]] = None + self.build_for_cuda_target: Union[None, Set[Target]] = None class GitData(AMDObject): @@ -145,7 +146,7 @@ def __init__(self): self.CUDA_PATH: Union[None, str] = None self.ROCM_PATH: Union[None, str] = None - # if self.build_for_target, then --offload_arch= + # if self.build_for_gfx_target, then --offload_arch= # -I ./ self.includes_path: Union[None, List[str]] = None @@ -193,7 +194,8 @@ def loadConfig(self): self.ROCM_PATH = self.config.ROCM_PATH self.CUDA_PATH = self.config.CUDA_PATH - self.build_for_target = self.config.build_for_target + self.build_for_gfx_target = self.config.build_for_gfx_target + self.build_for_cuda_target = self.config.build_for_cuda_target self.includes_path = self.config.includes_path self.link_libs = self.config.link_libs self.link_libs_path = self.config.link_libs_path diff --git a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.patch b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.patch new file mode 100644 index 0000000..b18427f --- /dev/null +++ b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.patch @@ -0,0 +1,22 @@ +diff --git a/cmake/definitions.cmake b/cmake/definitions.cmake +index 8c49b56d..cd3743bd 100644 +--- a/cmake/definitions.cmake ++++ b/cmake/definitions.cmake +@@ -61,6 +61,17 @@ if(CUDA_AVAILABLE) + elseif(GT_CUDA_COMPILATION_TYPE MATCHES "HIPCC-AMDGPU") + set(hipcc_options_ -xhip --amdgpu-target=${GT_CUDA_ARCH}) + target_compile_options(gridtools INTERFACE $<$:${hipcc_options_}>) ++ target_compile_definitions(gridtools INTERFACE GT_USE_HIP) ++ elseif(GT_CUDA_COMPILATION_TYPE MATCHES "HIPCC-NVCC") ++ ++ # allow to call constexpr __host__ from constexpr __device__, e.g. call std::max in constexpr context ++ target_compile_options(gridtools INTERFACE ++ $<$:--expt-relaxed-constexpr>) ++ ++ if(${GT_CXX_STANDARD} STREQUAL "c++17") ++ message(FATAL_ERROR "c++17 is not supported for CUDA compilation") ++ endif() ++ + target_compile_definitions(gridtools INTERFACE GT_USE_HIP) + endif() + diff --git a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.py b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.py index f5c128b..c3d101a 100644 --- a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.py +++ b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools.py @@ -23,6 +23,7 @@ from typing import Union, List from hiptestsuite.test_classifier import TestClassifier from hiptestsuite.applications.hpc_apps.gridtools.gridtools_build_amd import BuildRunAmd +from hiptestsuite.applications.hpc_apps.gridtools.gridtools_build_nvidia import BuildRunNvidia from hiptestsuite.common.hip_get_packages import HipPackages from hiptestsuite.common.hip_shell import execshellcmd @@ -75,10 +76,12 @@ def downloadtest(self, logFile, test_data: HIPTestData): self.gtbenchbranch, self.gtbenchcommitId, "gtbench") return ret - def buildtest(self, logFile, platform): + def buildtest(self, logFile, platform, cuda_target): isBinaryPresent = True if platform == HIP_PLATFORM.amd: self.prepareobj = BuildRunAmd(self.thistestpath, logFile) + elif platform == HIP_PLATFORM.nvidia: + self.prepareobj = BuildRunNvidia(self.thistestpath, logFile, cuda_target) else: print("Invalid Platform") return False @@ -133,10 +136,6 @@ def clean(self): def test(self, test_data: HIPTestData): print("=============== Gridtool Convergence Test ===============") - if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: - print("Gridtool Convergence Test is not supported on NVIDIA") - test_data.test_result = TestResult.SKIP - return # Check if Boost package exists if not os.path.isfile(\ os.path.join(self.thistestpath, "boost_1_72_0.tar.bz2")): @@ -164,7 +163,7 @@ def test(self, test_data: HIPTestData): if not res: test_data.test_result = TestResult.FAIL return - res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, test_data.build_for_cuda_target) if not res: test_data.test_result = TestResult.FAIL return @@ -196,10 +195,6 @@ def clean(self): def test(self, test_data: HIPTestData): print("=============== Gridtool Perf Test ===============") - if test_data.HIP_PLATFORM == HIP_PLATFORM.nvidia: - print("Gridtool Perf Test is not supported on NVIDIA") - test_data.test_result = TestResult.SKIP - return # Check if Boost package exists if not os.path.isfile(\ os.path.join(self.thistestpath, "boost_1_72_0.tar.bz2")): @@ -230,7 +225,7 @@ def test(self, test_data: HIPTestData): if not res: test_data.test_result = TestResult.FAIL return - res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + res = self.buildtest(testLogger, test_data.HIP_PLATFORM, test_data.build_for_cuda_target) if not res: test_data.test_result = TestResult.FAIL return diff --git a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py index ce80af5..34684e1 100644 --- a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py +++ b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py @@ -33,7 +33,6 @@ def setenv(self, gpu_arch): env = "export HIP_PLATFORM=`/opt/rocm/bin/hipconfig --platform`;" env += "export GT_CUDA_COMPILATION_TYPE=HIPCC-AMDGPU;" env += "export HCC_AMDGPU_TARGET=\""+ gpu_arch +"\";" - env += "export CXX=/opt/rocm/bin/hipcc;" env += "export ROCMHOME=/opt/rocm;" env += "export PATH=$ROCMHOME/bin:$ROCMHOME/llvm/bin:$ROCMHOME/hip/bin:$ROCMHOME/opencl/bin:$ROCMHOME/rocprofiler/bin:$PATH;" env += "export LD_LIBRARY_PATH=/$ROCMHOME/lib:$ROCMHOME/llvm/lib:$LD_LIBRARY_PATH;" @@ -72,7 +71,8 @@ def buildtest(self): if not os.path.exists(os.path.join(self.thistestpath, "GridTools/gridtools/build")): print("Building and Installing GridTools..") cmdexc = self.setenv(gpuarch) - cmdexc += "cd $GT_TREE_DIR;cd $GRIDTOOLS_TREE_DIR;mkdir -p $GRIDTOOLS_BUILD_DIR;cd $GRIDTOOLS_BUILD_DIR;" + cmdexc += "export CXX=/opt/rocm/bin/hipcc;" + cmdexc += "cd $GT_TREE_DIR;cd $GRIDTOOLS_TREE_DIR;git apply ../../gridtools.patch;mkdir -p $GRIDTOOLS_BUILD_DIR;cd $GRIDTOOLS_BUILD_DIR;" cmdexc += "CXX=/opt/rocm/bin/hipcc cmake .. -DBUILD_TESTING=OFF -DBoost_INCLUDE_DIR=$BOOST_INSTALL_DIR/include " +\ "-DGT_CUDA_COMPILATION_TYPE=$GT_CUDA_COMPILATION_TYPE -DGT_CUDA_ARCH=" + gpuarch + " " +\ "-DGT_ENABLE_BACKEND_CUDA=ON -DGT_ENABLE_BACKEND_MC=OFF -DGT_ENABLE_BACKEND_X86=OFF -DGT_ENABLE_BACKEND_NAIVE=OFF " +\ @@ -89,6 +89,7 @@ def buildtest(self): if not os.path.exists(os.path.join(self.thistestpath, "GridTools/gtbench/build")): print("Building and Installing Gtbench..") cmdexc = self.setenv(gpuarch) + cmdexc += "export CXX=/opt/rocm/bin/hipcc;" cmdexc += "cd $GT_TREE_DIR;cd $GTBENCH_TREE_DIR;git apply ../../gtbench.patch;mkdir -p $GTBENCH_BUILD_DIR;cd $GTBENCH_BUILD_DIR;" cmdexc += "CXX=/opt/rocm/bin/hipcc cmake .. -DGridTools_DIR=$GRIDTOOLS_INSTALL_DIR/lib/cmake -DGTBENCH_BACKEND=cuda " +\ "-DGTBENCH_RUNTIME=single_node -DCMAKE_CXX_FLAGS=-D__HIPCC__ -DBoost_INCLUDE_DIR=$BOOST_INSTALL_DIR/include;" diff --git a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_nvidia.py b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_nvidia.py new file mode 100644 index 0000000..84a0524 --- /dev/null +++ b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_nvidia.py @@ -0,0 +1,110 @@ +# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +import tempfile +from hiptestsuite.common.hip_shell import * +from hiptestsuite.applications.hpc_apps.gridtools.gridtools_parser_common import GridtoolsParser + +class BuildRunNvidia(): + def __init__(self, thistestpath, logFile, cuda_target): + self.thistestpath = thistestpath + self.logFile = logFile + self.runlog = "" + self.cuda_target = cuda_target + + def setenv(self): + env = "export HIP_PLATFORM=nvidia; export HIP_COMPILER=nvcc; export HIP_RUNTIME=cuda;" + env += "export GT_CUDA_COMPILATION_TYPE=HIPCC-NVCC;" + env += "export GT_ALL_DIR=$PWD/src/hiptestsuite/applications/hpc_apps/gridtools;" + env += "export GT_TREE_DIR=$GT_ALL_DIR/GridTools;" + env += "export BOOST_TREE_DIR=$GT_TREE_DIR/boost_1_72_0;" + env += "export BOOST_INSTALL_DIR=$GT_ALL_DIR/boost_1_72_0;" + env += "export GRIDTOOLS_TREE_DIR=$GT_TREE_DIR/gridtools;" + env += "export GRIDTOOLS_BUILD_DIR=$GRIDTOOLS_TREE_DIR/build;" + env += "export GRIDTOOLS_INSTALL_DIR=$GT_ALL_DIR/gridtools;" + env += "export GTBENCH_TREE_DIR=$GT_TREE_DIR/gtbench;" + env += "export GTBENCH_BUILD_DIR=$GTBENCH_TREE_DIR/build;" + return env + + def buildtest(self): + if not os.path.exists(os.path.join(self.thistestpath, "boost_1_72_0")): + print("Building and Installing Boost..") + cmdexc = self.setenv() + cmdexc += "cd $GT_TREE_DIR;" + cmdexc += "tar -xvjf ../boost_1_72_0.tar.bz2;cd $BOOST_TREE_DIR;" + cmdexc += "./bootstrap.sh --prefix=$BOOST_INSTALL_DIR --with-python=python3;" + cmdexc += "./b2 install -j8 threading=multi link=shared;" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, runlogdump, None) + runlogdump.close() + else: + print("Boost already installed..") + # Build Gridtools + if not os.path.exists(os.path.join(self.thistestpath, "GridTools/gridtools/build")): + print("Building and Installing GridTools..") + cmdexc = self.setenv() + cmdexc += "export CXX=/opt/rocm/bin/hipcc;" + cmdexc += "cd $GT_TREE_DIR;cd $GRIDTOOLS_TREE_DIR;git apply ../../gridtools.patch;mkdir -p $GRIDTOOLS_BUILD_DIR;cd $GRIDTOOLS_BUILD_DIR;" + cmdexc += "CXX=/opt/rocm/bin/hipcc cmake .. -DBUILD_TESTING=OFF -DBoost_INCLUDE_DIR=$BOOST_INSTALL_DIR/include " +\ + "-DGT_CUDA_COMPILATION_TYPE=$GT_CUDA_COMPILATION_TYPE -DGT_CUDA_ARCH=" + self.cuda_target + " " +\ + "-DGT_ENABLE_BACKEND_CUDA=ON -DGT_ENABLE_BACKEND_MC=OFF -DGT_ENABLE_BACKEND_X86=OFF -DGT_ENABLE_BACKEND_NAIVE=OFF " +\ + "-DGT_USE_MPI=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$GRIDTOOLS_INSTALL_DIR;" + cmdexc += "make -j;" + cmdexc += "make install;" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, runlogdump, None) + runlogdump.close() + else: + print("GridTools already built..") + + # Build Gtbench + if not os.path.exists(os.path.join(self.thistestpath, "GridTools/gtbench/build")): + print("Building and Installing Gtbench..") + cmdexc = self.setenv() + cmdexc += "export CXX=/opt/rocm/bin/hipcc;" + cmdexc += "cd $GT_TREE_DIR;cd $GTBENCH_TREE_DIR;git apply ../../gtbench.patch;mkdir -p $GTBENCH_BUILD_DIR;cd $GTBENCH_BUILD_DIR;" + cmdexc += "CXX=/opt/rocm/bin/hipcc cmake .. -DGridTools_DIR=$GRIDTOOLS_INSTALL_DIR/lib/cmake -DGTBENCH_BACKEND=cuda " +\ + "-DGTBENCH_RUNTIME=single_node -DCMAKE_CXX_FLAGS=--expt-relaxed-constexpr -DBoost_INCLUDE_DIR=$BOOST_INSTALL_DIR/include;" + cmdexc += "make CFLAGS=--expt-relaxed-constexpr CXXFLAGS=--expt-relaxed-constexpr -j8;" + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, runlogdump, None) + runlogdump.close() + else: + print("Gtbench already built..") + + return True + + def runtest(self, testnum): + cmdcd = "cd " + os.path.join(self.thistestpath, "GridTools/gtbench/build") + ";" + if testnum == 0: + print("Testing Convergence Test") + cmdrun = "./convergence_tests;" + elif testnum == 1: + print("Testing Performance Test") + cmdrun = "./benchmark --domain-size 256 256 --runs 100;" + cmdexc = cmdcd + cmdrun + self.runlog = execshellcmd(cmdexc, self.logFile, None) + + def clean(self): + print("Cleaning Gridtools..") + + def parse_result(self, testnum): + return GridtoolsParser(self.runlog).parse(testnum) diff --git a/src/hiptestsuite/applications/hpc_apps/laghos/hip_on_nvcc.patch b/src/hiptestsuite/applications/hpc_apps/laghos/hip_on_nvcc.patch new file mode 100644 index 0000000..835cb81 --- /dev/null +++ b/src/hiptestsuite/applications/hpc_apps/laghos/hip_on_nvcc.patch @@ -0,0 +1,44 @@ +diff --git a/general/error.hpp b/general/error.hpp +index 957aef944..a59cee0a3 100644 +--- a/general/error.hpp ++++ b/general/error.hpp +@@ -150,16 +150,20 @@ void mfem_warning(const char *msg = NULL); + + // Additional abort functions for HIP + #if defined(MFEM_USE_HIP) +-template +-__host__ void abort_msg(T & msg) +-{ +- MFEM_ABORT(msg); +-} ++//template ++//__host__ void abort_msg(T & msg) ++//{ ++// MFEM_ABORT(msg); ++//} + + template +-__device__ void abort_msg(T & msg) ++__host__ __device__ void abort_msg(T & msg) + { ++#ifndef __HIP_DEVICE_COMPILE_ ++ MFEM_ABORT(msg); ++#else + abort(); ++#endif + } + #endif + +diff --git a/general/hip.hpp b/general/hip.hpp +index 9ad804fff..62d8ed625 100644 +--- a/general/hip.hpp ++++ b/general/hip.hpp +@@ -20,7 +20,7 @@ + + #ifdef MFEM_USE_HIP + #define MFEM_DEVICE __device__ +-#define MFEM_LAMBDA __host__ __device__ ++#define MFEM_LAMBDA __host__ + #define MFEM_HOST_DEVICE __host__ __device__ + #define MFEM_DEVICE_SYNC MFEM_GPU_CHECK(hipDeviceSynchronize()) + #define MFEM_STREAM_SYNC MFEM_GPU_CHECK(hipStreamSynchronize(0)) diff --git a/src/hiptestsuite/applications/hpc_apps/laghos/laghos-multinode.patch b/src/hiptestsuite/applications/hpc_apps/laghos/laghos-multinode.patch new file mode 100644 index 0000000..8f6981c --- /dev/null +++ b/src/hiptestsuite/applications/hpc_apps/laghos/laghos-multinode.patch @@ -0,0 +1,27 @@ +diff --git a/laghos.cpp b/laghos.cpp +index cfcb0ed..bee1447 100644 +--- a/laghos.cpp ++++ b/laghos.cpp +@@ -201,6 +201,7 @@ int main(int argc, char *argv[]) + if (mpi.Root()) { args.PrintOptions(cout); } + + // Configure the device from the command line options ++ dev=myid; + Device backend; + backend.Configure(device, dev); + if (mpi.Root()) { backend.Print(); } +diff --git a/laghos_solver.cpp b/laghos_solver.cpp +index 2a04477..255ce57 100644 +--- a/laghos_solver.cpp ++++ b/laghos_solver.cpp +@@ -689,6 +689,10 @@ void LagrangianHydroOperator::PrintTimingData(bool IamRoot, int steps, + cout << "Major kernels total time (seconds): " << T[4] << endl; + cout << "Major kernels total rate (megadofs x time steps / second): " + << FOM << endl; ++ cout <<" "<< endl; ++ cout <<" FOM= "< - #include - #ifdef HAVE_HIP -- #include -+ #include - #endif - - -diff -Naur ../Quicksilver_orig/src/Makefile src/Makefile ---- ../Quicksilver_orig/src/Makefile 2021-09-24 16:48:20.974111483 +0530 -+++ src/Makefile 2021-09-24 17:10:19.804984064 +0530 -@@ -114,20 +114,20 @@ - ##################################################################################### - #hip, no MPI - ##################################################################################### --#CXX = $(ROCM_PATH)/bin/hipcc --#CXXFLAGS = -I$(ROCM_PATH)/include/ --#CPPFLAGS = -DHAVE_HIP=1 -DMaxIt=15 --#LDFLAGS = -L$(ROCM_PATH)/lib -L$(ROCM_PATH)/lib -+CXX = $(ROCM_PATH)/bin/hipcc -+CXXFLAGS = -I$(ROCM_PATH)/include/ -+CPPFLAGS = -DHAVE_HIP=1 -DMaxIt=15 -+LDFLAGS = -L$(ROCM_PATH)/lib -L$(ROCM_PATH)/lib - - ############################################################################ - #hip + mpi - ############################################################################# --CXX = $(HIP)/bin/hipcc --CXXFLAGS1 = -I$(HIP)/include/ --CXXFLAGS2 = $(CXXFLAGS1) -I$(MPIPATH)/include --CXXFLAGS = $(CXXFLAGS2) -pthread --CPPFLAGS = -DHAVE_HIP=1 -DHAVE_MPI -DMaxIt=15 --LDFLAGS = -L$(HIP)/lib -L$(MPIPATH)/lib -lmpi -+#CXX = $(HIP)/bin/hipcc -+#CXXFLAGS1 = -I$(HIP)/include/ -+#CXXFLAGS2 = $(CXXFLAGS1) -I$(MPIPATH)/include -+#CXXFLAGS = $(CXXFLAGS2) -pthread -+#CPPFLAGS = -DHAVE_HIP=1 -DHAVE_MPI -DMaxIt=15 -+#LDFLAGS = -L$(HIP)/lib -L$(MPIPATH)/lib -lmpi - ######################################################################## - #hip + nvcc - ###################################################################### diff --git a/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py b/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py index b5c9289..db35dda 100644 --- a/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py +++ b/src/hiptestsuite/applications/hpc_apps/quicksilver/quicksilver_parser_common.py @@ -18,12 +18,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from hiptestsuite.common.hip_shell import execshellcmd import re class QuicksilverParser(): def __init__(self, results): - self.results = results + results.seek(0) + self.results = results.read() def parse(self): passed1 = False From 2626a5cbb4b36f42732678d3274770510e878930 Mon Sep 17 00:00:00 2001 From: Rupam Chetia Date: Tue, 30 Nov 2021 19:28:06 +0530 Subject: [PATCH 16/18] MS5 Activities 1. Adding support for AMD_LOG_LEVEL Change-Id: Ibdd312df79ad7a07ba8aabe41ee963c0ccdb83dd --- .../cuda_grep/cuda_grep_build_amd.py | 8 +- .../cuda_grep/cuda_grep_build_nvidia.py | 8 +- .../applications/cuda_memtest/cuda_memtest.cu | 568 ++++++++++++++++++ .../cuda_memtest/cuda_memtest_build_amd.py | 13 +- .../cuda_memtest/cuda_memtest_build_nvidia.py | 12 +- .../cuda_memtest_parser_common.py | 5 +- .../cuda_memtest/cuda_memtest_patch | 11 - .../hip_examples/hip_examples_build_amd.py | 3 +- .../hip_examples/hip_examples_build_common.py | 120 ++-- .../applications/hip_samples/hip_samples.py | 16 +- .../hip_samples/hip_samples_build_amd.py | 1 - .../hip_samples/hip_samples_build_common.py | 2 +- .../hip_samples/hip_samples_build_nvidia.py | 3 +- .../hpc_apps/gridtools/gridtools_build_amd.py | 8 +- .../gridtools/gridtools_build_nvidia.py | 8 +- .../gridtools/gridtools_parser_common.py | 5 +- .../hpc_apps/kokkos/kokkos_build_amd.py | 8 +- .../hpc_apps/kokkos/kokkos_parser_common.py | 5 +- .../hpc_apps/laghos/laghos_build_amd.py | 8 +- .../hpc_apps/laghos/laghos_build_nvidia.py | 8 +- .../hpc_apps/laghos/laghos_parser_common.py | 5 +- .../keccaktreegpu/keccaktreegpu_build_amd.py | 10 +- .../keccaktreegpu_build_nvidia.py | 10 +- .../keccaktreegpu_parser_common.py | 4 +- .../applications/mgbench/mgbench_build_amd.py | 11 +- .../mgbench/mgbench_build_nvidia.py | 10 +- .../mgbench/mgbench_parser_common.py | 5 +- 27 files changed, 773 insertions(+), 102 deletions(-) create mode 100644 src/hiptestsuite/applications/cuda_memtest/cuda_memtest.cu delete mode 100644 src/hiptestsuite/applications/cuda_memtest/cuda_memtest_patch diff --git a/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_amd.py b/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_amd.py index 7f2e879..654c594 100644 --- a/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_amd.py +++ b/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_amd.py @@ -19,7 +19,8 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd +import tempfile +from hiptestsuite.common.hip_shell import * from hiptestsuite.applications.cuda_grep.cuda_grep_parser_common import CudaGrepParser class BuildRunAmd(): @@ -52,7 +53,10 @@ def buildtest(self): def runtest(self): print("Running cuda_grep..") cmdexc = "cd " + self.runpath + ";" + "./runtests.sh;" - execshellcmd(cmdexc, self.logFile, None) + envtoset = os.environ.copy() + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, runlogdump, envtoset) + runlogdump.close() def clean(self): print("Cleaning cuda_grep..") diff --git a/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_nvidia.py b/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_nvidia.py index 91ecb4c..d1211da 100644 --- a/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_nvidia.py +++ b/src/hiptestsuite/applications/cuda_grep/cuda_grep_build_nvidia.py @@ -19,7 +19,8 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd +import tempfile +from hiptestsuite.common.hip_shell import * from hiptestsuite.applications.cuda_grep.cuda_grep_parser_common import CudaGrepParser class BuildRunNvidia(): @@ -61,7 +62,10 @@ def runtest(self): print("Running cuda_grep..") env = self.getenvironmentvariables() cmdexc = "cd " + self.runpath + ";" + "./runtests.sh;" - execshellcmd(cmdexc, self.logFile, env) + runlogdump = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, runlogdump, env) + runlogdump.close() + def clean(self): print("Cleaning cuda_grep..") diff --git a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest.cu b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest.cu new file mode 100644 index 0000000..576a368 --- /dev/null +++ b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest.cu @@ -0,0 +1,568 @@ +/* + * Illinois Open Source License + * + * University of Illinois/NCSA + * Open Source License + * + * Copyright � 2009, University of Illinois. All rights reserved. + * + * Developed by: + * + * Innovative Systems Lab + * National Center for Supercomputing Applications + * http://www.ncsa.uiuc.edu/AboutUs/Directorates/ISL.html + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal with + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * * Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimers. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimers in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the names of the Innovative Systems Lab, the National Center for Supercomputing + * Applications, nor the names of its contributors may be used to endorse or promote products + * derived from this Software without specific prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS WITH THE SOFTWARE. + */ + +#include "misc.h" +#include +#include +#include +#include +#include +#include +#include + +#define MAX_NUM_GPUS 128 +bool useMappedMemory; +void* mappedHostPtr; +char hostname[64]; +unsigned int verbose =0; +unsigned int interactive =0; +extern cuda_memtest_t cuda_memtests[11]; +unsigned int max_num_blocks = 0; +unsigned int exit_on_error = 0; +unsigned int monitor_temp = 0; +unsigned int monitor_interval = 5; +unsigned int email_notification = 0; +unsigned int global_pattern = 0; +unsigned long global_pattern_long = 0; +char emails[128]; +unsigned int report_interval = 1800; //senconds +unsigned int num_iterations = 1000; +unsigned int num_passes = 0; +unsigned int healthy_threads = 0; +unsigned int disable_serial_number = 0; +__thread unsigned int gpu_idx; +char driver_info[MAX_STR_LEN]; + +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t atomic_mutex = PTHREAD_MUTEX_INITIALIZER; + +void run_tests(char*, unsigned int); +extern void update_temperature(void); +extern void allocate_small_mem(void); +volatile int active_update_temperature; + +typedef struct arg_s{ + unsigned int device; +}arg_t; + +void +display_device_info(struct hipDeviceProp_t* prop) +{ +#if !defined(NVML_DEVICE_SERIAL_BUFFER_SIZE) + char devSerialNum[] = "unknown (no NVML found)"; +#else + char devSerialNum[NVML_DEVICE_SERIAL_BUFFER_SIZE]; + get_serial_number( gpu_idx, devSerialNum ); +#endif + + PRINTF("Device name=%s, global memory size=%zu, serial=%s\n", prop->name, prop->totalGlobalMem, devSerialNum); + return; +} + + +void +atomic_inc(unsigned int* value) +{ + pthread_mutex_lock(&atomic_mutex); + (*value)= (*value) + 1; + pthread_mutex_unlock(&atomic_mutex); +} + +unsigned int +atomic_read(unsigned int* value) +{ + unsigned int ret; + + pthread_mutex_lock(&atomic_mutex); + ret = *value; + pthread_mutex_unlock(&atomic_mutex); + + return ret; +} + +void* +thread_func(void* _arg) +{ + + arg_t* arg = (arg_t*)_arg; + unsigned int device = arg->device; + gpu_idx = device; + + + + hipDeviceProp_t prop; + hipGetDeviceProperties(&prop, device); CUERR; + + display_device_info(&prop); + + unsigned long totmem = prop.totalGlobalMem; + + PRINTF("major=%d, minor=%d\n", prop.major, prop.minor); + + //need to leave a little headroom or later calls will fail + unsigned int tot_num_blocks = totmem/BLOCKSIZE -16; + if (max_num_blocks != 0){ + tot_num_blocks = MIN(max_num_blocks+16, tot_num_blocks); + } + + + hipSetDevice(device); + hipDeviceSynchronize(); + CUERR; + + PRINTF("Attached to device %d successfully.\n", device); + + size_t free, total; + hipMemGetInfo(&free, &total); + + allocate_small_mem(); + + char* ptr = NULL; + + tot_num_blocks = MIN(tot_num_blocks, free/BLOCKSIZE - 16); + do{ + tot_num_blocks -= 16 ; //magic number 16 MB + DEBUG_PRINTF("Trying to allocate %d MB\n", tot_num_blocks); + if (tot_num_blocks <= 0){ + FPRINTF("ERROR: cannot allocate any memory from GPU\n"); + exit(ERR_GENERAL); + } + if(useMappedMemory) + { + //create cuda mapped memory + hipHostAlloc((void**)&mappedHostPtr,tot_num_blocks* BLOCKSIZE,hipHostMallocMapped); + hipHostGetDevicePointer((void **)&ptr,mappedHostPtr,0); + } + else + { + hipMalloc((void**)&ptr, tot_num_blocks* BLOCKSIZE); + } + }while(hipGetLastError() != hipSuccess); + + PRINTF("Allocated %d MB\n", tot_num_blocks); + + atomic_inc(&healthy_threads); + run_tests(ptr, tot_num_blocks); + + return NULL; + +} + + +void* +temp_monitor_thread_func(void*) +{ + do{ + update_temperature(); + sleep(monitor_interval); + }while(active_update_temperature); + return NULL; +} + + +void list_tests_info(void) +{ + size_t i; + for (i = 0;i < DIM(cuda_memtests); i++){ + printf("%s %s\n", cuda_memtests[i].desc, cuda_memtests[i].enabled?"":" ==disabled by default=="); + } + return; +} + + +void +usage(char** argv) +{ + + char example_usage[] = + "run on default setting: ./cuda_memtest\n" + "run on stress test only: ./cuda_memtest --stress\n"; + + printf("Usage:%s [options]\n", argv[0]); + printf("options:\n"); + printf("--mappedMem run all checks with cuda mapped memory instead of native device memory\n"); + printf("--silent Do not print out progress message (default)\n"); + printf("--device Designate one device for test\n"); + printf("--interactive Progress info will be printed in the same line\n"); + printf("--disable_all Disable all tests\n"); + printf("--enable_test Enable the test \n"); + printf("--disable_test Disable the test \n"); + printf("--max_num_blocks Set the maximum of blocks of memory to test\n"); + printf(" 1 block = 1 MB in here\n"); + printf("--exit_on_error When finding error, print error message and exit\n"); + printf("--monitor_temp Monitoring temperature, the temperature will be updated every seconds\n"); + printf(" This feature is experimental\n"); + printf("--emails Setting email notification\n"); + printf("--report_interval Setting the interval in seconds between email notifications(default 1800)\n"); + printf("--pattern Manually set test pattern for test4/test8/test10\n"); + printf("--list_tests List all test descriptions\n"); + printf("--num_iterations Set the number of iterations (only effective on test0 and test10)\n"); + printf("--num_passes Set the number of test passes (this affects all tests)\n"); + printf("--verbose Setting verbose level\n"); + printf(" 0 -- Print out test start and end message only (default)\n"); + printf(" 1 -- Print out pattern messages in test\n"); + printf(" 2 -- Print out progress messages\n"); + printf("--stress Stress test. Equivalent to --disable_all --enable_test 10 --exit_on_error\n"); + printf("--help Print this message\n"); + printf("\nExample usage:\n\n"); + printf("%s\n", example_usage); + + exit(ERR_GENERAL); +} + + +int +main(int argc, char** argv) +{ + int i; + useMappedMemory=false; + mappedHostPtr=NULL; + + if (argc >=2 ){ + if( strcmp(argv[1], "--help")== 0){ + usage(argv); + } + } + + if(gethostname(hostname, 64) !=0){ + fprintf(stderr, "ERROR: gethostname() returns error\n"); + exit(ERR_GENERAL); + } + + for(i=0;i < 64; i++){ + if (hostname[i] == '.'){ + hostname[i] = 0; + break; + } + } + + PRINTF("Running cuda memtest, version %s\n", VERSION); + int device = -1; + int num_gpus; + hipGetDeviceCount(&num_gpus);CUERR; + + if (num_gpus == 0){ + fprintf(stderr,"ERROR: no GPUs found\n"); + exit(ERR_GENERAL); + } + + + for (i =1;i < argc; i++){ + + if( strcmp(argv[i], "--help")== 0){ + usage(argv); + } + + if( strcmp(argv[i], "--mappedMem")== 0){ + useMappedMemory=true; + continue; + } + + if( strcmp(argv[i], "--verbose") == 0){ + if (i+1 >= argc){ + usage(argv); + } + verbose = atoi(argv[i+1]); + i++; + continue; + + } + if (strcmp(argv[i], "--silent") == 0){ + verbose = 0; + continue; + } + if (strcmp(argv[i], "--interactive") == 0){ + interactive = 1; + continue; + } + if (strcmp(argv[i], "--noninteractive") == 0){ + interactive = 0; + continue; + } + if (strcmp(argv[i], "--enable_test") == 0){ + if (i+1 >= argc){ + usage(argv); + } + size_t idx = atoi(argv[i+1]); + if (idx >= DIM(cuda_memtests)){ + fprintf(stderr, "Error: invalid test id\n"); + usage(argv); + } + + cuda_memtests[idx].enabled = 1; + + i++; + continue; + } + if (strcmp(argv[i], "--disable_test") == 0){ + if (i+1 >= argc){ + usage(argv); + } + size_t idx = atoi(argv[i+1]); + if (idx >= DIM(cuda_memtests)){ + fprintf(stderr, "Error: invalid test id\n"); + usage(argv); + } + + cuda_memtests[idx].enabled = 0; + i++; + continue; + } + if (strcmp(argv[i], "--disable_all") == 0){ + size_t k; + for (k=0;k < DIM(cuda_memtests);k++){ + cuda_memtests[k].enabled = 0; + } + continue; + } + + if (strcmp(argv[i], "--device") == 0){ + if (i+1 >= argc){ + usage(argv); + } + device = atoi(argv[i+1]); + i++; + num_gpus = 1; + continue; + } + + if (strcmp(argv[i], "--max_num_blocks") == 0){ + if (i+1 >= argc){ + usage(argv); + } + max_num_blocks = atoi(argv[i+1]); + i++; + continue; + } + + if (strcmp(argv[i], "--exit_on_error") == 0){ + exit_on_error = 1; + continue; + } + + if (strcmp(argv[i], "--monitor_temp") == 0){ + if( ENABLE_NVML != 1 ) + { + printf("ERROR: --monitor_temp requires NVML (not found)\n\n"); + usage(argv); + } + monitor_temp =1; + if (i+1 >= argc){ + usage(argv); + } + monitor_interval = atoi(argv[i+1]); + i++; + continue; + } + if (strcmp(argv[i], "--pattern") == 0){ + if (i+1 >= argc){ + usage(argv); + } + sscanf(argv[i+1], "0x%lx", &global_pattern_long); + if (global_pattern_long ==0){ + printf("ERROR: global test pattern cannot be zero\n"); + usage(argv); + } + printf("Using global test pattern: 0x%lx\n", global_pattern_long); + global_pattern = (unsigned long)global_pattern_long; + i++; + continue; + } + if (strcmp(argv[i], "--emails") == 0){ + email_notification =1; + + struct stat statbuf; + if (stat(MAILFILE, &statbuf)!=0){ + fprintf(stderr, "ERROR: stating mail unitility(%s) failed\n", MAILFILE); + usage(argv); + } + + if( !(S_IXOTH & statbuf.st_mode)){ + fprintf(stderr, "ERROR: no permission on exeution on the mail utility\n"); + usage(argv); + } + + + if (i+1 >= argc){ + usage(argv); + } + if ( strlen( argv[i+1]) > sizeof(emails)){ + fprintf(stderr, "ERROR: email string too long\n"); + usage(argv); + } + strcpy(emails, argv[i+1]); + i++; + continue; + } + if (strcmp(argv[i], "--report_interval") == 0){ + + if (i+1 >= argc){ + usage(argv); + } + report_interval = atoi(argv[i+1]); + i++; + continue; + } + + if (strcmp(argv[i], "--num_iterations") == 0){ + + if (i+1 >= argc){ + usage(argv); + } + num_iterations = atoi(argv[i+1]); + if (num_iterations <= 0){ + printf("ERROR: invalid number of iterations\n"); + usage(argv); + } + i++; + continue; + } + + if (strcmp(argv[i], "--num_passes") == 0){ + + if (i+1 >= argc){ + usage(argv); + } + num_passes = atoi(argv[i+1]); + if (num_passes <= 0){ + printf("ERROR: invalid number of passes\n"); + usage(argv); + } + i++; + continue; + } + + if (strcmp(argv[i], "--disable_serial_number") == 0){ + printf("DEPRECATED: --disable_serial_number is ignored\n"); + continue; + } + + if (strcmp(argv[i], "--stress") == 0){ + //equal to "--disable_all --enable_test 10 --exit_on_error" + size_t k; + for (k=0;k < DIM(cuda_memtests);k++){ + cuda_memtests[k].enabled = 0; + } + cuda_memtests[10].enabled = 1; + exit_on_error = 1; + continue; + } + + if (strcmp(argv[i], "--list_tests") == 0){ + list_tests_info(); + return 0; + } + fprintf(stderr, "ERROR: Invalid option:%s\n", argv[i]); + usage(argv); + } + + get_driver_info(driver_info, MAX_STR_LEN); + + PRINTF("num_gpus=%d\n", num_gpus); + if(num_gpus > MAX_NUM_GPUS){ + fprintf(stderr, "Error: max number of GPUs (%d) exceeded: %d\n", MAX_NUM_GPUS, num_gpus); + exit(ERR_GENERAL); + } + pthread_t temp_pid; + if (monitor_temp){ + active_update_temperature = 1; + if (pthread_create(&temp_pid, NULL, temp_monitor_thread_func, NULL) != 0){ + printf("ERROR: creating thread for temperature monitoring failed\n"); + exit(ERR_GENERAL); + } + } + +#if (ENABLE_NVML==1) + NVML_CHECK(nvmlInit()); +#endif + + arg_t args[MAX_NUM_GPUS]; + pthread_t pid[MAX_NUM_GPUS]; + + if (device != -1){ //device set, only 1 GPU + args[0].device = device; + pthread_create(&pid[0], NULL, thread_func, (void*)&args[0]); + }else{//multiple GPUs + for (i=0;i < num_gpus;i++){ + args[i].device = i; + pthread_create(&pid[i], NULL, thread_func, (void*)&args[i]); + } + + } + + struct timeval t0, t1; + int ht=0; + double wait_time = 500; + gettimeofday(&t0, NULL); + + while(1){ + ht = atomic_read(&healthy_threads); + if (ht == num_gpus){ + break; + } + + gettimeofday(&t1, NULL); + double passed_time = TDIFF(t1, t0); + if (passed_time >= wait_time){ + break; + } + sleep(1); + } + + if (ht < num_gpus){ + printf("ERROR: Some GPU threads are not progressing (healthy_threads=%d, num_gpus=%d)\n", ht, num_gpus); + fflush(stdout); fflush(stderr); + for(i=0;i < num_gpus;i++){ + pthread_kill(pid[i], SIGTERM); + } + exit(ERR_BAD_STATE); + } + + active_update_temperature = 0; + + for(i=0;i < num_gpus;i++){ + pthread_join(pid[i], NULL); + } + + printf("main thread: Program exits\n"); + + return 0; +} + diff --git a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_amd.py b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_amd.py index 05bb539..f3c5aa9 100644 --- a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_amd.py +++ b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_amd.py @@ -19,14 +19,15 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd +import tempfile +from hiptestsuite.common.hip_shell import * from hiptestsuite.applications.cuda_memtest.cuda_memtest_parser_common import CudaMemtestParser class BuildRunAmd(): def __init__(self, thistestpath, logFile, binary): self.thistestpath = thistestpath self.logFile = logFile - self.runlog = "" + self.runlog = None self.binary = binary def buildtest(self): @@ -41,7 +42,7 @@ def buildtest(self): cmd_modify = "" if not os.path.isfile(os.path.join(self.thistestpath, "hipified")): cmd_hipify = "ls cuda_memtest.* misc.* tests.cu | xargs -t -I % sh -c '/opt/rocm/bin/hipify-perl % > hip_%; rm %; mv hip_% %;';" - cmd_modify = "patch -p0 < ../cuda_memtest_patch; touch hipified;" + cmd_modify = "cp ../cuda_memtest.cu .;" cmd_build = "/opt/rocm/bin/hipcc -DENABLE_NVML=0 cuda_memtest.cu misc.cpp tests.cu -o " + self.binary + ";" cmdexc = cmdcd + cmd_hipify + cmd_modify + cmd_build execshellcmd(cmdexc, self.logFile, None) @@ -52,13 +53,17 @@ def runtest(self, testnum): cmdcd = "cd " + self.thistestpath + ";" cmdrun = "./" + self.binary + " --disable_all --enable_test " + str(testnum) + " --num_passes 1" cmdexc = cmdcd + cmdrun - self.runlog = execshellcmd(cmdexc, self.logFile, None) + envtoset = os.environ.copy() + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, self.runlog, envtoset) def clean(self): print("Cleaning cuda_memtest..") cmdcd = "cd " + self.thistestpath + ";" cmdrm = "rm -f " + self.binary + ";" cmdexc = cmdcd + cmdrm + if self.runlog != None: + self.runlog.close() execshellcmd(cmdexc, None, None) def parse_result(self): diff --git a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py index 0070976..b21f521 100644 --- a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py +++ b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_build_nvidia.py @@ -19,14 +19,15 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd +import tempfile +from hiptestsuite.common.hip_shell import * from hiptestsuite.applications.cuda_memtest.cuda_memtest_parser_common import CudaMemtestParser class BuildRunNvidia(): def __init__(self, thistestpath, logFile, binary): self.thistestpath = thistestpath self.logFile = logFile - self.runlog = "" + self.runlog = None self.binary = binary def getenvironmentvariables(self): @@ -49,7 +50,7 @@ def buildtest(self): cmd_modify = "" if not os.path.isfile(os.path.join(self.thistestpath, "hipified")): cmd_hipify = "ls cuda_memtest.* misc.* tests.cu | xargs -t -I % sh -c '/opt/rocm/bin/hipify-perl % > hip_%; rm %; mv hip_% %;';" - cmd_modify = "patch -p0 < ../cuda_memtest_patch; touch hipified;" + cmd_modify = "cp ../cuda_memtest.cu .;" cmd_build = "/opt/rocm/bin/hipcc -DENABLE_NVML=0 cuda_memtest.cu misc.cpp tests.cu -o " + self.binary + ";" cmdexc = cmdcd + cmd_hipify + cmd_modify + cmd_build execshellcmd(cmdexc, self.logFile, env) @@ -61,13 +62,16 @@ def runtest(self, testnum): cmdcd = "cd " + self.thistestpath + ";" cmdrun = "./" + self.binary + " --disable_all --enable_test " + str(testnum) + " --num_passes 1" cmdexc = cmdcd + cmdrun - self.runlog = execshellcmd(cmdexc, self.logFile, env) + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, self.runlog, env) def clean(self): print("Cleaning cuda_memtest..") cmdcd = "cd " + self.thistestpath + ";" cmdrm = "rm -f " + self.binary + ";" cmdexc = cmdcd + cmdrm + if self.runlog != None: + self.runlog.close() execshellcmd(cmdexc, None, None) def parse_result(self): diff --git a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_parser_common.py b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_parser_common.py index 701cf38..4cf1454 100644 --- a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_parser_common.py +++ b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_parser_common.py @@ -18,12 +18,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from hiptestsuite.common.hip_shell import execshellcmd import re class CudaMemtestParser(): def __init__(self, results): - self.results = results + results.seek(0) + logbytes = results.read() + self.results = logbytes def parse(self): passed = False diff --git a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_patch b/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_patch deleted file mode 100644 index 7f6f823..0000000 --- a/src/hiptestsuite/applications/cuda_memtest/cuda_memtest_patch +++ /dev/null @@ -1,11 +0,0 @@ ---- cuda_memtest.cu 2021-10-21 22:29:34.974957959 +0530 -+++ cuda_memtest_mod.cu 2021-10-21 22:30:48.198958856 +0530 -@@ -168,7 +168,7 @@ - { - //create cuda mapped memory - hipHostAlloc((void**)&mappedHostPtr,tot_num_blocks* BLOCKSIZE,hipHostMallocMapped); -- hipHostGetDevicePointer(&ptr,mappedHostPtr,0); -+ hipHostGetDevicePointer((void **)&ptr,mappedHostPtr,0); - } - else - { diff --git a/src/hiptestsuite/applications/hip_examples/hip_examples_build_amd.py b/src/hiptestsuite/applications/hip_examples/hip_examples_build_amd.py index 3fe9155..149fd05 100644 --- a/src/hiptestsuite/applications/hip_examples/hip_examples_build_amd.py +++ b/src/hiptestsuite/applications/hip_examples/hip_examples_build_amd.py @@ -41,7 +41,8 @@ def runtest(self, logFile, testid): # In this function put the execution steps for test cases # which differ across platforms (amd/nvidia/intel) else # invoke BuildRunCommon.buildtest - ret = BuildRunCommon.runtest(self, logFile, testid) + envtoset = os.environ.copy() + ret = BuildRunCommon.runtest(self, logFile, testid, envtoset) return ret # Clean all generated binaries diff --git a/src/hiptestsuite/applications/hip_examples/hip_examples_build_common.py b/src/hiptestsuite/applications/hip_examples/hip_examples_build_common.py index 6292a28..becc790 100644 --- a/src/hiptestsuite/applications/hip_examples/hip_examples_build_common.py +++ b/src/hiptestsuite/applications/hip_examples/hip_examples_build_common.py @@ -32,7 +32,6 @@ class BuildRunCommon(): def __init__(self, path): self.thistestpath = path self.runlogdump = tempfile.TemporaryFile("w+") - self.runlog = "" self.genbinaryname = None self.binarydic = {"vectorAdd":["vectoradd_hip.exe"],\ "gpu-burn":["/build/gpuburn-hip"],\ @@ -140,10 +139,7 @@ def buildtest(self, logFile, testid, env = None): cmdexc = cmdcd + cmd_build # Execute the command on shell - if "HIP-Examples-Applications" in testid: - execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) - else: - self.runlog = execshellcmd(cmdexc, logFile, env) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) # Check if the test binary/ies is/are generated for binary in self.binarydic[testid]: if not os.path.isfile(self.thistestpath + binary): @@ -156,76 +152,109 @@ def runtest(self, logFile, testid, env = None): if testid == "vectorAdd": # Test already executed during make # Run Parser - ret = Hip_examples_parser().vectorAdd(self.runlog) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().vectorAdd(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "gpu-burn": cmdexc = "cd " + self.thistestpath + ";" + "." +\ self.binarydic[testid][0] + " -t 5" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().gpu_burn(self.runlog) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().gpu_burn(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "strided-access": cmdexc = "cd " + self.thistestpath + ";" + "./" +\ self.binarydic[testid][0] - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().strided_access(self.runlog) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().strided_access(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "rtm8": cmdexc = "cd " + self.thistestpath + ";" + "./" +\ self.binarydic[testid][0] - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().rtm8(self.runlog) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().rtm8(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "reduction": cmdexc = "cd " + self.thistestpath + ";" +\ "bash ./run.sh" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().reduction(self.runlog) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().reduction(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "mini-nbody": # Test already executed during make # Run Parser - ret = Hip_examples_parser().mini_nbody(self.runlog) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().mini_nbody(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "add4": cmdexc = "cd " + self.thistestpath + ";" +\ "./runhip.sh" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().add4(self.runlog) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().add4(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "cuda-stream": cmdexc = "cd " + self.thistestpath + ";" + "./" +\ self.binarydic[testid][0] - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().cuda_stream(self.runlog) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().cuda_stream(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "openmp-helloworld": # Test already executed during make # Run Parser - ret = Hip_examples_parser().openmp_helloworld(self.runlog) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().openmp_helloworld(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "rodinia_3.bfs" or testid == "rodinia_3.cfd" or \ testid == "rodinia_3.dwt2d" or testid == "rodinia_3.particlefilter": cmdexc = "cd " + self.thistestpath + ";" + "make test;" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().rodina3(self.runlog, "PASSED", 2) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().rodina3(logbytes, "PASSED", 2) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "rodinia_3.gaussian" or testid == "rodinia_3.lavaMD": cmdexc = "cd " + self.thistestpath + ";" + "make test;" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().rodina3(self.runlog, "PASSED", 5) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().rodina3(logbytes, "PASSED", 5) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "rodinia_3.heartwall" or testid == "rodinia_3.hotspot"\ or testid == "rodinia_3.hybridsort" or testid == "rodinia_3.lud"\ or testid == "rodinia_3.myocyte" or testid == "rodinia_3.nn"\ @@ -233,16 +262,22 @@ def runtest(self, logFile, testid, env = None): or testid == "rodinia_3.srad" or testid == "rodinia_3.streamcluster"\ or testid == "rodinia_3.b+tree" or testid == "rodinia_3.backprop": cmdexc = "cd " + self.thistestpath + ";" + "make test;" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().rodina3(self.runlog, "PASSED", 1) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().rodina3(logbytes, "PASSED", 1) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "rodinia_3.kmeans": cmdexc = "cd " + self.thistestpath + ";" + "make test;" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().rodina3(self.runlog, "PASSED", 4) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().rodina3(logbytes, "PASSED", 4) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "HIP-Examples-Applications.BinomialOption" or\ testid == "HIP-Examples-Applications.BitonicSort" or\ testid == "HIP-Examples-Applications.dct" or\ @@ -263,32 +298,43 @@ def runtest(self, logFile, testid, env = None): elif testid == "GPU-STREAM-DOUBLE": cmdexc = "cd " + self.thistestpath + ";" +\ "./hip-stream" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().gpu_stream(self.runlog) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().gpu_stream(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "GPU-STREAM-FLOAT": cmdexc = "cd " + self.thistestpath + ";" +\ "./hip-stream --float" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().gpu_stream(self.runlog) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().gpu_stream(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "mixbench-hip-alt": cmdexc = "cd " + self.thistestpath + ";" +\ "./mixbench-hip-alt" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().mix_bench(self.runlog) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().mix_bench(logbytes) if ret == "Failed": res &= False + self.runlogdump.close() elif testid == "mixbench-hip-ro": cmdexc = "cd " + self.thistestpath + ";" +\ "./mixbench-hip-ro" - self.runlog = execshellcmd(cmdexc, logFile, env) - ret = Hip_examples_parser().mix_bench(self.runlog) + execshellcmd_largedump(cmdexc, logFile, self.runlogdump, env) + self.runlogdump.seek(0) + logbytes = self.runlogdump.read() + ret = Hip_examples_parser().mix_bench(logbytes) if ret == "Failed": res &= False - + self.runlogdump.close() return res def clean(self, testid): diff --git a/src/hiptestsuite/applications/hip_samples/hip_samples.py b/src/hiptestsuite/applications/hip_samples/hip_samples.py index e24042c..d375f75 100644 --- a/src/hiptestsuite/applications/hip_samples/hip_samples.py +++ b/src/hiptestsuite/applications/hip_samples/hip_samples.py @@ -25,8 +25,8 @@ from hiptestsuite.applications.hip_samples.hip_samples_build_amd import BuildRunAmd from hiptestsuite.applications.hip_samples.hip_samples_build_nvidia import BuildRunNvidia from hiptestsuite.common.hip_get_packages import HipPackages -from hiptestsuite.common.hip_shell import execshellcmd - +from hiptestsuite.common.hip_shell import * +import tempfile import os import re # Common class to clone, set up, build and run test @@ -37,7 +37,7 @@ def __init__(self, path, binary, cwd): self.hippath = os.path.join(self.conformancePath, "HIP/") self.thistestpath = os.path.join(self.hippath, path) self.binary = binary - self.testExecOutput = "" + self.testExecOutput = None self.hiprepo = "" # Default self.hipbranch = "" self.hipcommitId = "" @@ -83,8 +83,14 @@ def clean(self): def runtest(self, logFile): cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary - self.testExecOutput += execshellcmd(cmdexc, logFile, None) - + if os.environ.get('AMD_LOG_LEVEL') is None: + self.testExecOutput = execshellcmd(cmdexc, logFile, None) + else: + runlogdump = tempfile.TemporaryFile("w+") + envtoset = os.environ.copy() + execshellcmd_largedump(cmdexc, logFile, runlogdump, envtoset) + runlogdump.seek(0) + self.testExecOutput = runlogdump.read() # Common class to parse the result of test execution class LogParser(): diff --git a/src/hiptestsuite/applications/hip_samples/hip_samples_build_amd.py b/src/hiptestsuite/applications/hip_samples/hip_samples_build_amd.py index badcd88..ba02894 100644 --- a/src/hiptestsuite/applications/hip_samples/hip_samples_build_amd.py +++ b/src/hiptestsuite/applications/hip_samples/hip_samples_build_amd.py @@ -19,7 +19,6 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd from hiptestsuite.applications.hip_samples.hip_samples_build_common import BuildRunCommon class BuildRunAmd(BuildRunCommon): diff --git a/src/hiptestsuite/applications/hip_samples/hip_samples_build_common.py b/src/hiptestsuite/applications/hip_samples/hip_samples_build_common.py index cc3f668..488c2d9 100644 --- a/src/hiptestsuite/applications/hip_samples/hip_samples_build_common.py +++ b/src/hiptestsuite/applications/hip_samples/hip_samples_build_common.py @@ -19,7 +19,7 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd +from hiptestsuite.common.hip_shell import * class BuildRunCommon(): ''' diff --git a/src/hiptestsuite/applications/hip_samples/hip_samples_build_nvidia.py b/src/hiptestsuite/applications/hip_samples/hip_samples_build_nvidia.py index 62a97af..7920c91 100644 --- a/src/hiptestsuite/applications/hip_samples/hip_samples_build_nvidia.py +++ b/src/hiptestsuite/applications/hip_samples/hip_samples_build_nvidia.py @@ -19,7 +19,8 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd +import tempfile +from hiptestsuite.common.hip_shell import * from hiptestsuite.applications.hip_samples.hip_samples_build_common import BuildRunCommon class BuildRunNvidia(BuildRunCommon): diff --git a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py index 34684e1..6cb7594 100644 --- a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py +++ b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_amd.py @@ -27,7 +27,7 @@ class BuildRunAmd(): def __init__(self, thistestpath, logFile): self.thistestpath = thistestpath self.logFile = logFile - self.runlog = "" + self.runlog = None def setenv(self, gpu_arch): env = "export HIP_PLATFORM=`/opt/rocm/bin/hipconfig --platform`;" @@ -111,10 +111,14 @@ def runtest(self, testnum): print("Testing Performance Test") cmdrun = "./benchmark --domain-size 256 256 --runs 100;" cmdexc = cmdcd + cmdrun - self.runlog = execshellcmd(cmdexc, self.logFile, None) + env = os.environ.copy() + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, self.runlog, env) def clean(self): print("Cleaning Gridtools..") + if self.runlog != None: + self.runlog.close() def parse_result(self, testnum): return GridtoolsParser(self.runlog).parse(testnum) diff --git a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_nvidia.py b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_nvidia.py index 84a0524..62a3173 100644 --- a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_nvidia.py +++ b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_build_nvidia.py @@ -27,7 +27,7 @@ class BuildRunNvidia(): def __init__(self, thistestpath, logFile, cuda_target): self.thistestpath = thistestpath self.logFile = logFile - self.runlog = "" + self.runlog = None self.cuda_target = cuda_target def setenv(self): @@ -101,10 +101,14 @@ def runtest(self, testnum): print("Testing Performance Test") cmdrun = "./benchmark --domain-size 256 256 --runs 100;" cmdexc = cmdcd + cmdrun - self.runlog = execshellcmd(cmdexc, self.logFile, None) + env = os.environ.copy() + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, self.runlog, env) def clean(self): print("Cleaning Gridtools..") + if self.runlog != None: + self.runlog.close() def parse_result(self, testnum): return GridtoolsParser(self.runlog).parse(testnum) diff --git a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py index 356fe25..957d10b 100644 --- a/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py +++ b/src/hiptestsuite/applications/hpc_apps/gridtools/gridtools_parser_common.py @@ -18,12 +18,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from hiptestsuite.common.hip_shell import execshellcmd import re class GridtoolsParser(): def __init__(self, results): - self.results = results + results.seek(0) + logbytes = results.read() + self.results = logbytes self.conv_tests = ["HORIZONTAL DIFFUSION", "VERTICAL DIFFUSION", "FULL DIFFUSION",\ "HORIZONTAL ADVECTION", "VERTICAL ADVECTION", "RUNGE-KUTTA ADVECTION", "ADVECTION-DIFFUSION"] def parse(self, testnum): diff --git a/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py b/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py index 95e1a35..2ef02bb 100644 --- a/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py +++ b/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_build_amd.py @@ -28,7 +28,7 @@ class BuildRunAmd(): def __init__(self, thistestpath, logFile): self.thistestpath = thistestpath self.logFile = logFile - self.runlog = "" + self.runlog = None def buildtest(self): # In this function put the build steps for test cases @@ -63,10 +63,14 @@ def runtest(self, testnum): elif testnum == 1: cmdrun = "./core/perf_test/KokkosCore_PerfTestExec;" cmdexc = cmdcd + cmdrun - self.runlog = execshellcmd(cmdexc, self.logFile, None) + env = os.environ.copy() + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, self.runlog, env) def clean(self): print("Cleaning Kokkos..") + if self.runlog != None: + self.runlog.close() def parse_result(self, testnum): return KokkosParser(self.runlog).parse(testnum) diff --git a/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py b/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py index 919e0bf..1afe1ea 100644 --- a/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py +++ b/src/hiptestsuite/applications/hpc_apps/kokkos/kokkos_parser_common.py @@ -18,12 +18,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from hiptestsuite.common.hip_shell import execshellcmd import re class KokkosParser(): def __init__(self, results): - self.results = results + results.seek(0) + logbytes = results.read() + self.results = logbytes def parse(self, testnum): testpassed = False diff --git a/src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_amd.py b/src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_amd.py index 4492784..adb0648 100644 --- a/src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_amd.py +++ b/src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_amd.py @@ -28,7 +28,7 @@ class BuildRunAmd(): def __init__(self, thistestpath, logFile): self.thistestpath = thistestpath self.logFile = logFile - self.runlog = "" + self.runlog = None def set_env(self): cmd = "export MPI_PATH=/usr/local/openmpi;" @@ -154,10 +154,14 @@ def runtest(self, testnum): cmd += "mpirun -np 1 laghos -pa -p 1 -tf 0.6 -no-vis -m data/cube01_hex.mesh --cg-tol 0 --cg-max-steps 50 --max-steps 2 -ok 2 -ot 1 -rs 5 -d hip;" elif testnum == 1: cmd += "mpirun -np 1 laghos -pa -p 1 -tf 0.6 -no-vis -m data/cube_12_hex.mesh --cg-tol 0 --cg-max-steps 50 --max-steps 2 -ok 3 -ot 2 -rs 4 -d hip;" - self.runlog = execshellcmd(cmd, self.logFile, None) + env = os.environ.copy() + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, self.logFile, self.runlog, env) def clean(self): print("Cleaning Laghos..") + if self.runlog != None: + self.runlog.close() def parse_result(self, testnum): return LaghosParser(self.runlog).parse(testnum) diff --git a/src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_nvidia.py b/src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_nvidia.py index 897d9fb..ddc7e15 100644 --- a/src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_nvidia.py +++ b/src/hiptestsuite/applications/hpc_apps/laghos/laghos_build_nvidia.py @@ -28,7 +28,7 @@ class BuildRunNvidia(): def __init__(self, thistestpath, logFile, cuda_target): self.thistestpath = thistestpath self.logFile = logFile - self.runlog = "" + self.runlog = None self.cuda_target = cuda_target def set_env(self): @@ -155,10 +155,14 @@ def runtest(self, testnum): cmd += "mpirun -np 1 laghos -pa -p 1 -tf 0.6 -no-vis -m data/cube01_hex.mesh --cg-tol 0 --cg-max-steps 50 --max-steps 2 -ok 2 -ot 1 -rs 5 -d hip;" elif testnum == 1: cmd += "mpirun -np 1 laghos -pa -p 1 -tf 0.6 -no-vis -m data/cube_12_hex.mesh --cg-tol 0 --cg-max-steps 50 --max-steps 2 -ok 3 -ot 2 -rs 4 -d hip;" - self.runlog = execshellcmd(cmd, self.logFile, None) + env = os.environ.copy() + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, self.logFile, self.runlog, env) def clean(self): print("Cleaning Laghos..") + if self.runlog != None: + self.runlog.close() def parse_result(self, testnum): return LaghosParser(self.runlog).parse(testnum) diff --git a/src/hiptestsuite/applications/hpc_apps/laghos/laghos_parser_common.py b/src/hiptestsuite/applications/hpc_apps/laghos/laghos_parser_common.py index 35c3d9a..a98cc09 100644 --- a/src/hiptestsuite/applications/hpc_apps/laghos/laghos_parser_common.py +++ b/src/hiptestsuite/applications/hpc_apps/laghos/laghos_parser_common.py @@ -18,12 +18,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from hiptestsuite.common.hip_shell import execshellcmd import re class LaghosParser(): def __init__(self, results): - self.results = results + results.seek(0) + logbytes = results.read() + self.results = logbytes def parse(self, testnum): count = 0 diff --git a/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py index 2825856..ed16d94 100644 --- a/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py +++ b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_amd.py @@ -19,14 +19,15 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd +import tempfile +from hiptestsuite.common.hip_shell import * from hiptestsuite.applications.keccaktreegpu.keccaktreegpu_parser_common import KeccakTreeParser class BuildRunAmd(): def __init__(self, thistestpath, logFile, binary): self.thistestpath = thistestpath self.logFile = logFile - self.runlog = "" + self.runlog = None self.binary = binary def getenvironmentvariables(self): @@ -51,13 +52,16 @@ def runtest(self): print("Running keccaktreegpu..") env = self.getenvironmentvariables() cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary + ";" - self.runlog = execshellcmd(cmdexc, self.logFile, env) + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, self.runlog, env) def clean(self): print("Cleaning keccaktreegpu..") cmdcd = "cd " + self.thistestpath + ";" cmdclean = "make clean;" cmdexc = cmdcd + cmdclean + if self.runlog != None: + self.runlog.close() execshellcmd(cmdexc, None, None) def parse_result(self): diff --git a/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py index 47e35c8..fc8e888 100644 --- a/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py +++ b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_build_nvidia.py @@ -19,14 +19,15 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd +import tempfile +from hiptestsuite.common.hip_shell import * from hiptestsuite.applications.keccaktreegpu.keccaktreegpu_parser_common import KeccakTreeParser class BuildRunNvidia(): def __init__(self, thistestpath, logFile, binary): self.thistestpath = thistestpath self.logFile = logFile - self.runlog = "" + self.runlog = None self.binary = binary def getenvironmentvariables(self): @@ -54,13 +55,16 @@ def runtest(self): print("Running keccaktreegpu..") env = self.getenvironmentvariables() cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary + ";" - self.runlog = execshellcmd(cmdexc, self.logFile, env) + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, self.runlog, env) def clean(self): print("Cleaning keccaktreegpu..") cmdcd = "cd " + self.thistestpath + ";" cmdclean = "make clean;" cmdexc = cmdcd + cmdclean + if self.runlog != None: + self.runlog.close() execshellcmd(cmdexc, None, None) def parse_result(self): diff --git a/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py index 2774344..8cd4a24 100644 --- a/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py +++ b/src/hiptestsuite/applications/keccaktreegpu/keccaktreegpu_parser_common.py @@ -23,7 +23,9 @@ class KeccakTreeParser(): def __init__(self, results): - self.results = results + results.seek(0) + logbytes = results.read() + self.results = logbytes def parse(self): passedts1 = False diff --git a/src/hiptestsuite/applications/mgbench/mgbench_build_amd.py b/src/hiptestsuite/applications/mgbench/mgbench_build_amd.py index bb8f9ab..47894cb 100644 --- a/src/hiptestsuite/applications/mgbench/mgbench_build_amd.py +++ b/src/hiptestsuite/applications/mgbench/mgbench_build_amd.py @@ -19,7 +19,8 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd +import tempfile +from hiptestsuite.common.hip_shell import * from hiptestsuite.applications.mgbench.mgbench_parser_common import MgbenchParser class BuildRunAmd(): @@ -29,7 +30,7 @@ def __init__(self, app_root, thistestpath, logFile, mgtestfile, binary): self.logFile = logFile self.mgtestfile = mgtestfile self.binary = binary - self.runlog = "" + self.runlog = None def buildtest(self): # In this function put the build steps for test cases @@ -61,13 +62,17 @@ def buildtest(self): def runtest(self): print("Running mgbench..") cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary + ";" - self.runlog = execshellcmd(cmdexc, self.logFile, None) + envtoset = os.environ.copy() + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, self.runlog, envtoset) def clean(self): print("Cleaning mgbench..") cmdcd = "cd " + self.thistestpath + ";" cmdrm = "rm -f " + self.binary + ";" cmdexc = cmdcd + cmdrm + if self.runlog != None: + self.runlog.close() execshellcmd(cmdexc, None, None) def parse_result(self, test): diff --git a/src/hiptestsuite/applications/mgbench/mgbench_build_nvidia.py b/src/hiptestsuite/applications/mgbench/mgbench_build_nvidia.py index 0230705..174f33f 100644 --- a/src/hiptestsuite/applications/mgbench/mgbench_build_nvidia.py +++ b/src/hiptestsuite/applications/mgbench/mgbench_build_nvidia.py @@ -19,7 +19,8 @@ # THE SOFTWARE. import os -from hiptestsuite.common.hip_shell import execshellcmd +import tempfile +from hiptestsuite.common.hip_shell import * from hiptestsuite.applications.mgbench.mgbench_parser_common import MgbenchParser class BuildRunNvidia(): @@ -29,7 +30,7 @@ def __init__(self, app_root, thistestpath, logFile, mgtestfile, binary): self.logFile = logFile self.mgtestfile = mgtestfile self.binary = binary - self.runlog = "" + self.runlog = None def getenvironmentvariables(self): envtoset = os.environ.copy() @@ -69,13 +70,16 @@ def runtest(self): print("Running mgbench..") env = self.getenvironmentvariables() cmdexc = "cd " + self.thistestpath + ";" + "./" + self.binary + ";" - self.runlog = execshellcmd(cmdexc, self.logFile, env) + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmdexc, self.logFile, self.runlog, env) def clean(self): print("Cleaning mgbench..") cmdcd = "cd " + self.thistestpath + ";" cmdrm = "rm -f " + self.binary + ";" cmdexc = cmdcd + cmdrm + if self.runlog != None: + self.runlog.close() execshellcmd(cmdexc, None, None) def parse_result(self, test): diff --git a/src/hiptestsuite/applications/mgbench/mgbench_parser_common.py b/src/hiptestsuite/applications/mgbench/mgbench_parser_common.py index 2c0c3e2..13c556a 100644 --- a/src/hiptestsuite/applications/mgbench/mgbench_parser_common.py +++ b/src/hiptestsuite/applications/mgbench/mgbench_parser_common.py @@ -20,11 +20,12 @@ import os import re -from hiptestsuite.common.hip_shell import execshellcmd class MgbenchParser(): def __init__(self, results): - self.results = results + results.seek(0) + logbytes = results.read() + self.results = logbytes def parse(self, test): numgpus = 0 From 50f60c8d329d5a56440bc1a86b41d02e2a779e88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry=20Linjam=C3=A4ki?= Date: Tue, 23 May 2023 15:20:15 +0300 Subject: [PATCH 17/18] execshellcmd_largedump(): return subprocess' return code --- src/hiptestsuite/common/hip_shell.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hiptestsuite/common/hip_shell.py b/src/hiptestsuite/common/hip_shell.py index 166c515..a106322 100644 --- a/src/hiptestsuite/common/hip_shell.py +++ b/src/hiptestsuite/common/hip_shell.py @@ -42,6 +42,7 @@ def execshellcmd_largedump(cmdexc, logfile, runlog, myenv): for line in runlog: logfile.write(line) runlog.seek(0) + return proc.returncode def get_gpuarch(logFile): # Get GPU Architecture From bc2ef84f547b91e7c9fdd2a9239c6b336c331c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry=20Linjam=C3=A4ki?= Date: Mon, 22 May 2023 10:16:42 +0300 Subject: [PATCH 18/18] Add CP2K regression tests to the test suite --- README.md | 6 + cfg.py | 5 + .../applications/hpc_apps/cp2k/__init__.py | 0 .../applications/hpc_apps/cp2k/cp2k.py | 181 ++++++++++++++++++ .../hpc_apps/cp2k/cp2k_build.bash | 9 + .../hpc_apps/cp2k/cp2k_build_amd.py | 124 ++++++++++++ .../hpc_apps/cp2k/cp2k_parser_common.py | 35 ++++ src/hiptestsuite/common/hip_get_packages.py | 7 + 8 files changed, 367 insertions(+) create mode 100644 src/hiptestsuite/applications/hpc_apps/cp2k/__init__.py create mode 100644 src/hiptestsuite/applications/hpc_apps/cp2k/cp2k.py create mode 100644 src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_build.bash create mode 100644 src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_build_amd.py create mode 100644 src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_parser_common.py diff --git a/README.md b/README.md index 65c02f7..35ff81d 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,12 @@ $ cd hip-testsuite $ pip3 install -r requirements.txt ``` +• Required packages for some tests on Ubuntu +``` +$ sudo apt install autoconf autogen automake autotools-dev bzip2 g++ gcc \ +gfortran libtool libtool-bin make patch pkg-config zlib1g-dev +``` + • Verify "cfg.py" contents to make sure that desired values are present To provide input configurations like log location, platform, repos etc. "cfg.py" is used. Please check 6.3.1 for further details. diff --git a/cfg.py b/cfg.py index 8f2315d..7b37b51 100644 --- a/cfg.py +++ b/cfg.py @@ -134,5 +134,10 @@ "repo_url": "https://github.com/CEED/Laghos.git", "branch": None, "commit_id": "a7f6123d42847f6bdbdb614f5af876541f49cd16" + }, + "cp2k": { + "repo_url": "git@github.com:cp2k/cp2k.git", + "branch": "master", + "commit_id": "a11bcb3b8565e52344423df66485879857c7b754" } } diff --git a/src/hiptestsuite/applications/hpc_apps/cp2k/__init__.py b/src/hiptestsuite/applications/hpc_apps/cp2k/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k.py b/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k.py new file mode 100644 index 0000000..dfa0ae6 --- /dev/null +++ b/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k.py @@ -0,0 +1,181 @@ +# Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2023 Intel Finland Oy. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from hiptestsuite.TesterRepository import Tester, Test, TestData +from hiptestsuite.Test import HIPTestData, TestResult, HIP_PLATFORM +from typing import Union, List +from hiptestsuite.test_classifier import TestClassifier +from hiptestsuite.applications.hpc_apps.cp2k.cp2k_build_amd import BuildRunAmd +from hiptestsuite.common.hip_get_packages import HipPackages +from hiptestsuite.common.hip_shell import execshellcmd_largedump + +import os +import re +import tempfile + +# Common class to clone, set up, build and run test +class PrepareTest(): + def __init__(self, cwd): + self.cwdAbs = cwd + self.app_path = os.path.join(self.cwdAbs, + "src/hiptestsuite/applications/hpc_apps/", + "cp2k/") + self.thistestpath = self.app_path + self.prepareobj = None + self.apprepo = "" # Default + self.appbranch = "" + self.appcommitId = "" + self.rocm_path = os.environ.get('ROCM_PATH') + if not self.rocm_path: + self.rocm_path = "/opt/rocm" + self.gpu_version = None + + def pick_gpu(self): + """Picks a GPU supported by the CP2K""" + if self.gpu_version: + return self.gpu_version + + selected_gpu = None + cmd = self.rocm_path + "/bin/rocm_agent_enumerator" + with tempfile.TemporaryFile("w+") as output: + ret_code = execshellcmd_largedump(cmd, None, output, None) + if ret_code != 0: + return None + output.seek(0) + for line in output: + # See cp2k/tools/toolchain/scripts/generate_arch_files.sh + # for supported GPUs. + if line.startswith("gfx90a"): + selected_gpu = "Mi250" + elif line.startswith("gfx908"): + selected_gpu = "Mi100" + elif line.startswith("gfx906"): + selected_gpu = "Mi50" + + self.gpu_version = selected_gpu + return selected_gpu + + def set_cp2k_repoinfo(self, test_data: HIPTestData): + validrepconfig = True + if test_data.repos["cp2k"].repo_url != None: + self.apprepo = test_data.repos["cp2k"].repo_url + else: + validrepconfig &= False + if test_data.repos["cp2k"].branch != None: + self.appbranch = test_data.repos["cp2k"].branch + if test_data.repos["cp2k"].commit_id != None: + self.appcommitId = test_data.repos["cp2k"].commit_id + return validrepconfig + + def downloadtest(self, logFile, test_data: HIPTestData): + ret = HipPackages().pull_repo(logFile, self.apprepo, + self.appbranch, self.appcommitId, "cp2k") + return ret + + def buildtest(self, logFile, platform): + if platform == HIP_PLATFORM.amd: + self.prepareobj = BuildRunAmd(self.thistestpath, logFile, + self.rocm_path, self.pick_gpu()) + else: + print("Unsupported Platform") + return False + return self.prepareobj.buildtest() == True + + def clean(self): + if self.prepareobj != None: + self.prepareobj.clean() + + def runtest(self, testnum): + if self.prepareobj != None: + self.prepareobj.runtest(testnum) + + def parse_result(self, testnum): + if self.prepareobj != None: + return self.prepareobj.parse_result(testnum) + return False + +class CP2K(TestClassifier): + def __init__(self): + TestClassifier.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + TestClassifier.add_matched_with_names(self, {"cp2k": matched_with_names}) + +class AllRegressionTests(CP2K): + def __init__(self): + CP2K.__init__(self) + + def add_matched_with_names(self, matched_with_names: Union[None, dict] = None): + CP2K.add_matched_with_names(self, {"all-tests": matched_with_names}) + +# Cp2k Unit/Regression Test +class CP2K_UNIT_TEST(Tester, PrepareTest): + def __init__(self): + Tester.__init__(self) + self.cwd = os.getcwd() + PrepareTest.__init__(self, self.cwd) + + def getTests(self) -> List[Test]: + test = Test() + test.test_name = self.__class__.__name__ + classifier = AllRegressionTests() + classifier.add_matched_with_names() + test.classifiers = [classifier] + test.tester = self + return [test] + + def clean(self): + PrepareTest.clean(self) + + def test(self, test_data: HIPTestData): + print("=============== CP2K Unit test ===============") + if test_data.HIP_PLATFORM != HIP_PLATFORM.amd: + print("CP2K test is not supported on NVIDIA") + test_data.test_result = TestResult.SKIP + return + if not self.pick_gpu(): + print("Could not find supported GPU version.") + test_data.test_result = TestResult.SKIP + return + + # Set repo info + isrepocfgvalid = self.set_cp2k_repoinfo(test_data) + if not isrepocfgvalid: + test_data.test_result = TestResult.ERROR + return + # Create the log directory + resultLogDir = test_data.log_location + with open(resultLogDir + "/cp2k_unit.log", 'w+') as testLogger: + res = self.downloadtest(testLogger, test_data) + if not res: + test_data.test_result = TestResult.FAIL + return + res = self.buildtest(testLogger, test_data.HIP_PLATFORM) + if not res: + test_data.test_result = TestResult.FAIL + return + self.runtest(0) + # Parse the test result + if True == self.parse_result(0): + test_data.test_result = TestResult.PASS + else: + test_data.test_result = TestResult.FAIL + diff --git a/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_build.bash b/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_build.bash new file mode 100644 index 0000000..7d1f03b --- /dev/null +++ b/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_build.bash @@ -0,0 +1,9 @@ +#!/bin/bash +set -eu +cd ${CP2K_ROOT} +rm -f build.stamp +set +eu +source ./tools/toolchain/install/setup +set -eu +make -j${JOB_COUNT:-1} ARCH=local_hip VERSION=ssmp +touch build.stamp diff --git a/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_build_amd.py b/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_build_amd.py new file mode 100644 index 0000000..85cabd8 --- /dev/null +++ b/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_build_amd.py @@ -0,0 +1,124 @@ +# Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2023 Intel Finland Oy. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +import re +import tempfile +from multiprocessing import cpu_count + +from hiptestsuite.common.hip_shell import * +from hiptestsuite.applications.hpc_apps.cp2k.cp2k_parser_common import Cp2kParser + +class BuildRunAmd(): + def __init__(self, thistestpath, logFile, rocm_path, gpu_version): + self.thistestpath = thistestpath + self.logFile = logFile + self.runlog = None + self.rocm_path = rocm_path + self.gpu_version = gpu_version + + def configure_build_cp2k(self): + + if not os.path.exists(self.rocm_path): + print("Error: ROCm path does not exist. Exiting Test!") + print("NOTE: tried path '{}'".format(self.rocm_path)) + return False + + repo_root = os.path.join(self.thistestpath, "cp2k") + + submodule_cmd = """ +set -e +cd {repo_root} +git submodule update --init --recursive +""".format(repo_root=repo_root) + print("cp2k submodule fetching in progress ..") + with tempfile.TemporaryFile("w+") as runlogdump: + retcode = execshellcmd_largedump(submodule_cmd, self.logFile, + runlogdump, None) + if retcode != 0: + print("cp2k submodule fetch Failed", ) + return False + + configure_cmd = """ +set -e +export ROCM_PATH={rocm_path} +export LIBRARY_PATH=$ROCM_PATH/lib:$LIBRARY_PATH +cd {repo_root} +rm -f configure.stamp +cd tools/toolchain +./install_cp2k_toolchain.sh --enable-hip --with-cmake=system \ +--with-libint=system --with-libxc=system --with-fftw=system \ +--with-openblas=system --with-sirius=no --with-gsl=no \ +--gpu-ver={gpu_version} +cd {repo_root} +cp tools/toolchain/install/arch/* arch/ +touch configure.stamp +""".format(repo_root=repo_root, rocm_path=self.rocm_path, + gpu_version=self.gpu_version) + print("cp2k dependency fetch and configuration in progress ..") + with tempfile.TemporaryFile("w+") as runlogdump: + execshellcmd_largedump(configure_cmd, self.logFile, runlogdump, + None) + if not os.path.exists(repo_root + "/configure.stamp"): + print("cp2k dependency fetch and/or configuration Failed", ) + return False + + # The build step relies on bash shell. + build_script = os.path.join(self.thistestpath, "cp2k_build.bash") + build_cmd = "/bin/bash " + build_script + build_env = os.environ.copy() + build_env['CP2K_ROOT'] = str(repo_root) + build_env['JOB_COUNT'] = str(cpu_count()) + print("cp2k build in progress ..") + with tempfile.TemporaryFile("w+") as runlogdump: + execshellcmd_largedump(build_cmd, self.logFile, runlogdump, + build_env) + if not os.path.exists(repo_root + "/build.stamp"): + print("cp2k build Failed", ) + return False + return True + + def buildtest(self): + if not self.configure_build_cp2k(): + return False + return True + + def runtest(self, testnum): + print("Running cp2k Test" + str(testnum)) + repo_root = os.path.join(self.thistestpath, "cp2k") + + cmd = """ +set -e +export LD_LIBRARY_PATH={rocm_path}/lib:$LD_LIBRARY_PATH +cd {repo_root} +make -j{job_count} TESTOPTS="--skipdir QS/regtest-rs-dhft --skipdir QS/regtest-hfx-ri" ARCH=local_hip VERSION=ssmp test +""".format(repo_root=repo_root, job_count=cpu_count(), rocm_path=self.rocm_path) + + self.runlog = tempfile.TemporaryFile("w+") + execshellcmd_largedump(cmd, self.logFile, self.runlog, None) + + def clean(self): + print("Cleaning cp2k..") + if self.runlog != None: + self.runlog.close() + + def parse_result(self, testnum): + return Cp2kParser(self.runlog).parse(testnum) diff --git a/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_parser_common.py b/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_parser_common.py new file mode 100644 index 0000000..e7838a0 --- /dev/null +++ b/src/hiptestsuite/applications/hpc_apps/cp2k/cp2k_parser_common.py @@ -0,0 +1,35 @@ +# Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2023 Intel Finland Oy. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import re + +class Cp2kParser(): + def __init__(self, results): + results.seek(0) + logbytes = results.read() + self.results = logbytes + + def parse(self, testnum): + no_failed_tests = re.search("Number of FAILED \s*tests 0", self.results, + re.MULTILINE) is not None + no_wrong_tests = re.search("Number of WRONG \s*tests 0", self.results, + re.MULTILINE) is not None + return no_failed_tests and no_wrong_tests diff --git a/src/hiptestsuite/common/hip_get_packages.py b/src/hiptestsuite/common/hip_get_packages.py index 57ea7d1..7f3ac0e 100644 --- a/src/hiptestsuite/common/hip_get_packages.py +++ b/src/hiptestsuite/common/hip_get_packages.py @@ -63,6 +63,9 @@ def __init__(self): self.mfemapppath = os.path.join(self.mfemrootpath, "mfem/") self.laghosrootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/laghos/") self.laghosapppath = os.path.join(self.laghosrootpath, "Laghos/") + # CP2K + self.cp2krootpath = os.path.join(self.cwdAbs, "src/hiptestsuite/applications/hpc_apps/cp2k/") + self.cp2kapppath = os.path.join(self.cp2krootpath, "cp2k/") def pull_repo(self, logFile, repo, branch, commitId, reponame): repo_root_path = "" @@ -132,6 +135,10 @@ def pull_repo(self, logFile, repo, branch, commitId, reponame): repo_root_path = self.laghosapppath repo_location = self.laghosrootpath repo_dir = "Laghos" + elif reponame == "cp2k": + repo_root_path = self.cp2kapppath + repo_location = self.cp2krootpath + repo_dir = reponame if os.path.isdir(repo_root_path) and os.path.isdir(repo_root_path + "/.git"): print(reponame + " already exist")